朋友圈列表的點擊“全文”展開、點擊“收起”摺疊,實現起來很簡單,主要是以下兩步:
- 獲取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);
}
}
});
最終效果圖如下:
github地址:https://github.com/CrazyPumPkin/ExpandableText