Android 单个TextView 点击“显示全部”功能实现方法

网上很多TextView的“显示全部”,“显示更多”的方案实现都是两个TextView,一个在上面显示内容,一个在下面用来点击。但是我在实际工作中遇到的需求是“显示全部”提示要内联在原文的后面,使用一个TextView进行显示,不能放在原文的下面,下面把代码贴一下,主要实现的功能如下:

1、“显示全部”/“显示更多”紧连在正文的后面,不能另起一行

2、当字数超过一定数量显示“显示更多”,

3、当行数超过一定数量显示“显示更多”,比如每行只有一个字,不停的换行,虽然字少但是行数多,也应该将限制之外的行全部省略掉


效果展示

Android 单个TextView 点击“显示全部”功能实现方法_第1张图片

实现起来非常简单,主要步骤如下

1、首先判断要处理段落的字数是否超过限制,如果超过就在后面缀上“显示更多”

2、判断要处理段落在某个TextView上完整显示的行数,如果行数超过限制,那么就显示“显示全部”

3、使用SpannableString,构造:削减后的段落+“...显示更多”。然后将最后“...显示更多”这个字使用ClickableSpan设置上点击事件


有以下几个难点

1、如何在不进行UI绘制的情况下拿到TextView显示某段文字能显示多少行

2、如何获得第x行最后一个字的下标以便从此截取

3、在异步处理的环境中,如何在不进行绘制的情况下获得TextView会画多高以便预留位置


首先下面这段代码是通过传入一个TextView及其宽度,然后获得任意一行最末那个字符的下标的方法,只是为了业务方便,获取的是最大行限制的那一行最后一个字符,如果传入的文字不到最大行数限制,那么就返回-1,这个函数的作用是如果你要做行数“显示全部”限制的话,你知道该从一段文字的哪个地方开始截断。注意下面这个函数一定要在主线程进行执行


 /**
     * get the last char index for max limit row,if not exceed the limit,return -1
     * @param textView
     * @param content
     * @param width
     * @param maxLine
     * @return
     */
    public static int getLastCharIndexForLimitTextView(TextView textView, String content, int width, int maxLine){
        Log.i("Alex","宽度是"+width);
        TextPaint textPaint  = textView.getPaint();
        StaticLayout staticLayout = new StaticLayout(content, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
        if(staticLayout.getLineCount()>maxLine) return staticLayout.getLineStart(maxLine) - 1;//exceed
        else return -1;//not exceed the max line
    }
下面这个函数是在上面函数的基础上,在不绘制UI的前提下,计算一段文本显示的高度,获得它高度的主要目的是为了占位高度,免得上下滑动的时候屏幕跳跃,方便异步的显示这些文字。下面的代码在逻辑上做了相应的具体业务的处理,如果文字没有超出最大行数,那么就返回这段文字实际高度,如果超过了最大行数,那么就只返回最大行数之内的文本的高度
/**
     * 在不绘制textView的情况下算出textView的高度,并且根据最大行数得到应该显示最后一个字符的下标,请在主线程顺序执行,第一个返回值是最后一个字符的下标,第二个返回值是TextView最终应该占用的高度
     * @param textView
     * @param content
     * @param width
     * @param maxLine
     * @return
     */
    public static int[] measureTextViewHeight(TextView textView, String content, int width, int maxLine){
        Log.i("Alex","宽度是"+width);
        TextPaint textPaint  = textView.getPaint();
        StaticLayout staticLayout = new StaticLayout(content, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
        int[] result = new int[2];
        if(staticLayout.getLineCount()>maxLine) {//如果行数超出限制
            int lastIndex = staticLayout.getLineStart(maxLine) - 1;
            result[0] = lastIndex;
            result[1] = new StaticLayout(content.substring(0, lastIndex), textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1, 0, false).getHeight();
            return result;
        }else {//如果行数没有超出限制
            result[0] = -1;
            result[1] = staticLayout.getHeight();
            return result;
        }
    }


下面的函数就是上面效果展示中展示的例子,通过上面在不绘制UI的前提下获得最大行末尾文字下标,然后让源字符串subString这个下标,在获得的结果上加上“...read more”,然后将添加这一段文字设置点击事件,一个“显示更多”的功能就做好了。

/**
     * 限制为300字符,并且添加showmore和show more的点击事件
     * @param summerize
     * @param textView
     * @param clickListener textview的clickListener
     */
    public static void limitStringTo140(String summerize, final TextView textView, final View.OnClickListener clickListener){
        final long startTime = System.currentTimeMillis();
        if(textView==null)return;
        int width = textView.getWidth();//在recyclerView和ListView中,由于复用的原因,这个TextView可能以前就画好了,能获得宽度
        if(width==0)width = 1000;//获取textview的实际宽度,这里可以用各种方式(一般是dp转px写死)填入TextView的宽度
        int lastCharIndex = getLastCharIndexForLimitTextView(textView,summerize,width,10);
        if(lastCharIndex<0 && summerize.length() <= 300){//如果行数没超过限制
            textView.setText(summerize);
            return;
        }
        //如果超出了行数限制
        textView.setMovementMethod(LinkMovementMethod.getInstance());//this will deprive the recyclerView's focus
        if(lastCharIndex>300 || lastCharIndex<0)lastCharIndex=300;
        String explicitText = null;
        if(summerize.charAt(lastCharIndex)=='\n'){//manual enter
            explicitText = summerize.substring(0,lastCharIndex);
        }else if(lastCharIndex > 12){//TextView auto enter
            JLogUtils.i("Alex","the last char of this line is --"+lastCharIndex);
            explicitText = summerize.substring(0,lastCharIndex-12);
        }
        int sourceLength = explicitText.length();
        String showmore = "show more";
        explicitText = explicitText + "..." + showmore;
        final SpannableString mSpan = new SpannableString(explicitText);
        final String finalSummerize = summerize;
        mSpan.setSpan(new ClickableSpan() {
            @Override
            public void updateDrawState(TextPaint ds) {
                super.updateDrawState(ds);
                ds.setColor(textView.getResources().getColor(R.color.blue4d9cf2));
                ds.setAntiAlias(true);
                ds.setUnderlineText(false);
            }

            @Override
            public void onClick(View widget) {//"...show more" click event
                Log.i("Alex", "click showmore");
                textView.setText(finalSummerize);
                textView.setOnClickListener(null);
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (clickListener != null)
                            textView.setOnClickListener(clickListener);//prevent the double click
                    }
                }, 20);
            }
        }, sourceLength, explicitText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        textView.setText(mSpan);
        Log.i("Alex", "字符串处理耗时" + (System.currentTimeMillis() - startTime));
    }



你可能感兴趣的:(Android 单个TextView 点击“显示全部”功能实现方法)