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

来源:互联网 发布:墨泥学车软件破解版 编辑:程序博客网 时间:2024/05/22 00:13

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

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

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

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


效果展示


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

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的高度,并且根据最大行数得到应该显示最后一个字符的下标,请在主线程顺序执行,第一个返回值是最后一个字符的下标,第二个返回值是高度     * @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 CustomTextView 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 = AlxTextView.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                JLogUtils.i("Alex", "click showmore");                AMPTrack.trackCLReadMoreReview(textView.getContext());                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));    }



0 0