可伸缩的TextView
GIF录制效果不是太好:
原理:
1,获取textview有多少行,根据行数显示箭头图标。
2,获取只有一行时(自己设定的行数),textview的高度,并且记录。
3,点击textview或者图标执行动画,动态改变textview的高度。
ExpandableTextViewHorizontal
package com.ms.square.android.expandabletextview;public class ExpandableTextViewHorizontal extends LinearLayout implements View.OnClickListener {private static final String TAG = ExpandableTextViewHorizontal.class.getSimpleName();/* The default number of lines */private static final int MAX_COLLAPSED_LINES = 8;/* The default animation duration */private static final int DEFAULT_ANIM_DURATION = 300;/* The default alpha value when the animation starts */private static final float DEFAULT_ANIM_ALPHA_START = 0.7f;protected TextView mTv;protected ImageButton mButton; // Button to expand/collapseprivate boolean mRelayout = true;/*** true 折叠,箭头是向下的,false 展开,箭头是向上的<br/>* 同时也影响xml布局界面<br/>* 默认必须为true*/private boolean mCollapsed = true; // Show short version as default.private int mCollapsedHeight;/*** textview整体高度*/private int mTextHeightWithMaxLines;private int mMaxCollapsedLines;private int mMarginBetweenTxtAndBottom;/*** 箭头向下 展开*/private Drawable mExpandDrawable;/*** 箭头向上 折叠*/private Drawable mCollapseDrawable;private int mAnimationDuration;private float mAnimAlphaStart;private boolean mAnimating;/* Listener for callback */private OnExpandStateChangeListener mListener;/* For saving collapsed status when used in ListView */private SparseBooleanArray mCollapsedStatus;private int mPosition;public ExpandableTextViewHorizontal(Context context) {this(context, null);}public ExpandableTextViewHorizontal(Context context, AttributeSet attrs) {super(context, attrs);init(attrs);}@TargetApi(Build.VERSION_CODES.HONEYCOMB)public ExpandableTextViewHorizontal(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(attrs);}private void init(AttributeSet attrs) {TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);mMaxCollapsedLines = typedArray.getInt(R.styleable.ExpandableTextView_maxCollapsedLines, MAX_COLLAPSED_LINES);mAnimationDuration = typedArray.getInt(R.styleable.ExpandableTextView_animDuration, DEFAULT_ANIM_DURATION);mAnimAlphaStart = typedArray.getFloat(R.styleable.ExpandableTextView_animAlphaStart, DEFAULT_ANIM_ALPHA_START);mExpandDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_expandDrawable);mCollapseDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_collapseDrawable);if (mExpandDrawable == null) {mExpandDrawable = getDrawable(getContext(), R.drawable.ic_expand_more_black_12dp);}if (mCollapseDrawable == null) {mCollapseDrawable = getDrawable(getContext(), R.drawable.ic_expand_less_black_12dp);}typedArray.recycle();setOrientation(getOrientation());}private void findViews() {mTv = (TextView) getChildAt(0);mTv.setOnClickListener(this);mButton = (ImageButton) getChildAt(1);mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);mButton.setOnClickListener(this);getRootView().setOnClickListener(this);}@Overridepublic void setOrientation(int orientation){/* if(LinearLayout.HORIZONTAL == orientation){throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation.");}*/super.setOrientation(orientation);}@Overridepublic void onClick(View view) {startAnimationClick();}private void startAnimationClick() {if (mButton.getVisibility() != View.VISIBLE) {return;}mCollapsed = !mCollapsed;mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);if (mCollapsedStatus != null) {mCollapsedStatus.put(mPosition, mCollapsed);}// mark that the animation is in progressmAnimating = true;Animation animation;if (mCollapsed) {animation = new ExpandCollapseAnimation(this, getHeight(), mCollapsedHeight);} else {animation = new ExpandCollapseAnimation(this, getHeight(), getHeight() + mTextHeightWithMaxLines - mTv.getHeight());}Log.e("onClick","mCollapsedHeight = "+ mCollapsedHeight + " hhhhh = "+(getHeight() + mTextHeightWithMaxLines - mTv.getHeight()));animation.setFillAfter(true);animation.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {applyAlphaAnimation(mTv, mAnimAlphaStart);}@Overridepublic void onAnimationEnd(Animation animation) {// clear animation here to avoid repeated applyTransformation() callsclearAnimation();// clear the animation flagmAnimating = false;// notify the listenerif (mListener != null) {mListener.onExpandStateChanged(mTv, !mCollapsed);}}@Overridepublic void onAnimationRepeat(Animation animation) { }});clearAnimation();startAnimation(animation);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return mAnimating;}@Overrideprotected void onFinishInflate() {super.onFinishInflate();findViews();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// If no change, measure and return
// if (!mRelayout || getVisibility() == View.GONE) {if (!mRelayout) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);return;}mRelayout = false;// Setup with optimistic case// i.e. Everything fits. No button neededmButton.setVisibility(View.GONE);mTv.setMaxLines(Integer.MAX_VALUE);// Measuresuper.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.e("onMeasure","mCollapsedHeight = "+ mCollapsedHeight + " mCollapsed = " + mCollapsed + " line count = "+ mTv.getLineCount() + " mCollapsed = "+ mCollapsed);// If the text fits in collapsed mode, we are done.if (mTv.getLineCount() <= mMaxCollapsedLines) {return;}super.onMeasure(widthMeasureSpec, heightMeasureSpec);// Saves the text height w/ max linesmTextHeightWithMaxLines = getRealTextViewHeight(mTv);// Doesn't fit in collapsed mode. Collapse text view as needed. Show// button.if (mCollapsed) {mTv.setMaxLines(mMaxCollapsedLines);}mButton.setVisibility(View.VISIBLE);// Re-measure with new setupsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);if (mCollapsed) {// Gets the margin between the TextView's bottom and the ViewGroup's bottommTv.post(new Runnable() {@Overridepublic void run() {mMarginBetweenTxtAndBottom = getHeight() - mTv.getHeight();Log.e("onMeasure","mTv height = "+ mTv.getHeight() + " mMarginBetweenTxtAndBottom = " + mMarginBetweenTxtAndBottom );}});// Saves the collapsed height of this ViewGroupmCollapsedHeight = getMeasuredHeight();Log.e("onMeasure","mCollapsedHeight = "+ mCollapsedHeight + " mTextHeightWithMaxLines = " + mTextHeightWithMaxLines );}}public void setOnExpandStateChangeListener(@Nullable OnExpandStateChangeListener listener) {mListener = listener;}public void setText(@Nullable CharSequence text) {mRelayout = true;mTv.setText(text);setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE);}public void setText(@Nullable CharSequence text, @NonNull SparseBooleanArray collapsedStatus, int position) {mCollapsedStatus = collapsedStatus;mPosition = position;boolean isCollapsed = collapsedStatus.get(position, true);clearAnimation();mCollapsed = isCollapsed;mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);setText(text);getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;requestLayout();}/*** 其他控件点击的时候,执行缩放或者展开动画*/public void startAnimation() {startAnimationClick();}@Nullablepublic CharSequence getText() {if (mTv == null) {return "";}return mTv.getText();}private static boolean isPostHoneycomb() {return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;}private static boolean isPostLolipop() {return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;}@TargetApi(Build.VERSION_CODES.HONEYCOMB)private static void applyAlphaAnimation(View view, float alpha) {if (isPostHoneycomb()) {view.setAlpha(alpha);} else {AlphaAnimation alphaAnimation = new AlphaAnimation(alpha, alpha);// make it instantalphaAnimation.setDuration(0);alphaAnimation.setFillAfter(true);view.startAnimation(alphaAnimation);}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)private static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {Resources resources = context.getResources();if (isPostLolipop()) {return resources.getDrawable(resId, context.getTheme());} else {return resources.getDrawable(resId);}}private static int getRealTextViewHeight(@NonNull TextView textView) {int textHeight = textView.getLayout().getLineTop(textView.getLineCount());Log.e("getRealTextViewHeight","textHeight = "+ textHeight + " getHeight = " + textView.getMeasuredHeight());int padding = textView.getCompoundPaddingTop() + textView.getCompoundPaddingBottom();return textHeight + padding;}public boolean isCollapsed() {return mCollapsed;}class ExpandCollapseAnimation extends Animation {private final View mTargetView;private final int mStartHeight;private final int mEndHeight;public ExpandCollapseAnimation(View view, int startHeight, int endHeight) {mTargetView = view;mStartHeight = startHeight;mEndHeight = endHeight;setDuration(mAnimationDuration);}@Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) {final int newHeight = (int)((mEndHeight - mStartHeight) * interpolatedTime + mStartHeight);Log.e("applyTransformation","newHeight = "+ newHeight + " mMarginBetweenTxtAndBottom = " + mMarginBetweenTxtAndBottom + " mStartHeight = " + mStartHeight);mTv.setMaxHeight(newHeight - mMarginBetweenTxtAndBottom);if (Float.compare(mAnimAlphaStart, 1.0f) != 0) {applyAlphaAnimation(mTv, mAnimAlphaStart + interpolatedTime * (1.0f - mAnimAlphaStart));}mTargetView.getLayoutParams().height = newHeight;mTargetView.requestLayout();}@Overridepublic void initialize( int width, int height, int parentWidth, int parentHeight ) {super.initialize(width, height, parentWidth, parentHeight);}@Overridepublic boolean willChangeBounds( ) {return true;}}public interface OnExpandStateChangeListener {/*** Called when the expand/collapse animation has been finished** @param textView - TextView being expanded/collapsed* @param isExpanded - true if the TextView has been expanded*/void onExpandStateChanged(TextView textView, boolean isExpanded);}
}
自定义属性attrs
<declare-styleable name="ExpandableTextView"><attr name="maxCollapsedLines" format="integer"/><attr name="animDuration" format="integer"/><attr name="animAlphaStart" format="float"/><attr name="expandDrawable" format="reference"/><attr name="collapseDrawable" format="reference"/></declare-styleable>
代码修改地方:
1,去掉原固定设置linearlayout的方向为竖直方向
这样水平方向和竖直方向都支持
@Overridepublic void setOrientation(int orientation){if(LinearLayout.HORIZONTAL == orientation){throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation.");}super.setOrientation(orientation);}
2,通过getChildAt来获取对应的控件,
不再使用R.id.expandable_text 和 R.id.expand_collapse
添加getRootView().setOnClickListener(this);
private void findViews() {mTv = (TextView) getChildAt(0);mTv.setOnClickListener(this);mButton = (ImageButton) getChildAt(1);mButton.setImageDrawable(mCollapsed ? mExpandDrawable : mCollapseDrawable);mButton.setOnClickListener(this);// 添加根布局点击事件getRootView().setOnClickListener(this);}
DEMO下载:http://download.csdn.net/detail/huyuchaoheaven/9607448
参考和感谢:
https://github.com/Manabu-GT/ExpandableTextView
可伸缩的TextView相关推荐
- android ExpandableTextView可伸缩的TextView
类似微信朋友圈里面,当文章太长时会隐藏一部分,当点击展开时就全部显示,这样一种效果. 关于这个自定义ViewGroup: 1.onFinishInflate方法执行的时机 2.自定义的animatio ...
- 常用的第三方框架汇总
Android热门三方库源码面试宝典 工具类合集: https://github.com/Blankj/AndroidUtilCode (32k) https://blankj.com/2016/07 ...
- ArrayAdapter requires the resource ID to be a TextView
这个是使用ListView 的ArrayAdapter 加载自定义布局的时候跟布局没有使用TextView 引起的,处理方法就是跟布局换成TextView 如下 <?xml version=&q ...
- Spinner 使用的使用 报错:ArrayAdapter requires the resource ID to be a TextView
Spinner 使用的使用 报错:ArrayAdapter requires the resource ID to be a TextView 的问题 是修改layout的最外成不是使用TextVie ...
- Android Textview 实现版权符号© 的 实现
版权 @ 这个实现其实很简单就是在TextView 里面 添加 \u00a9 即可 具体效果如下 <TextViewandroid:id="@+id/btn"android ...
- Android TextView 去除顶部和底部留白(上下的间距有空白问题处理)
有时候我们完全按照ui 布局写的代码 看到的效果却和ui的效果有一定的差距 有没有很苦恼的效果,最近帮同事改bug 发现了这个问题,很多地方都是他私自调节的 这个问题不可有啊,还是要按照ui的尺寸来, ...
- Android 替换TextView 里面指定的符号
有时候接口返回的标签类型如下 热血,爱情,经典 中间是,但是ui交互图是 热血/爱情/经典 这个时候我们替换一下就行了,没有必要要求别人去该 使用方法为replace 也是简单的一行代码就可以实现效果 ...
- Android TextView设置透明度方法的注意点
TextView 设置字体透明度 一般自己都是在颜色值前面添加多少透明度即可, 不如设置字体颜色的透明度为50% android:textColor="#50D81B60" 这里 ...
- Android TextView 在strings 里面 实现换行
在TextView 把text 使用快捷键放到strings 里面 \n是无法被放到里面的 如果想实现换行 可以手动在里面添加 \n 例如写了一个小例子, 如果你写了多语言适配 ,那么在你的其他的st ...
最新文章
- centos使用镜像源轻松配置golang+vscode的方法
- 深度学习3:手动实现L2正则化(L2 Regularization)
- TP5 实现微信支付和支付宝支付
- 日历记事本java代码_急需日历记事本JAVA源代码
- ngrx注入到应用类构造函数里的store变量
- onesignal php,PHP FPM源代码反刍品味之五:信号signal处理
- Myeclipse学习总结(11)——Eclipse中设置Java/Css/Html/Jsp换行长度
- android权限列表
- [SQL实战]之查找当前薪水排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,不准使用order by
- git commit后,如何撤销commit
- pyodbc 连接informix
- 什么是分布式定时任务框架?
- [Qt笔记]设置VS2015下的应用工程图标
- 数据库内外连接、自连接
- wannacry作者捉到了吗_WannaCry爆发的根源原来是它?
- 工作室培训第一周总结
- 一日精通python编程_爱上Python 一日精通Python编程 [Learn Python in One Day and Learn it Well ]...
- oracle期初余额录入,用金蝶kis录入数量初始数据的方法
- 【智能优化算法】基于蜉蝣算法求解多目标优化问题附matlab代码
- python使用手册<5>函数
热门文章
- 在Windows上安装TkInter:完整教程
- php mysql 模糊查询_PHP如何实现模糊查询(图文代码)
- php数组元素倒序输出,PHP数组foreach倒序输出
- java 读取远程文件并让浏览器下载
- c语言中像y的字符是什么意思,C语言中字符型(char)的简单使用
- 10.7 抽象类最佳实践-模板设计模式
- 谷粒商城BUG P52新建二级目录中新建三级目录无法显示bug解决
- 算法该不该刷?如何高效刷算法?
- C/C++业内行情分析学习计划
- Camera1.0和Camear2.0的区别