Android 查看更多 TextView实现

在公司开发过程当中,须要用到固定行数的展开查看更多的控件,在网上找了很久,都没有找到,因而花了半天时间实现了一个,在此作下记录,主要有几个关键的地方:html

private Layout makeTextLayout(String text) {
        return new StaticLayout(text, getPaint(), getWidth() - getPaddingLeft() - getPaddingRight(),
                Layout.Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
    }

生成StaticLayout 对象,经过它就能够知道有多少行和每一行最后及最前一位字符对应的文本Sring的下标,经过对它进行裁剪,便可实现固定显示多少行。java

完整代码以下:canvas


/**
 * 固定行数展开收缩控件
 * Created by evan on 2016/3/3.
 */
public class FolderTextView extends TextView{

    private static final String ELLIPSIS="...";
    private static final String FOLD_TEXT = "收缩";
    private static final String UNFOLD_TEXT = "查看详情";

    /**
     * 收缩状态
     */
    private boolean isFold = false;

    /**
     * 绘制,防止重复进行绘制
     */
    private boolean isDrawed = false;
    /**
     * 内部绘制
     */
    private boolean isInner = false;

    /**
     * 折叠行数
     */
    private int foldLine;

    /**
     * 全文本
     */
    private String fullText;
    private float mSpacingMult = 1.0f;
    private float mSpacingAdd = 0.0f;



    public FolderTextView(Context context) {
        this(context, null);
    }

    public FolderTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FolderTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(R.styleable.FolderTextView);
        foldLine = a.getInt(R.styleable.FolderTextView_foldline, 3);

        a.recycle();
    }

    /**
     * 不更新全文本下,进行展开和收缩操做
     * @param text
     */
    private void setUpdateText(CharSequence text){
        isInner = true;
        setText(text);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if(TextUtils.isEmpty(fullText) || !isInner){
            isDrawed = false;
            fullText = String.valueOf(text);
        }
        super.setText(text, type);
    }

    @Override
    public void setLineSpacing(float add, float mult) {
        mSpacingAdd = add;
        mSpacingMult = mult;
        super.setLineSpacing(add, mult);
    }

    public int getFoldLine() {
        return foldLine;
    }

    public void setFoldLine(int foldLine) {
        this.foldLine = foldLine;
    }

    private Layout makeTextLayout(String text) {
        return new StaticLayout(text, getPaint(), getWidth() - getPaddingLeft() - getPaddingRight(),
                Layout.Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if(!isDrawed){
            resetText();
        }
        super.onDraw(canvas);
        isDrawed = true;
        isInner = false;
    }

    private void resetText() {
        String spanText = fullText;

        SpannableString spanStr;

        //收缩状态
        if(isFold){
            spanStr = createUnFoldSpan(spanText);
        }else{ //展开状态
            spanStr = createFoldSpan(spanText);
        }

        setUpdateText(spanStr);
        setMovementMethod(LinkMovementMethod.getInstance());
    }

    /**
     * 建立展开状态下的Span
     * @param text 源文本
     * @return
     */
    private SpannableString createUnFoldSpan(String text) {
        String destStr = text + FOLD_TEXT;
        int start = destStr.length() - FOLD_TEXT.length();
        int end = destStr.length();

        SpannableString spanStr = new SpannableString(destStr);
        spanStr.setSpan(clickSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        return spanStr;
    }

    /**
     * 建立收缩状态下的Span
     * @param text
     * @return
     */
    private SpannableString createFoldSpan(String text) {
        String destStr = tailorText(text);
        int start = destStr.length() - UNFOLD_TEXT.length();
        int end = destStr.length();

        SpannableString spanStr = new SpannableString(destStr);
        spanStr.setSpan(clickSpan,start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        return spanStr;
    }

    /**
     * 裁剪文本至固定行数
     * @param text 源文本
     * @return
     */
    private String tailorText(String text){
        String destStr = text + ELLIPSIS + UNFOLD_TEXT;
        Layout layout = makeTextLayout(destStr);

        //若是行数大于固定行数
        if(layout.getLineCount() > getFoldLine()){
            int index = layout.getLineEnd(getFoldLine());
            if(text.length() < index){
                index = text.length();
            }
            String subText = text.substring(0, index-1); //从最后一位逐渐试错至固定行数
            return tailorText(subText);
        }else{
            return destStr;
        }
    }

    ClickableSpan clickSpan = new ClickableSpan() {
        @Override
        public void onClick(View widget) {
            isFold = !isFold;
            isDrawed = false;
            invalidate();
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            ds.setColor(ds.linkColor);
        }
    };
}


其中定义了一个属性foldLine,用于设置收缩行数ide

定义到attrs.xml中this

<declare-styleable name="FolderTextView">
        <attr name="foldline" format="integer" />
    </declare-styleable>
至此,展开查看更多和收起的功能实现 ,使用和TextView的使用方法一致,这里就不介绍了。