Android TextView实现查看全部和收起功能

Android TextView实现查看全部和收起功能_第1张图片

在工作遇到上图所示的一个小需求,将“查看全部”的提示连在原文的后面,使用一个textview显示。实现该功能大致步骤:

  1. 判断处理的文字是否超过最大的限制行数;
  2. 如果超过行数限制,截取掉超过的部分,并加上“...查看全部”;
  3. 然后用SpannableString将“查看全部”设置为蓝色,并且给整个textview设置点击事件即可。

实现上述步骤的难点在于:

  1. 如何在setText()之前判断处理文字是否超过了最大的限制行数
  2. 如何获取超过限制行数最后一个文字的下标

解决以上两个问题需要用到一个处理TextView文本排版,拆行处理的工具类SaticLayoutSaticLayout构造函数很多,但最终回调用这个构造函数

    public StaticLayout(CharSequence source, int bufstart, int bufend,
                        TextPaint paint, int outerwidth,
                        Alignment align, TextDirectionHeuristic textDir,
                        float spacingmult, float spacingadd,
                        boolean includepad,
                        TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines)

参数说明:

  • CharSequence source 文本内容
  • int bufstart, int bufend, 开始位置和结束位置
  • TextPaint paint 文本画笔对象
  • int outerwidth 布局宽度,超出宽度换行显示
  • Alignment align 对齐方式
  • TextDirectionHeuristic textDir 文本显示方向
  • float spacingmult 行间距倍数,默认是1
  • float spacingadd 行距增加值,默认是0
  • boolean includepad 文本顶部和底部是否留白
  • TextUtils.TruncateAt ellipsize 文本省略方式,有 START、MIDDLE、 END、MARQUEE 四种省略方式
  • int ellipsizedWidth 省略宽度
  • int maxLines 最大行数

在构造函数中最后会相继调用generate()out()方法,对文本进行拆行处理。如果需要详细了解StaticLayout的工作原理,可参考StaticLayout 源码分析

然后我们可以通过调用getLineCount()方法获取到布局该文本的行数,调用getLineStart(int line)方法可以获取line下一行第一个文字的下标。

下面是具体实现的相关代码:

    private int maxLine = 3;
    private SpannableString elipseString;//收起的文字
    private SpannableString notElipseString;//展开的文字
    private void getLastIndexForLimit(TextView tv, int maxLine, String content) {
        //获取TextView的画笔对象
        TextPaint paint = tv.getPaint();
        //每行文本的布局宽度
        int width =getResources().getDisplayMetrics().widthPixels - dip2px(this,40);
        //实例化StaticLayout 传入相应参数
        StaticLayout staticLayout = new StaticLayout(content,paint,width, Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
        //判断content是行数是否超过最大限制行数3行
        if (staticLayout.getLineCount()>maxLine) {
            //定义展开后的文本内容
            String string1 = content + "    收起";
            notElipseString = new SpannableString(string1);
            //给收起两个字设成蓝色
            notElipseString.setSpan(new ForegroundColorSpan(Color.parseColor("#0079e2")), string1.length() - 2, string1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            //获取到第三行最后一个文字的下标
            int index = staticLayout.getLineStart(maxLine) - 1;
            //定义收起后的文本内容
            String substring = content.substring(0, index - 4) + "..." + "查看全部";
            elipseString = new SpannableString(substring);
            //给查看全部设成蓝色
            elipseString.setSpan(new ForegroundColorSpan(Color.parseColor("#0079e2")), substring.length() - 4, substring.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            //设置收起后的文本内容
            tv.setText(elipseString);
            tv.setOnClickListener(this);
            //将textview设成选中状态 true用来表示文本未展示完全的状态,false表示完全展示状态,用于点击时的判断
            tv.setSelected(true);
        } else {
            //没有超过 直接设置文本
            tv.setText(content);
            tv.setOnClickListener(null);
        }
    }

   /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context mContext, float dpValue) {
        final float scale = mContext.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

点击事件相关代码:

  @Override
    public void onClick(View v) {
        if (v.getId() ==R.id.tv) {
            if (v.isSelected()) {
                //如果是收起的状态
                tv.setText(notElipseString);
                tv.setSelected(false);
            } else {
                 //如果是展开的状态
                tv.setText(elipseString);
                tv.setSelected(true);
            }
        }
    }
}
2017.6.19补充---展开收起动画
Android TextView实现查看全部和收起功能_第2张图片

关于展开和收起动画应该如何添加,首先我们需要在textview外面包一层布局, 然后在自定义一个Animation,最后在点击事件处开始动画即可。

  • 简单布局xml


    
        
    


  • 自定义Animation
public class ExpandCollapseAnimation extends Animation{
    private final View mTargetView;//动画执行view
    private final int mStartHeight;//动画执行的开始高度
    private final int mEndHeight;//动画结束后的高度

    public ExpandCollapseAnimation(View contentview , int startHeight, int endHeight) {
        mTargetView = contentview;
        mStartHeight = startHeight;
        mEndHeight = endHeight;
        setDuration(2000);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        //applyTransformation()方法就是动画具体的实现,每隔一段时间会调用此方法
        //计算出每次应该显示的高度
        final int newHeight = (int)((mEndHeight - mStartHeight) * interpolatedTime + mStartHeight);
        //改变执行view的高度,实现动画
        mTargetView.getLayoutParams().height = newHeight;
        mTargetView.requestLayout();
    }
}
  • 动画的调用执行
  private View contentView;
  private int expandHeight;//view展开的高度
  private int elipseHeight;//view收起的高度
  private Animation animation;//动画
  private void getLastIndexForLimit(TextView tv, int maxLine, String content) {
     ......
    //以上代码省略
    //计算得出contentview最后展开的高度
     expandHeight= staticLayout.getHeight() + tv.getPaddingTop() + tv.getPaddingBottom();
    }

    @Override
    public void onClick(View v) {
        if (v.getId() ==R.id.tv) {
            if (v.isSelected()) {
                //收起的状态
                //因为现在是收起的状态,所以可以得到contentview开始执行动画的高度
                elipseHeight = tv.getHeight();
                animation = new ExpandCollapseAnimation(contentView,elipseHeight,expandHeight);
                animation.setFillAfter(true);
                animation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        //将contentview高度设置为textview的高度,以此让textview是一行一行的展示
                        contentView.getLayoutParams().height = elipseHeight;
                        contentView.requestLayout();
                        tv.setText(notElipseString);
                        tv.setSelected(false);
                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {

                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });

            } else {
                //展开
                animation = new ExpandCollapseAnimation(contentView,expandHeight,elipseHeight);
                animation.setFillAfter(true);
                animation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        //  动画结束后textview设置展开的状态
                        tv.setText(elipseString);
                        tv.setSelected(true);
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });
            }
            contentView.clearAnimation();
            //  执行动画
            contentView.startAnimation(animation);
        }
    }

以上就是实现展开收起的相关动画的代码,有不正确的地方,请大家指出。谢谢!

你可能感兴趣的:(Android TextView实现查看全部和收起功能)