Android自定义View系列之可伸缩的TextView

在写博客之前,告诉大家一个消息,我开通了自己的微信公众账号,如果你喜欢我的文章,希望关注我的微信公众号,我会定期与大家分享最新的博客文章,以及移动互联网最新动态。
我的微信公众号:yuanzeyao_android
二维码:

经常访问我博客的同学应该注意到了我的博客大部分是关于分析Android系统原理的,很少涉及应用层的知识,但是想要开发一个用户欢迎的App,没有一个炫酷的UI肯定是不行的,就好比一个人想成为武林高手仅仅修炼内功是不行的,必须内功和外功同时修炼才能成为真正的武林高手。很多人在开发Android的App过程中有这样的感觉,我不用学习Android系统原理照样可以进行Android开发,而且可以做出非常炫酷的效果,确实是这样的 ,因为对于一款App,它的大部分需求无非就是请求数据然后展示数据,至于一些炫酷的UI控件网上有很多开源的,所以我们不用懂Android系统的原理同样可以进行App开发,相信你自己也发现了,长期做这样的工作,你的技术是永远得不到提升的,当你使用别人开源的炫酷的控件时,你并不知道别人是怎么实现的,即使你有兴趣看别人的实现方法,如果你不懂Android系统原理,估计你也看不懂别人为什么要这么做。所以从今天起的半年时间,我会将学习重点放在应用层的开发,主要是自定义View和炫酷的动画的学习上。

开始今天的主题:可伸缩的TextView,先来看看效果图

Android自定义View系列之可伸缩的TextView_第1张图片

该自定义View具备如下特性:

  1. 当显示的内容没有2行(可以通过属性配置)时,和普通的TextView一样
  2. 当显示的内容大于2行时,仅仅显示两行,并且没有显示完的内容使用省略号代替,并显示向下箭头表示还有内容没有显示全
  3. 当用户点击TextView时,内容全部展开,当用户再次点击时,TextView又变为压缩模式

下面我们开始代码实现,正如上面所说,此View支持自定义属性,所以我们先要定义attrs.xml文件,如何编写attrs.xml文件网上有很多教程,我这里就不在说了。


<resources>
    <declare-styleable name="ExpandTextView">
        <attr name="textcolor" format="color">attr>
        <attr name="textsize" format="dimension">attr>
        <attr name="icon" format="reference">attr>
        <attr name="lines" format="integer">attr>
        <attr name="text" format="string">attr>
    declare-styleable>
resources>

这几个属性的意义如下:

  • textcolor : 字体颜色
  • textsize :字体大小
  • icon :标识内容没有显示完成的图片,默认是向下箭头
  • lines:压缩状态下显示的行数
  • text :显示的内容

    如果需要,你可以自己定义更多的属性。下面进入代码部分

