“全文”和“收起”實現

朋友圈列表的點擊“全文”展開、點擊“收起”摺疊,實現起來很簡單,主要是以下兩步:

  • 獲取item文本的行數
  • 記錄item文本的狀態

1.獲取文本的行數

很容易想到獲取文本的行數,超出規定行數便摺疊文本,但沒有方法可以直接根據字數計算出TextView的行數,所以只能用

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

這時會發現這樣獲取到的行數爲0,因爲setText()後立即調用getLineCount()TextView還未完成measure,要想準確獲取到TextView的行數有兩種方法:

  • ViewTreeObserver監聽View初始化的各種狀態
    使用它的OnPreDrawListener在TextView完成測量和定位即將繪製時調用 getLineCount()即可得到TextView的真實行數
  • View.post(Runnable r)方法
    這個Runnable會被添加到一個順序執行的UI事件隊列,等執行到裏面的代碼時,View已經完成了measure和layout等一系列初始化工作,所以可以正確獲取到View的高度等信息,很好用的方法,相比第一種方法的好處就是代碼少且只執行一次,不用取消監聽

    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,感覺語義性更好

holder.content.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        //這個回調會調用多次,獲取完行數記得註銷監聽
                        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文本的狀態

如果只是像上面寫的那樣每次初始化item時去獲取文本的行數,然後根據行數選擇是否摺疊文本的話,會引發一個問題:

即已經獲取過行數的position item滑出可視範圍又滑回來時,根據RecyclerView的複用,TextView又會被重新測量高度行數然後是否摺疊,有興趣的同學可以試試,從列表頂部往下滑是沒問題,但從底部往上滑,列表會不斷跳動,在文字多的情況下甚至滑不回頂部,因爲上面即將進入可視範圍的item始終處於measure(展開)和超出行數摺疊文本的死循環

所以當獲取完每個position上的item文本行數後應把信息存起來,在這裏我們定義三種狀態並在每個item初始化時保存起來:
STATE_NOT_OVERFLOW //文本不超過規定行數
STATE_COLLAPSED //文本超過了規定行數,處於摺疊狀態
STATE_EXPANDED //文本超過了規定行數,被點擊後處於展開狀態

代碼如下:

int state = mTextStateList.get(position, STATE_UNKNOW);
    //如果該item是第一次初始化,則去獲取文本的行數
    if(state == STATE_UNKNOW){
        holder.content.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                //這個回調會調用多次,獲取完行數記得註銷監聽
                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("全文");
                    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{
        //如果之前已經初始化過了,則使用保存的狀態,無需再獲取一次
        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));
    }

最後設置點擊事件:

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章