对于一个初学者来说,如何优雅的写好一个聊天消息列表是非常麻烦的事情,刚开始使用网易云demo中的UI库,但是该库特别沉重,就其中一些群,聊天室来说。我们可能是不需要的,引入进来就会增加apk的大小。后来我引用github上的一些开源库来实现,后来因为一些需求要更改,也特别麻烦,就有了自己写的想法。

设计思想

简单说一下该demo的设计思想,其实和大部分列表显示不同类型的Item差不多,使用Apdater中的getItemViewType方法给父类返回这Item所需要的布局。我这里借鉴了网易云demo中的实现方法。将每个Item布局分别继承一个基类,这样继承的类就可以专注一种类型的实现。不必关心其他类。

  1. 创建一个抽象的用于被继承的基类MessageViewHelper和一个继承该类的BaseMessageViewHelper。
  2. 创建一个工厂类用来储存继承自MessageViewHelper的子类,以及根据消息类型不同返回不同的Helper类。
  3. 在Adapter的onBindViewHolder方法中,我们获取到当前的消息类型,根据不同的消息类型,通过反射获得继承自MessageViewHelper的子类。得到实例之后helper.convert(holder, mDatas[position], position)实现基类抽象方法,将数据传递到子类中。
  4. 准备工作都做完了,接下来就是实现Helper,首先实现BaseMessageViewHelper,该类用来实现头像显示,判断消息方向,做一些通用的方法判断之类

大概的思路就是这样,接下来展示一些核心代码,我会带你一步步实现。代码我使用的是kotlin,这段时间刚好在学习这个,就想试着用用。不熟悉语法不要紧,思路是一样的。

第一步 MessageViewHelper

abstract class MessageViewHelper<out ADAPTER : RecyclerView.Adapter<MessageViewHolder>, in HOLDER : MessageViewHolder, in DATA>(adapter: ADAPTER){private val mAdapter:ADAPTER = adapterfun getAdapter() : ADAPTER{return mAdapter}//传递Adapter中的数据到Helper中abstract fun convert(holder: HOLDER, data: DATA, position: Int)
}

这个类特别简单,跟我上面介绍的一样,实现了Adapter,MessageViewHolder,Data(数据model)三个泛型和一个传递Adapter的构造函数。这几个泛型,是为了convert这个方法做服务。

第二步 ViewHelperFactory工厂类

class ViewHelperFactory {companion object {private val viewHelpers: HashMap<MessageType, Class<out BaseMessageViewHelper>> = HashMap()/*** 注册消息类型*/fun register(messageType: MessageType, viewhelper: Class<out BaseMessageViewHelper>) {viewHelpers.put(messageType, viewhelper)}/*** 获取所有继承自BaseMessageViewHelper的子类*/fun getAllViewHolders(): List<Class<out BaseMessageViewHelper>> {val list = ArrayList<Class<out BaseMessageViewHelper>>()list.add(TextViewHelper::class.java)list.add(UnknownViewHelper::class.java)when {viewHelpers.size > 0 -> list.addAll(viewHelpers.values)}return list}/*** 不同的消息类型返回不同的Helper*/fun getViewHolderByType(message: IMessage): Class<out BaseMessageViewHelper> {when {message.getMsgType() == MessageType.text -> return TextViewHelper::class.javaelse -> {var helper: Class<out BaseMessageViewHelper>? = nullwhile (helper == null && viewHelpers.size>0) {helper = viewHelpers[message.getMsgType()]}return if (helper==null) UnknownViewHelper::class.java else  helper}}}}
}

getAllViewHolders()这个静态方法,返回所有你继承自BaseMessageViewHelper的子类集合。getViewHolderByType()根据消息类型的返回与之匹配的Helper,其中TextViewHelper是我实现的一个文本显示的helper。

第三步 BaseRecyclerAdapter 适配器

这个类太长,我分开来说,

     helperViewType = HashMap()val list: List<Class<out BaseMessageViewHelper>> = ViewHelperFactory.getAllViewHolders()var viewType = 0for (helper: Class<out BaseMessageViewHelper> in list) {viewType++addItemType(viewType, R.layout.im_base_layout, helper)helperViewType[helper] = viewType}

helperViewType 是一个Map集合,使用工厂类中的getAllViewHolders()取得所有的Helper,在循环中将Helper根据viewType存储到Map中。

