1. 程式人生 > >Android 單個TextView 點選“顯示全部”功能實現方法

Android 單個TextView 點選“顯示全部”功能實現方法

網上很多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的高度,並且根據最大行數得到應該顯示最後一個字元的下標,請在主執行緒順序執行,第一個返回值是最後一個字元的下標,第二個返回值是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));
    }