相信很多人用过微信, 一个小小的聊天业务, 一个 IM 软件的灵魂也就是这个聊天页面的呈现. 如何能能够做好这个功能, 细节点实在太多了, 这里慢慢给大家一步步描述重点, 画重点的时间到了.
聊天信息可以分为很多样, 例如文本, 语音, 相片, 视频, 地图, 特殊链接, 附件等等. 这么多样的业务, 如何才能扩展出比较适合的框架, 来应对多样业务变更呢.
因为很多人, 估计都非常厌烦编写 RecylerView, 因为每次添加一个新类型, 都需要编写添加新类型的代码, 如果不作特殊解耦, 很可能是 adapter 文件, 不断膨胀, 也越难维护.
RecylerView 框架
这里估计很多人都会想到使用一些巧妙的 RecylerView 的框架.
项目中最终决定选择了使用 https://github.com/Werb/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 显示时需要隐藏的情况. 文件的代码量也是让人头痛的.
来源: https://juejin.im/entry/5c6e6cbf5188252d56426f21