/*** viewType->布局*/private var layouts: SparseArray<Int>? = null/*** viewType->helper类*/private var helperClasses: SparseArray<Class<out BaseMessageViewHelper>>? = null/*** viewType->实例化helper*/private var typeViewHelper: MutableMap<Int, HashMap<String, BaseMessageViewHelper>>? = null........private fun addItemType(type: Int, layout: Int, helper: Class<out BaseMessageViewHelper>) {if (layouts == null) {layouts = SparseArray()}layouts!!.put(type, layout)if (helperClasses == null) {helperClasses = SparseArray()}helperClasses!!.put(type, helper)if (typeViewHelper == null) typeViewHelper = HashMap()typeViewHelper!!.put(type, HashMap())}

addItemType中,layouts中存储基类的布局资源,helperClasses中存储Helper类型类,typeViewHelper存储实例化的Helper,这个集合是为了避免重复实例化Helper所设置的;接下来就是获取layouts 中存储的布局资源,设置到MessageViewHolder中。

   override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MessageViewHolder {this.mLayoutInflater = LayoutInflater.from(mContext)return onCreateBaseViewHolder(parent!!, viewType)}....../*** 这里获取layouts中存储的布局资源,生成View,放入MessageViewHolder中*/private fun onCreateBaseViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {return MessageViewHolder(getItemView(layouts!![viewType], parent))}private fun getItemView(layoutResId: Int, parent: ViewGroup): View {return mLayoutInflater.inflate(layoutResId, parent, false)}

布局和返回的类型都设置好了,现在就是如何显示这些数据。这里也是第三步的核心。在onBindViewHolder方法中获取到当前的item类型,首先判断typeViewHelper中是否有了该类型的Helper,如果没有,就根据类型获取helperClasses其中的Helper,在通过反射获取到实例,将获取的到的实例存储到typeViewHelper中。最后把数据通过MessageViewHelper中的convert方法传递到它的子类中取。代码如下:

    override fun onBindViewHolder(holder: MessageViewHolder?, position: Int) {val itemType: Int = holder!!.itemViewTypeval itemKey: String = getItemKey(mDatas[position])var helper: BaseMessageViewHelper? = typeViewHelper?.get(itemType)?.get(itemKey)if (helper == null) {try {val cls: Class<out BaseMessageViewHelper> = helperClasses!!.get(itemType)val constructor = cls.declaredConstructors[0]constructor.isAccessible = truehelper = constructor.newInstance(this) as BaseMessageViewHelper?typeViewHelper!![itemType]!!.put(itemKey, helper!!)} catch (e: Exception) {e.printStackTrace()}}if (helper != null) {helper.convert(holder, mDatas[position], position)}}

全部代码我就不贴了,核心的都在这里。

第四步 BaseMessageViewHelper 统一设置

这个类就非常容易理解了,他是继承自MessageViewHelper类的子类,所以他实现了父类的抽象方法convert,从而就拿到了Adapter中的消息数据和View,拿到这些就可以做一些操作了,头像,消息背景,点击事件等,例如下面这些:

 /*** 设置列表的点击事件*/private fun setOnClick() {val helperListener: IMListEventListener = getAdapter().getHelperEvent() ?: returnmLayoutContent.setOnClickListener {helperListener.onItemClick(mData)}mLayoutContent.setOnLongClickListener {helperListener.onItemLongClick(mData)false}mLeftAvatar.setOnClickListener {helperListener.onLeftAvatar(mData)}mRightAvatar.setOnClickListener {helperListener.onRightAvatar(mData)}}/*** 设置内容布局显示*/@SuppressLint("RtlHardcoded")private fun setContentView() {val bodylayout: LinearLayout = findViewById(R.id.im_base_body)if (isMiddleItem()) {setGravity(bodylayout, Gravity.CENTER)} else {if (isMsgDirection()) {setGravity(bodylayout, Gravity.LEFT)mLayoutContent.setBackgroundResource(leftBackground())} else {setGravity(bodylayout, Gravity.RIGHT)mLayoutContent.setBackgroundResource(rightBackground())}}}

到这里就将整个列表的实现过程表述完了,可以直接下载demo,看我里面如何实现,如果有什么不明白的可以留言。我会尽量及时回复。

项目地址

