KyleWang
发布于 2024-08-14 / 37 阅读
0
0

自定义折叠View

Android多行文本折叠展开效果

import android.content.Context;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
 * @author
 */
public class CollapsibleTextView extends LinearLayout {

    /**
     * 默认(展开与隐藏)文本颜色 蓝色
     */
    private ForegroundColorSpan expandAndHideColorSpan = new ForegroundColorSpan(Color.parseColor("#3b76ec"));

    /**
     * 默认(内容文本)文本颜色 黑色
     */
    private ForegroundColorSpan textColorSpan = new ForegroundColorSpan(Color.parseColor("#000000"));

    /**
     * 文本显示最大行数,默认为1
     */
    private int maxLines = 1;

    /**
     * 标记
     */
    private boolean flag, flag2, flag3;

    /**
     * 文本
     */
    private String text;

    /**
     * 文本长度
     */
    private int length;

    private TextView mCollapsibleTextView;

    public CollapsibleTextView(Context context) {
        super(context);
    }

    public CollapsibleTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        View view = inflate(context, R.layout.collapsible_text_view, this);
        mCollapsibleTextView = (TextView) view.findViewById(R.id.mCollapsibleTextView);
        //设置textView可点击
        mCollapsibleTextView.setMovementMethod(LinkMovementMethod.getInstance());
    }

    /**
     * 对外提供暴露的方法,为设置文本内容、文本显示最大行数、(内容文本)文本颜色、(展开与隐藏)文本颜色
     *
     * @param text               设置文本内容
     * @param maxLines           设置文本显示最大行数
     * @param textColor          (内容文本)文本颜色
     * @param expandAndHideColor (展开与隐藏)文本颜色
     */
    public void setText(String text, int maxLines, int textColor, int expandAndHideColor) {
        this.text = text;
        //赋值文本显示最大行数,最小是1
        this.maxLines = Math.max(maxLines, 1);
        //得到字符串的长度
        length = text.length();
        //setText调用就设置flag为true
        flag = true;
        //flag3为false时 设置一次最大行数
        if (!flag3) {
            //设置默认最大行数
            mCollapsibleTextView.setMaxLines(maxLines);
        }
        //设置文本
        mCollapsibleTextView.setText(text);
        //设置(内容文本)文本颜色
        textColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.black));
        //设置(展开与隐藏)文本颜色
        expandAndHideColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.black));
        //重新请求布局
        requestLayout();
    }

    /**
     * 文本设置
     * 设置展开或隐藏
     *
     * @param count
     * @param str
     * @return
     */
    private SpannableString getClickableSpan(int count, String str) {
        //新建一个spannableString(设置字体颜色,监听点击,字体大小)
        SpannableString span = new SpannableString(str);
        //count == 0时设置文本颜色并返回
        if (count == 0) {
            //内容文本 设置字体大小
            span.setSpan(new RelativeSizeSpan(1f), 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            //内容文本 设置字体可点击并监听
            span.setSpan(new TextListener(), 0, str.length(), Spanned.SPAN_MARK_MARK);
            //内容文本 设置字体颜色
            span.setSpan(textColorSpan, 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            //返回span
            return span;
        }
        int maxLines = mCollapsibleTextView.getMaxLines();
        if (maxLines == this.maxLines) {
            //内容文本 设置字体大小
            span.setSpan(new RelativeSizeSpan(1f), 0, count - 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            //内容文本 设置字体可点击并监听
            span.setSpan(new TextListener(), 0, count - 5, Spanned.SPAN_MARK_MARK);
            //内容文本 设置字体颜色
            span.setSpan(textColorSpan, 0, count - 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            //展开与隐藏 设置字体大小
            span.setSpan(new RelativeSizeSpan(1.1f), count - 5, count, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            //展开与隐藏 设置字体可点击并监听
            span.setSpan(new ExpandAndHideListener(), count - 5, count, Spanned.SPAN_MARK_MARK);
            //展开与隐藏 设置字体颜色
            span.setSpan(expandAndHideColorSpan, count - 5, count, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        } else {
            //内容文本 设置字体大小
            span.setSpan(new RelativeSizeSpan(1f), 0, count, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            //内容文本 设置字体可点击并监听
            span.setSpan(new TextListener(), 0, count, Spanned.SPAN_MARK_MARK);
            //内容文本 设置字体颜色
            span.setSpan(textColorSpan, 0, count, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            //展开与隐藏 设置字体大小
            span.setSpan(new RelativeSizeSpan(1.1f), count, count + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            //展开与隐藏 设置字体可点击并监听
            span.setSpan(new ExpandAndHideListener(), count, count + 3, Spanned.SPAN_MARK_MARK);
            //展开与隐藏 设置字体颜色
            span.setSpan(expandAndHideColorSpan, count, count + 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        //返回span
        return span;
    }

    /**
     * 1.展开与隐藏的点击事件
     * 2.(内容文本的点击事件 与 展开与隐藏文本的点击事件  可全部使用也可只使用一个 这里全部使用)
     */
    private class ExpandAndHideListener extends ClickableSpan implements View.OnClickListener {
        //文字点击事件
        @Override
        public void onClick(@NonNull View widget) {
            //flag设置并重新请求布局
            flagSetting();
        }

        @Override
        public void updateDrawState(@NonNull TextPaint ds) {
            super.updateDrawState(ds);
            ds.setUnderlineText(false);
        }
    }

    /**
     * 1.内容文本的点击事件
     * 2.(内容文本的点击事件 与 展开与隐藏文本的点击事件  可全部使用也可只使用一个 这里全部使用)
     */
    private class TextListener extends ClickableSpan implements View.OnClickListener {
        //文字点击事件
        @Override
        public void onClick(@NonNull View widget) {
            //flag设置并重新请求布局
            flagSetting();
        }

        @Override
        public void updateDrawState(@NonNull TextPaint ds) {
            super.updateDrawState(ds);
            ds.setUnderlineText(false);
        }
    }

    /**
     * flag设置并重新请求布局
     */
    private void flagSetting() {
        flag = true;
        if (flag3) {
            //mCollapsibleTextView.getLineCount() <= defaultMaxLines返回true 反之false
            flag2 = mCollapsibleTextView.getLineCount() <= maxLines;
            //重新请求布局
            requestLayout();
        }
    }

    /**
     * 布局设置
     *
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //flag为false返回
        if (!flag) {
            return;
        }
        //设置为false
        flag = false;

        //flag2为true展开
        if (flag2) {
            //展开状态
            mCollapsibleTextView.setMaxLines(Integer.MAX_VALUE);
            //在原字符串后面加上“ 隐藏”
            String allText = text + " 隐藏";
            SpannableString clickableSpan = getClickableSpan(length, allText);
            mCollapsibleTextView.setText(clickableSpan);
            flag3 = true;
        } else {
            //flag2为false 判断mCollapsibleTextView.getLineCount() > defaultMaxLines
            if (mCollapsibleTextView.getLineCount() > maxLines) {
                //收起状态
                mCollapsibleTextView.setMaxLines(maxLines);
                //得到第一行最后一个字符的位置
                int lineEnd = mCollapsibleTextView.getLayout().getLineEnd(maxLines - 1);
                //截取第一行的字符 替换最后五个字符为“...展开”
                String hiddenString = text.substring(0, lineEnd - 5) + "...展开";
                SpannableString clickableSpan = getClickableSpan(lineEnd, hiddenString);
                mCollapsibleTextView.setText(clickableSpan);
                flag3 = true;
            } else {
                SpannableString clickableSpan = getClickableSpan(0, text);
                mCollapsibleTextView.setText(clickableSpan);
            }
        }
    }

}

布局文件 collapsible_text_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/mCollapsibleTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:text=""
        android:textSize="18pt" />

</LinearLayout>

使用方法:

<xxxx.view.CollapsibleTextView
    android:id="@+id/mCollapsibleView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="20pt"
    android:gravity="top"
    android:textSize="18pt"
    />
CollapsibleTextView view =  holder.getView(R.id.mCollapsibleView);
//传入文本内容、默认要显示的行数、(内容文本)文本颜色、(展开与隐藏)文本颜色
 if (item.getPrincipal()!=null && !TextUtils.isEmpty(item.getPrincipal())){
      view.setText("责任人:"+item.getPrincipal(), 1, Color.BLACK, Color.BLACK);
}else {
      view.setText("责任人:暂无"+item.getPrincipal(), 1, Color.BLACK, Color.BLACK);
}

评论