朋友圈的“全文”“收起”實(shí)現(xiàn)

朋友圈列表的點(diǎn)擊“全文”展開(kāi)、點(diǎn)擊“收起”折疊,實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,主要是以下兩步:

  • 獲取item文本的行數(shù)
  • 記錄item文本的狀態(tài)

1.獲取文本的行數(shù)

很容易想到獲取文本的行數(shù),超出規(guī)定行數(shù)便折疊文本,但沒(méi)有方法可以直接根據(jù)字?jǐn)?shù)計(jì)算出TextView的行數(shù),所以只能用

content.setText();
content.getLineCount();

這時(shí)會(huì)發(fā)現(xiàn)這樣獲取到的行數(shù)為0,因?yàn)?code>setText()后立即調(diào)用getLineCount()TextView還未完成measure,要想準(zhǔn)確獲取到TextView的行數(shù)有兩種方法:

  • ViewTreeObserver監(jiān)聽(tīng)View初始化的各種狀態(tài)
    使用它的OnPreDrawListener在TextView完成測(cè)量和定位即將繪制時(shí)調(diào)用 getLineCount()即可得到TextView的真實(shí)行數(shù)
  • View.post(Runnable r)方法
    這個(gè)Runnable會(huì)被添加到一個(gè)順序執(zhí)行的UI事件隊(duì)列,等執(zhí)行到里面的代碼時(shí),View已經(jīng)完成了measure和layout等一系列初始化工作,所以可以正確獲取到View的高度等信息,很好用的方法,相比第一種方法的好處就是代碼少且只執(zhí)行一次,不用取消監(jiān)聽(tīng)

    The UI event queue will process events in order. After setContentView() is invoked, the event queue will contain a message asking for a relayout, so anything you post to the queue will happen after the layout pass

這里還是用了第一種方法ViewTreeObserver,感覺(jué)語(yǔ)義性更好

holder.content.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                   @Override
                   public boolean onPreDraw() {
                       //這個(gè)回調(diào)會(huì)調(diào)用多次,獲取完行數(shù)記得注銷監(jiān)聽(tīng)
                       holder.content.getViewTreeObserver().removeOnPreDrawListener(this);
                       if(holder.content.getLineCount() > MAX_LINE_COUNT){
                           holder.content.setMaxLines(MAX_LINE_COUNT);
                           holder.expandOrCollapse.setVisibility(View.VISIBLE);
                           holder.expandOrCollapse.setText("全文");
                       }else{
                           holder.expandOrCollapse.setVisibility(View.GONE);
                       }
                       return true;
                   }
               });
holder.content.setMaxLines(Integer.MAX_VALUE);
holder.content.setText(Util.getContent(position));

2.記錄item文本的狀態(tài)

如果只是像上面寫(xiě)的那樣每次初始化item時(shí)去獲取文本的行數(shù),然后根據(jù)行數(shù)選擇是否折疊文本的話,會(huì)引發(fā)一個(gè)問(wèn)題:
**
即已經(jīng)獲取過(guò)行數(shù)的position item滑出可視范圍又滑回來(lái)時(shí),根據(jù)RecyclerView的復(fù)用,TextView又會(huì)被重新測(cè)量高度行數(shù)然后是否折疊,有興趣的同學(xué)可以試試,從列表頂部往下滑是沒(méi)問(wèn)題,但從底部往上滑,列表會(huì)不斷跳動(dòng),在文字多的情況下甚至滑不回頂部,因?yàn)樯厦婕磳⑦M(jìn)入可視范圍的item始終處于measure(展開(kāi))和超出行數(shù)折疊文本的死循環(huán)
**

所以當(dāng)獲取完每個(gè)position上的item文本行數(shù)后應(yīng)把信息存起來(lái),在這里我們定義三種狀態(tài)并在每個(gè)item初始化時(shí)保存起來(lái):
STATE_NOT_OVERFLOW //文本不超過(guò)規(guī)定行數(shù)
STATE_COLLAPSED //文本超過(guò)了規(guī)定行數(shù),處于折疊狀態(tài)
STATE_EXPANDED //文本超過(guò)了規(guī)定行數(shù),被點(diǎn)擊后處于展開(kāi)狀態(tài)

代碼如下:

int state = mTextStateList.get(position, STATE_UNKNOW);
    //如果該item是第一次初始化,則去獲取文本的行數(shù)
    if(state == STATE_UNKNOW){
        holder.content.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                //這個(gè)回調(diào)會(huì)調(diào)用多次,獲取完行數(shù)記得注銷監(jiān)聽(tīng)
                holder.content.getViewTreeObserver().removeOnPreDrawListener(this);
                //記錄文本的狀態(tài)
                if(holder.content.getLineCount() > MAX_LINE_COUNT){
                    holder.content.setMaxLines(MAX_LINE_COUNT);
                    holder.expandOrCollapse.setVisibility(View.VISIBLE);
                    holder.expandOrCollapse.setText("全文");
                    mTextStateList.put(position, STATE_COLLAPSED);
                }else{
                    holder.expandOrCollapse.setVisibility(View.GONE);
                    mTextStateList.put(position, STATE_NOT_OVERFLOW);
                }
                return true;
            }
        });
        holder.content.setMaxLines(Integer.MAX_VALUE);
        holder.content.setText(Util.getContent(position));
    }else{
        //如果之前已經(jīng)初始化過(guò)了,則使用保存的狀態(tài),無(wú)需再獲取一次
        switch (state){
            case STATE_NOT_OVERFLOW:
                holder.expandOrCollapse.setVisibility(View.GONE);
                break;
            case STATE_COLLAPSED:
                holder.content.setMaxLines(MAX_LINE_COUNT);
                holder.expandOrCollapse.setVisibility(View.VISIBLE);
                holder.expandOrCollapse.setText("全文");
                break;
            case STATE_EXPANDED:
                holder.content.setMaxLines(Integer.MAX_VALUE);
                holder.expandOrCollapse.setVisibility(View.VISIBLE);
                holder.expandOrCollapse.setText("收起");
                break;
        }
        holder.content.setText(Util.getContent(position));
    }

最后設(shè)置點(diǎn)擊事件:

holder.expandOrCollapse.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int state = mTextStateList.get(position, STATE_UNKNOW);
                    if(state == STATE_COLLAPSED){
                        holder.content.setMaxLines(Integer.MAX_VALUE);
                        holder.expandOrCollapse.setText("收起");
                        mTextStateList.put(position, STATE_EXPANDED);
                    }else if(state == STATE_EXPANDED){
                        holder.content.setMaxLines(MAX_LINE_COUNT);
                        holder.expandOrCollapse.setText("全文");
                        mTextStateList.put(position, STATE_COLLAPSED);
                    }
                }
            });

最終效果圖如下:


chip.gif

github地址:https://github.com/CrazyPumPkin/ExpandableText

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容