看下效果图:

android 展开更多 android 点击展开收起_android 展开更多

首先我们先理解收起和全文的逻辑:

假如产品设定:文字超过三行就要折叠
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));
    }
}

最后想说的是:

虽然网上有开源的库可以显示全文和收起,但是我认为开源库的最大问题是灵活的程度(扩展性),假如产品让你把文字显示为居中,居左,居右,颜色,字体,字号等等改变的时候,开源库很少能做到如此面面俱到,所以还是使用这种原生的方式比较好~