0%

多布局的聊天页面开发

MultiType是一个很好的多类型列表视图库,它在实现一些复杂的recyclerview列表时非常方便,而作者所举的例子也确实适合大部分场景,但看过timemachine的源码后,我认为multitype仍然不太适合聊天应用的列表布局,毕竟timemachine聊天布局的adapter也只是用int值来区分布局类型。

主要原因在于multitype是通过类型与布局绑定,而在聊天应用列表里,一般都只有一个message类型,我们需要通过两种标识,来区分左右布局以及内容布局,而adapter的viewtype就是个int值。当然也可以在开发之初就定义好信息内容的子类型,但这样就变成项目必须去“适应”第三方库了,对于任何一个开发者来说这都是不好的习惯。不过multitype提出了一个很好的布局实现思路,参考仿造微博的数据结构和二级ItemViewBinder

这里插入介绍一下baserecyclerview(下称brvah)的multi布局,brvah是对adapter的一个封装,因此仍然是通过int值来区分每一个布局,但没办法和multitype搭配一起使用,这样每一个布局xml都需要携带所有要显示的控件,而部分控件冗余,给修改和扩展都带来不便,而且存在大量不同的布局,在同一屏幕上显示有限,实际viewholder是无法被有效缓存的(真机测试中也会发现onCreateViewHolder是会不断被调用)。

那么为什么不能将两种方式结合起来呢?先来分析一般情况下聊天界面的布局:

如上,无论是发送出去的消息还是接收的消息,基本可以划分为:头像、名字、内容区。每一条消息的头像和名字(微信发送者是没有名字的)是固定的。至于采取发送的还是接收的信息布局,取决于消息体的发送者id是否与登录用户id一致(在getItemViewType作判断),而内容区则根据消息的类型变化。由于用户发送的信息(向左布局)还需要携带发送状态,因此只要实现一左一右的两个固定外层布局即可(xml示例参考上面给出的multitype链接)。

对于内容区(以下将内容区的view称为itemview,即framelayout所添加的布局),若不考虑缓存的话,简单利用工厂模式,在onBindViewHolder的时候根据消息体类型去生成对应的itemview并添加到framelayout上即可,如此其性能表现已经比为每一种viewtype创建一个布局要好得多了,而且对头像等固定要素只需要在onBindViewHolder处设置一遍。

当然我们不能止步于此,而是要实现像viewholder一样的缓存已创建好的itemview并复用,需要解决以下几个问题:

  1. 如何获知viewholder绑定了哪个itemview
  2. viewholder被回收后itemview的处理

针对第一个问题,则从viewholder本身入手,在onBindViewHolder时添加itemview并记录到viewholder的一个变量当中.

1
2
3
4
5
6
7
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
MessageItem item = list.get(position);
holder.setView(item);
Nestitemview<MessageItem> itemView = cache.getItemView(item.getItemType(), holder);
holder.addChild(itemView, item);
}

由于viewholder被回收时会调用onViewRecycled,在此方法处removeView,并将保存的itemview归还给cache缓存。

1
2
3
4
5
@Override
public void onViewRecycled(BaseViewHolder holder) {
cache.detachView(holder);
super.onViewRecycled(holder);
}

大致流程如下图所示:

这其实是非常简单的东西…但实际使用的时候发现了一个问题…当用户滑动速度非常快的时候,会出现viewholder未执行removeview就被再次显示到屏幕上了,这将会导致

  1. itemview被添加到两个framelayout上导致出错
  2. 某个framelayout添加了两个itemview导致重叠

因此在获取缓存中的itemview和绑定时,framelayout需要判断childviewcount是否为0,itemview需要判断parent是否为null

最后是我对上面操作的一个简单的封装库:https://github.com/pye52/Nestviewholder

(这么简单的东西都能凑个1k多字,明显就是来骗star的Orz)