/**
 * com.gavin.expandable.textview.ExpandTextView
 * @author yuanzeyao 
* create at 2015年10月10日 下午5:41:12 */
public class ExpandTextView extends LinearLayout implements OnClickListener{ public static final String TAG="ExpandTextView"; public static final int DEFAULT_TEXT_COLOR=0XFF000000; public static final int DEFAULT_LINE_NUM=3; public static final int DEFAULT_TEXT_SIZE=12; public static final int DEFAULT_MARGIN_TOP=10; private TextView mTextView; private ImageView mImageView; /**TextView字体的颜色*/ private int textColor; /**TextView字体的大小*/ private float textSize; /**TextView默认显示行数*/ private int maxLine; /**TextView的文本内容*/ private String text; /**ImageView使用的图片*/ private Drawable mIcon; /**TextView所有的内容暂用的行数*/ private int contentLine=0; /**是否展开*/ private boolean isExpand=false; public ExpandTextView(Context context) { super(context); init(null,0); } public ExpandTextView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs,0); } public ExpandTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs,defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public ExpandTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(attrs,defStyleAttr); } private void init(AttributeSet attrs,int defStyleAttr){ setOrientation(VERTICAL); setGravity(Gravity.CENTER_HORIZONTAL); TypedArray array=this.getContext().obtainStyledAttributes(attrs, R.styleable.ExpandTextView,defStyleAttr,0); textColor=array.getColor(R.styleable.ExpandTextView_textcolor,DEFAULT_TEXT_COLOR); textSize=array.getDimensionPixelOffset(R.styleable.ExpandTextView_textsize,dp2px(DEFAULT_TEXT_SIZE)); maxLine=array.getInt(R.styleable.ExpandTextView_lines,DEFAULT_LINE_NUM); mIcon=array.getDrawable(R.styleable.ExpandTextView_icon); text=array.getString(R.styleable.ExpandTextView_text); if(mIcon==null){ mIcon=this.getContext().getResources().getDrawable(R.drawable.text_expand); } array.recycle(); initViewAttribute(); } private void initViewAttribute(){ mTextView=new TextView(this.getContext()); //设置属性 mTextView.setText(text); mTextView.setTextColor(textColor); mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); int textHeight=mTextView.getLineHeight()*maxLine; LayoutParams mParams_txt=new LayoutParams(LayoutParams.MATCH_PARENT,textHeight); addView(mTextView,mParams_txt); mImageView=new ImageView(this.getContext()); mImageView.setImageDrawable(mIcon); LayoutParams mParams_img=new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT); mParams_img.topMargin=dp2px(DEFAULT_MARGIN_TOP); addView(mImageView,mParams_img); mImageView.setOnClickListener(this); this.setOnClickListener(this); this.post(new Runnable() { @Override public void run() { contentLine=mTextView.getLineCount(); if(contentLine<=maxLine){ mImageView.setVisibility(View.GONE); LayoutParams mParam=(LayoutParams) mTextView.getLayoutParams(); mParam.height=LayoutParams.WRAP_CONTENT; mTextView.setLayoutParams(mParam); ExpandTextView.this.setOnClickListener(null); }else{ //默认是非展开模式,那么设置最大行为maxLine mTextView.setMaxLines(maxLine); mTextView.setEllipsize(TruncateAt.END); mImageView.setVisibility(View.VISIBLE); } } }); } /** * dp单位和px单位的转化 * @param dp * @return */ private int dp2px(int dp){ return (int)(this.getResources().getDisplayMetrics().density*dp+0.5); } @Override public void onClick(View v) { if(v==mImageView|| v==this){ flexibleHeight(); } } /** * 对TextView进行伸缩处理 */ private void flexibleHeight() { isExpand=!isExpand; int textHeight=0; float startDegree=0.0f; float endDegree=180.0f; if(isExpand){ //如果是展开模式,那么取消最大行为maxLine的限制 textHeight=contentLine*mTextView.getLineHeight(); mTextView.setMaxLines(contentLine); }else{ textHeight=mTextView.getLineHeight()*maxLine; endDegree=0.0f; startDegree=180.0f; } final LayoutParams mParam=(LayoutParams) mTextView.getLayoutParams(); //TextView的平移动画 ValueAnimator animator_textView= ValueAnimator.ofInt(mTextView.getHeight(),textHeight); animator_textView.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mParam.height=(Integer)animation.getAnimatedValue(); mTextView.setLayoutParams(mParam); } }); //imageView的旋转动画 ObjectAnimator animator_img=ObjectAnimator.ofFloat(mImageView, "rotation", startDegree, endDegree); AnimatorSet mAnimatorSets=new AnimatorSet(); mAnimatorSets.setDuration(500); mAnimatorSets.play(animator_img).with(animator_textView); mAnimatorSets.start(); mAnimatorSets.addListener(new AnimatorListenerAdapter(){ @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); //动画结束之后,如果是非展开模式,则设置最大行数为maxLine if(!isExpand){ mTextView.setMaxLines(maxLine); } } }); } }

其实代码并不复杂,这里就是继承LinearLayout 并在里面放置一个普通的TextViewImageView,并在构造函数中对自定义的属性进行解析然后分别赋值给TextViewImageView的相应属性。

这里有一个细节需要注意,调用TextViewgetLineCount() 方法获取TextView的行数是放置在View的post方法中执行,为什么要这么做呢,因为如果在该View的构造方法中直接调用getLineCount() 方法,必然返回0,因为这个时候View 的 measure,layout,draw过程都没有执行,必然无法获取行数。

由于没有显示完的内容需要使用省略号表示,所以这里需要对TextView 进行一些属性的设置:

 mTextView.setMaxLines(maxLine);
 mTextView.setEllipsize(TruncateAt.END);

写到这里,估计有些同学就要纳闷了,这里都设置最大行数的属性了,为何还要通过getLineCount() 方法获取TextView 的内容 的行数?你能注意到这里,我不得不给你一个赞…,我现在给你解释一下我为什么这么做,为了让TextView 将为显示完的内容变为省略号,不得不设置上面连个属性,但是设置了两个属性之后,我就无法知道TextView 展开后的高度是多少,所以我是先调用了getLineCount() 之后,再设置上面属性的。然后在用户点击的时候将最大行数设置成contentLine 并通过动画不断设置TextView 的高度,从maxLine 行的高度变为contentLine 行的高度。

最后不得不说一下这里使用到的动画了,这里我使用的是属性动画,属性动画是Android 3.0之后引入的一种动画,较之前Android提供的两种动画,属性动画功能强大得多,网上也有很多关于属性动画的教程,不熟悉的同学可以在网上找找资料,想要学会自己定义炫酷的View,掌握各种动画的使用时必不可少的。

代码下载地址

你可能感兴趣的:(Android,android,移动互联网)