IM使用的多种聊天信息显示以及拓展

647 阅读3分钟
原文链接: xiaozhuanlan.com

相信很多人用过微信,一个小小的聊天业务,一个IM软件的灵魂也就是这个聊天页面的呈现。如何能能够做好这个功能,细节点实在太多了,这里慢慢给大家一步步描述重点,画重点的时间到了。
聊天信息可以分为很多样,例如文本,语音,相片,视频,地图,特殊链接,附件等等。这么多样的业务,如何才能扩展出比较适合的框架,来应对多样业务变更呢。
因为很多人,估计都非常厌烦编写RecylerView,因为每次添加一个新类型,都需要编写添加新类型的代码,如果不作特殊解耦,很可能是adapter文件,不断膨胀,也越难维护。

RecylerView框架

这里估计很多人都会想到使用一些巧妙的RecylerView的框架。
项目中最终决定选择了使用MoreType这个框架。
使用了大概三个月的时间,暂时没发现特别的问题,非常稳定高效,而且解耦性也非常高。唯一一点不好的地方,就是我们需要扩展它的MoreAdapter的时候,发现他竟然不是open(kotlin属性)的,那么就是不能继承了,那么我们只能拉取他的源代码下来再继承了。文件量也不多,可以随便改了。
基础的使用非常简单,扩展使用如下:

//扩展ViewHolder
abstract class ChatViewHolder<T : Any>(override val containerView: View) : MoreViewHolder<T>(containerView) {
    override fun bindData(data: T, payloads: List<Any>) {

    }
    //map用于拓展参数
    abstract fun bindData(@NonNull context: Context,
                          @NonNull messageRecord: T,
                          @NonNull glideRequests: GlideRequests,
                          @NonNull batchSelected: Set<MessageRecord>,
                          @NonNull conversationRecipient: Recipient,
                          isSeries: Boolean, values: Map<String, String>)
}

//扩展MoreAdapter
class ChatConversationAdapter : MoreAdapter(){

   override fun onBindViewHolder(holder: MoreViewHolder<Any>, position: Int) {
        bindView(holder, position)
    }

    private fun bindView(holder: MoreViewHolder<Any>, position: Int) {
        val any = list[position]
        Logger.d((any as AmeGroupMessageDetail).toString())
        holder as ChatViewHolder
        //添加额外的参数
        val map = ArrayMap<String, String>()
        map.put(HAS_BUBBLE, (position / bubbleRadio == 0).toString())
        holder.bindData(holder.itemView.context, any, glideRequests, batchSelected, recipient, false, map)
}

类型区分

首先需要理解,每种信息都有接收和发送两种之分,需要两种不同的类。除了系统信息是一样的。
接收和发送的信息一般里面展现都是差不多,那么其里面的View就可以复用的。

千万别想着发送和接收都使用同一个类型,觉得这样RecylerView只对两种布局复用完美了。但是事实上,如果一个布局中不同的逻辑的管理和控制是非常繁琐的,你需要不停的检测需要显示那种类型,显示了文本类型,就需要对上一种视频类型的View进行隐藏,然后还需要隐藏掉其他类型的View,一个布局里面的逻辑,将会越来越繁琐,倘若你忘记了对某个View隐藏,正好测试也没能测出来,那将会是灾难的。关键字是卡顿和隐藏。

Signal中使用的就是那种一个布局多个类型的View的显示,它们使用了ViewStub的方式的完善预加载,但是如果一个群聊中已经有几万条消息的存在,虽然复用着逻辑,但是对View的复用逻辑的使用,也是非常繁琐的,如果添加一种类型将会非常复杂,你还需要兼顾到其他类型的View显示时需要隐藏的情况。文件的代码量也是让人头痛的。