看下效果图:
首先我们先理解收起和全文的逻辑:
假如产品设定:文字超过三行就要折叠
1 如果文字不超过三行,那么就不显示“全文”和“收起”
2 如果超过了三行显示“全文”,点击全文就可以看到所有文字,同时最下面显示“收起”
难点在如何记录每个Item的收起和全文的状态?
我们知道RecyerView和ListView都是复用ViewHolder,所以免不了有一个Holder要分别渲染两次数据,平时我们做的数据渲染只是把数据涂在了ViewHolder上,不影响Holder本身,但是这里需要改变Holder里面的状态,改变之后会影响下一个Holder的显示,如何解决呢?
public class TextHolder extends BaseHolder<News> {
private TextView content;
private TextView all_text;
private final int MAX_LINE_COUNT = 3;//最大显示行数
private final int STATE_UNKNOW = -1;//未知状态
private final int STATE_NOT_OVERFLOW = 1;//文本行数小于最大可显示行数
private final int STATE_COLLAPSED = 2;//折叠状态
private final int STATE_EXPANDED = 3;//展开状态
private SparseArray<Integer> mTextStateList;//保存文本状态集合
public TextHolder(View itemView) {
super(itemView);
content = itemView.findViewById(R.id.content);
all_text = itemView.findViewById(R.id.all_text);
}
@Override
public void bindViewHolder(News news) {
bindViewHolder(news,null);
}
/**
* 具体的绑定视图的操作
* @param news
* @param o
*/
public void bindViewHolder(final News news, final Object o) {
mTextStateList = (SparseArray<Integer>) o;
int state = mTextStateList.get(news.getId(), STATE_UNKNOW);
//第一次初始化,未知状态
if (state == STATE_UNKNOW) {
content.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
//这个回掉会调用多次,获取完行数后记得注销监听
content.getViewTreeObserver().removeOnPreDrawListener(this);
//content.getViewTreeObserver().addOnPreDrawListener(null);
//如果内容显示的行数大于最大显示行数
if (content.getLineCount() > MAX_LINE_COUNT) {
content.setMaxLines(MAX_LINE_COUNT);//设置最大显示行数
all_text.setVisibility(View.VISIBLE);//显示“全文”
all_text.setText("全文");
mTextStateList.put(news.getId(), STATE_COLLAPSED);//保存状态
} else {
all_text.setVisibility(View.GONE);
mTextStateList.put(news.getId(), STATE_NOT_OVERFLOW);
}
return true;
}
});
content.setMaxLines(Integer.MAX_VALUE);//设置文本的最大行数,为整数的最大数值
content.setText(news.getContent());
} else {
//如果之前已经初始化过了,则使用保存的状态。
switch (state) {
case STATE_NOT_OVERFLOW:
all_text.setVisibility(View.GONE);
break;
case STATE_COLLAPSED:
content.setMaxLines(MAX_LINE_COUNT);
all_text.setVisibility(View.VISIBLE);
all_text.setText("全文");
break;
case STATE_EXPANDED:
content.setMaxLines(Integer.MAX_VALUE);
all_text.setVisibility(View.VISIBLE);
all_text.setText("收起");
break;
}
content.setText(news.getContent());
}
//全文和收起的点击事件
all_text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int state = mTextStateList.get(news.getId(), STATE_UNKNOW);
if (state == STATE_COLLAPSED) {
content.setMaxLines(Integer.MAX_VALUE);
all_text.setText("收起");
mTextStateList.put(news.getId(), STATE_EXPANDED);
} else if (state == STATE_EXPANDED) {
content.setMaxLines(MAX_LINE_COUNT);
all_text.setText("全文");
mTextStateList.put(news.getId(), STATE_COLLAPSED);
}
}
});
}
}
public class NewsAgency extends BaseAdapterAgency<News> {
private Context context;
private Activity mContent;
/**
* 注意:保存文本状态集合的key一定要是唯一的,如果用position。
* 如果使用position作为key,则删除、增加条目的时候会出现显示错乱
* 而且不能在Holder里面创建!!!
*/
private SparseArray<Integer> mTextStateList;//保存文本状态集合
List<News> mList;
public NewsAgency(List<News> list, Activity context) {
super(context,list);
mContent = context;
this.mList = list;
mTextStateList = new SparseArray<>();
}
@Override
public void onBindViewHolder(BaseHolder holder, int position) {
if(holder instanceof TextHolder){
((TextHolder) holder).bindViewHolder(mList.get(position),mTextStateList);
}
}
@Override
public int getItemCount() {
return mList.size();
}
@Override
public int getAdapterItemViewType(int i) {
return 0;
}
@Override
public BaseHolder createBaseHolder(ViewGroup viewGroup, int i) {
return new TextHolder(mContent.getLayoutInflater().inflate(R.layout.item_text, viewGroup, false));
}
}
最后想说的是:
虽然网上有开源的库可以显示全文和收起,但是我认为开源库的最大问题是灵活的程度(扩展性),假如产品让你把文字显示为居中,居左,居右,颜色,字体,字号等等改变的时候,开源库很少能做到如此面面俱到,所以还是使用这种原生的方式比较好~