【IM】网易lM聊天列表UI相关推荐

  1. iOS 客户端 IM 以及列表 UI 框架

    一.背景及简介 1.1 背景 已经好多年都没有写过什么东西了,近期换了新工作,换工作面试期间有面试官问,看你在知乎工作时间挺长,怎么也没写什么博客呢?仔细想想,工作期间做的一些项目,也有不少可以拿出来 ...

  2. iOS 微信 7.0.16 内测更新!支持只删除聊天列表不删除聊天记录!附内测地址!

    Android 微信 7.0.19 刚刚更新,iOS 微信 7.0.16 也开始内测. 本次内测有哪些更新呢?我们一起来看看! 速览 新增聊天框"不显示"选项: 新增表情搜索: 视 ...

  3. 利用swipelistview完成qq聊天列表右滑删除功能

    感谢HarryWeasley整理:http://blog.csdn.net/harryweasley/article/details/41413547 前言:前段时间,由于自己比较悠闲,没有什么工作上 ...

  4. 【稀饭】react native 实战系列教程之首页列表UI实现

    首页设计与实现 首先,这章节讲的是首页内容的设计与实现,不包括主界面的设计,因为一开始入手,我希望能立马获取到数据并能展示出来,后面再来搭木积似的一步一步把整体框架做起来. 设计 (图丑,莫见怪~) ...

  5. Android聊天列表Demo(QQ,微信,等通讯工具的聊天列表)

     Android聊天列表Demo(QQ,微信,等通讯工具的聊天列表)                                                                  ...

  6. Android学QQ聊天列表展示ListView

    Android学QQ聊天列表展示ListView 稍微修改下实现的,比完全copy感觉爽多了 布局文件:activity_main.xml <RelativeLayout xmlns:andro ...

  7. python爬取微信群聊天信息_微信 Windows 3.1.0 测试版发布:群聊设置备注,聊天列表不显示某个聊天...

    新酷产品第一时间免费试玩,还有众多优质达人分享独到生活经验,快来新浪众测,体验各领域最前沿.最有趣.最好玩的产品吧~!下载客户端还能获得专享福利哦! 原标题:微信 Windows 3.1.0 测试版发 ...

  8. 微信内测 | 微信订阅号又改回之前的聊天列表样式了

    微信最新测试版 来啦 !! ▼ 正好一个月之前,6月23日,微信安卓版v6.7.0测试版更新,改变了订阅号消息的展示方式,更加类似朋友圈样式.今天,微信测试版再次更新,版本号更新至v6.7.2,订阅号 ...

  9. Android项目中接入网易云信聊天

    首先上图 由于项目中原有的聊天出现收发消息不及时以及其他的问题,导致客服那边损失了不少的订单,遂接入新的第三方即时聊天sdk.有人可能会说,为什么不自己写呢?技术人员不够,时间长,开发成本高,最主要的 ...

最新文章

  1. 《Python for Data Analysis》之 Series
  2. vc++6.0获取磁盘基本信息_分享一个实用脚本--一键获取linux内存、cpu、磁盘IO等信息...
  3. 网站建设技术――智能建站系统
  4. [云炬ThinkPython阅读笔记]2.7 注释
  5. dockerq启动报错(iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --t
  6. 密码学基本概念(一)
  7. UVA 1264 - Binary Search Tree(BST+计数)
  8. Spring.NET教程(三)——对象的创建(基础篇)
  9. csv文件 java_Java生成CSV文件实例详解
  10. 楚留香ai识别人脸_【专利解密】商汤科技:AI加持人脸识别
  11. 现代通信原理:第七章部分习题答案
  12. (附源码)springboot基于Web的儿童教育管理系统的开发毕业设计281442
  13. 如何在工作中设定和使用 SMART 目标
  14. 用百数教培管理系统轻松实现在线排课,优化机构日常管理
  15. CVPR 2016 摘要阅读
  16. kingcms php 搜索,KingCMS内容管理系统
  17. C++ 宽字符 窄字符 char wchar
  18. ROWNUM 与 ROW_NUMBER()OVER() 的区别
  19. 读书笔记-C语言程序设计-西安电子科技大学出版社-王娟勤-【未完待续】
  20. 时间同步装置和卫星对时是变电站基本要求

热门文章

  1. 提问的智慧( 笔记)
  2. 正则表达式(C、C++、Python、Shell)
  3. Centos7安装Vastbase
  4. C语言计算出结构体所占空间大小
  5. 日常电脑操作小技能篇(生活无处不精彩)
  6. 基于MIPI的高性能成像系统
  7. 使用Python进行立体几何和立体校正的综合教程
  8. 多功能在线起名取名查重工具微信小程序源码 可开流量主 带安装教程
  9. Android桌面角标调研
  10. Android移动应用开发