美文网首页Android自定义View
Android 自定义可展开的ExpandTextView

Android 自定义可展开的ExpandTextView

作者: Angelicas | 来源:发表于2018-07-27 09:58 被阅读25次

    一、概述
    可折叠的textview是一个很常见的功能,相信大家都在微信朋友圈体验过这种场景:朋友发的笑话都只有半截,下面是一片白色,你要展开全文之后才能知道最后结果。
    其实这也不是什么高大上的东西,按照惯例,我们先来看看效果图 :

    二、按照流程我们就开始来看看代码啦
    1、ExpandTextView:

    package per.lijuan.expandtextviewdome;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.os.Build;
    import android.text.Layout;
    import android.text.SpannableString;
    import android.text.Spanned;
    import android.text.StaticLayout;
    import android.text.method.LinkMovementMethod;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.TextView;
    
    
    /**
     * 自定义控件,长文本展开收起TextView
     */
    
    @SuppressLint("AppCompatCustomView")
    public class ExpandTextView extends TextView {
        private String originText;// 原始内容文本
        private int initWidth = 0;// TextView可展示宽度
        private int mMaxLines = 3;// TextView最大行数
        private SpannableString SPAN_CLOSE = null;// 收起的文案(颜色处理)
        private SpannableString SPAN_EXPAND = null;// 展开的文案(颜色处理)
        private String TEXT_EXPAND = "  全部";
        private String TEXT_CLOSE = "  收起";
    
        public ExpandTextView(Context context) {
            super(context);
            initCloseEnd();
        }
    
        public ExpandTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initCloseEnd();
        }
    
        public ExpandTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initCloseEnd();
        }
    
        /**
         * 设置TextView可显示的最大行数
         * @param maxLines 最大行数
         */
        @Override
        public void setMaxLines(int maxLines) {
            this.mMaxLines = maxLines;
            super.setMaxLines(maxLines);
        }
    
        /**
         * 初始化TextView的可展示宽度
         * @param width
         */
        public void initWidth(int width){
            initWidth = width;
        }
    
        /**
         * 收起的文案(颜色处理)初始化
         */
        private void initCloseEnd(){
            String content = TEXT_EXPAND;
            SPAN_CLOSE = new SpannableString(content);
            ButtonSpan span = new ButtonSpan(getContext(), new OnClickListener(){
                @Override
                public void onClick(View v) {
                    ExpandTextView.super.setMaxLines(Integer.MAX_VALUE);
                    setExpandText(originText);
                }
            }, R.color.colorAccent);
            SPAN_CLOSE.setSpan(span, 0, content.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        }
    
        /**
         * 展开的文案(颜色处理)初始化
         */
        private void initExpandEnd(){
            String content = TEXT_CLOSE;
            SPAN_EXPAND = new SpannableString(content);
            ButtonSpan span = new ButtonSpan(getContext(), new OnClickListener(){
                @Override
                public void onClick(View v) {
                    ExpandTextView.super.setMaxLines(mMaxLines);
                    setCloseText(originText);
                }
            }, R.color.colorAccent);
            SPAN_EXPAND.setSpan(span, 0, content.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        }
    
        public void setCloseText(CharSequence text) {
    
            if(SPAN_CLOSE == null){
                initCloseEnd();
            }
            boolean appendShowAll = false;// true 不需要展开收起功能, false 需要展开收起功能
            originText = text.toString();
    
            // SDK >= 16 可以直接从xml属性获取最大行数
            int maxLines = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                maxLines = getMaxLines();
            } else{
                maxLines = mMaxLines;
            }
            String workingText = new StringBuilder(originText).toString();
            if (maxLines != -1) {
                Layout layout = createWorkingLayout(workingText);
                if (layout.getLineCount() > maxLines) {
                    //获取一行显示字符个数,然后截取字符串数
                    workingText = originText.substring(0, layout.getLineEnd(maxLines - 1)).trim();// 收起状态原始文本截取展示的部分
                    String showText = originText.substring(0, layout.getLineEnd(maxLines - 1)).trim() + "..." + SPAN_CLOSE;
                    Layout layout2 = createWorkingLayout(showText);
                    // 对workingText进行-1截取,直到展示行数==最大行数,并且添加 SPAN_CLOSE 后刚好占满最后一行
                    while (layout2.getLineCount() > maxLines) {
                        int lastSpace = workingText.length()-1;
                        if (lastSpace == -1) {
                            break;
                        }
                        workingText = workingText.substring(0, lastSpace);
                        layout2 = createWorkingLayout(workingText + "..." + SPAN_CLOSE);
                    }
                    appendShowAll = true;
                    workingText = workingText + "...";
                }
            }
    
            setText(workingText);
            if (appendShowAll) {
                // 必须使用append,不能在上面使用+连接,否则spannable会无效
                append(SPAN_CLOSE);
                setMovementMethod(LinkMovementMethod.getInstance());
            }
        }
    
        public void setExpandText(String text) {
            if(SPAN_EXPAND == null){
                initExpandEnd();
            }
            Layout layout1 = createWorkingLayout(text);
            Layout layout2 = createWorkingLayout(text + TEXT_CLOSE);
            // 展示全部原始内容时 如果 TEXT_CLOSE 需要换行才能显示完整,则直接将TEXT_CLOSE展示在下一行
            if(layout2.getLineCount() > layout1.getLineCount()){
                setText(originText + "\n");
            }else{
                setText(originText);
            }
            append(SPAN_EXPAND);
            setMovementMethod(LinkMovementMethod.getInstance());
        }
    
        //返回textview的显示区域的layout,该textview的layout并不会显示出来,只是用其宽度来比较要显示的文字是否过长
        private Layout createWorkingLayout(String workingText) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                return new StaticLayout(workingText, getPaint(), initWidth - getPaddingLeft() - getPaddingRight(),
                        Layout.Alignment.ALIGN_NORMAL, getLineSpacingMultiplier(), getLineSpacingExtra(), false);
            } else{
                return new StaticLayout(workingText, getPaint(), initWidth - getPaddingLeft() - getPaddingRight(),
                        Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
            }
        }
    }
    

    2、ButtonSpan

    package per.lijuan.expandtextviewdome;
    
    import android.content.Context;
    import android.content.res.Resources;
    import android.text.TextPaint;
    import android.text.style.ClickableSpan;
    import android.view.View;
    
    /**
     * Created by juan on 2018/06/26.
     */
    public class ButtonSpan extends ClickableSpan {
    
        View.OnClickListener onClickListener;
        private Context context;
        private int colorId;
    
        public ButtonSpan(Context context, View.OnClickListener onClickListener) {
            this(context, onClickListener, R.color.color_while);
        }
    
        public ButtonSpan(Context context, View.OnClickListener onClickListener, int colorId){
            this.onClickListener = onClickListener;
            this.context = context;
            this.colorId = colorId;
        }
    
        @Override
        public void updateDrawState(TextPaint ds) {
            ds.setColor(context.getResources().getColor(colorId));
            ds.setTextSize(dip2px(16));
            ds.setUnderlineText(false);
        }
    
        @Override
        public void onClick(View widget) {
            if (onClickListener != null) {
                onClickListener.onClick(widget);
            }
        }
    
        public static int dip2px(float dipValue)
        {
            final float scale = Resources.getSystem().getDisplayMetrics().density;
            return (int) (dipValue * scale + 0.5f);
        }
    }
    

    3、然后在XML布局中声明我们的自定义View

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="16dp"
        android:paddingTop="16dp">
    
        <per.lijuan.expandtextviewdome.ExpandTextView
            android:id="@+id/txt_content"
            android:textSize="18dp"
            android:textColor="@color/colorPrimary"
            android:layout_width="match_parent"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:ellipsize="end"
            android:layout_height="wrap_content" />
    </RelativeLayout>
    

    4、MainActivity

    package per.lijuan.expandtextviewdome;
    
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    
    public class MainActivity extends AppCompatActivity {
        private ExpandTextView mContentExpandTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mContentExpandTextView = (ExpandTextView) findViewById(R.id.txt_content);
            mContentExpandTextView.initWidth(getWindowManager().getDefaultDisplay().getWidth());
            // 设置最大行数
            mContentExpandTextView.setMaxLines(2);
            String content = "中国共产党是中国工人阶级的先锋队,同时是中国人民和中华民族的先锋队,是中国特色社会主义事业的领导核心,代表中国先进生产力的发展要求,代表中国先进文化的前进方向,代表中国最广大人民的根本利益。党的最高理想和最终目标是实现共产主义。";
            mContentExpandTextView.setCloseText(content);
        }
    }
    
    

    好了,本篇文章就这样了,存在不足的地方还望指导,感谢_

    附录:
    自定义可展开收起TextView,展开收起按钮紧跟文本内容

    源码下载

    相关文章

      网友评论

        本文标题:Android 自定义可展开的ExpandTextView

        本文链接:https://www.haomeiwen.com/subject/vgdwmftx.html