Skip to content
能力中心
产品中心
应用市场
WebOffice
开发者后台

自定义会话列表界面

SDK 支持会话列表界面的自定义。可以通过配置接口、实现自定义协议两种方式来对会话列表进行自定义。

配置方式

可以通过配置方式完成会话列表界面的样式自定义,会话列表界面配置定义在 KIMChatListConfig 类中。

接口定义

KIMChatListConfig

属性类型说明
isEnabledChatListNetworkTipsBoolean会话列表网络异常提示视图是否显示, 默认为true。
isEnabledChatListMultiGroupBoolean是否开启会话分组模式,默认为true。
chatBackgroundDrawable@DrawableRes Int会话项-默认背景资源
chatStickiedDrawable@DrawableRes Int会话项-置顶背景资源
chatSelectedBackgroundDrawable@DrawableRes Int会话项 - 选中背景色
chatTimeFontKIMFont会话项 - 时间文字大小/字体样式
chatTimeColor@ColorRes Int会话项 - 时间文本颜色
chatTimeFormatter(time: Long) -> String会话项 - 会话时间格式
chatTitleFontKIMFont会话项 - 标题文字大小/标题字体样式
chatTitleColor@ColorRes Int会话项 - 标题字体颜色
chatContentFontKIMFont会话项 - 摘要 - 文字大小/摘要字体样式
chatContentColor@ColorRes Int会话项 - 摘要 - 字体颜色
chatUnreadBadgeBackgroundDrawable@DrawableRes Int会话项 - 未读数标记 - 背景资源
chatUnreadBadgeTextColor@ColorRes Int会话项 - 未读数标记 - 文字颜色
chatSeparatorDrawable@DrawableRes Int会话项 - 底部分割线图片资源
chatSeparatorStartPadding@DimenRes Int会话项 - 底部分割线左侧间距
chatSeparatorEndPadding@DimenRes Int会话项 - 底部分割线右侧间距
popupMenuCellBackgroundDrawable@DrawableRes Int加号按钮弹窗 cell - 默认背景资源

KIMFont

属性类型说明
textSize@DimenRes Int表示文字大小的dimen资源id
textStyleInt文字样式,默认为 Typeface.NORMAL

协议方式

可以通过实现会话列表自定义扩展接口来完成会话列表界面的自定义,自定义接口定义在KIMExpChatListPage 中。

接口定义

KIMExpChatListPage :

方法参数返回值说明
customTopView• context: Context 上下文
• parent: ViewGroup 父容器
View?
自定义顶部视图。
自定义会话列表顶部视图。可通过实现该方法自定义会话列表顶部视图,返回的自定义视图会固定在会话列表顶部与导航栏底部之间,不随会话列表滚动。
chatListHeaderView• context: Context 上下文
• parent: ViewGroup 父容器
View?
自定义 headerView。
自定义会话列表 headerView。可通过实现该方法自定义会话列表顶部视图,返回的自定义视图会被添加到会话列表的顶部并跟随列表滚动。
configTitleBar• titleView: CommonTitleBar
标题视图。
Unit配置会话列表导航栏标题视图。可实现该方法,通过 titleBar 参数获取导航栏标题视图控件,对标题视图控件进行配置,如修改标题样式、添加左右侧按钮等。
updateTitleBar• titleBar: CommonTitleBar
标题视图。
Unit更新会话列表导航栏标题视图。SDK 内部会更新 titleBar ,比如更新标题,修改样式等,如果客户需要拦截自行处理这些更新事件,则可以实现该方法覆盖内部的修改,与 configTitleBar 不同,此方法会调用多次。
onClickedAddButton• button: Button
标题栏加号按钮。
• defaultHandler: () -> Unit
默认点击处理,调用该闭包执行SDK内部默认点击行为。
Unit标题栏加号按钮点击事件拦截。点击标题栏加号按钮后会调用该方法,可以实现该方法拦截加号按钮点击事件,执行自定义操作。如果需要执行SDK内部默认点击行为,可以调用 defaultHandler 闭包实现。
onClickedSearchButton• button: Button
标题栏搜索按钮。
• defaultHandler: () -> Unit
默认点击处理,调用该闭包执行SDK内部默认点击行为。
Unit标题栏搜索按钮点击事件拦截。点击标题栏搜索按钮后会调用该方法,可以实现该方法拦截搜索按钮点击事件,执行自定义操作。如果需要执行SDK内部默认点击行为,可以调用 defaultHandler 闭包实现。
updatePopupMenus• context: Context 上下文
• menus: List<KIMMenuItem>
加号弹窗面板默认数据源。
List<KIMMenuItem>
修改后的加号弹窗面板数据源。
更新会话列表加号弹窗面板数据源。弹窗面板界面数据刷新时会调用该方法,可实现该方法对弹窗面板数据进行二次加工,items 为弹窗面板列表默认数据,可基于默认 items 进行改造,如修改排序、插入、删除元素,返回新的 KIMMenuItem 数组。
onClickedPopupMenuItem• context: Context 上下文
• item: KIMMenuItem
被点击的item。
• index: Int
被点击item所在的索引。
• defaultHandler: () -> Unit
默认点击处理,调用该闭包执行SDK内部默认点击行为。
Unit加号弹窗面板item点击事件拦截。点击标题栏搜索按钮后会调用该方法,可以实现该方法拦截搜索按钮点击事件,执行自定义操作。如果需要执行SDK内部默认点击行为,可以调用 defaultHandler闭包实现。

自定义示例

样式配置

会话列表样式配置需要在会话列表加载之前进行,可以通过修改 KIMChatListConfig 中定义的各种属性达到个性化样式配置。

代码示例

Kotlin
// 修改会话列表Cell标题文字颜色。
KIM.chatModule.chatListConfig.chatTitleColor = R.color.black
// 修改会话列表Cell标题文字大小。
KIM.chatModule.chatListConfig.chatTitleFont = KIMFont(R.dimen.chat_list_item_content)

自定义标题栏

会话列表标题栏默认展示了未读消息状态及右侧搜索、加号按钮。可通过实现KIMExpChatListPageconfigTitleBaronClickedSearchButtononClickedAddButton等协议方法来自定义标题栏的展示及点击事件。

代码示例

  1. 会话列表标题栏默认展示了未读消息状态及右侧搜索、加号按钮。可通过实现KIMExpChatListPageconfigTitleBarupdateTitleBaronClickedSearchButtononClickedAddButton等协议方法来自定义标题栏的展示及点击事件。
Kotlin
class CustomPageChatList : KIMExpChatListPage() {
    // 配置会话列表导航栏标题视图。
    override fun configTitleBar(titleBar: CommonTitleBar) {
        titleBar.apply {
            // 文字居中
            setTitlePosition(CommonTitleBar.TITLE_MIDDLE)
            // 设置背景色
            background =
                ContextCompat.getDrawable(titleBar.context, R.drawable.bg_custom_title_bar)
            // 设置标题文字颜色
            titleText.setTextColor(
                ContextCompat.getColor(
                    WAppRuntime.getApplication(),
                    R.color.white
                )
            )
            // 修改icon颜色
            listOf(rightIcon1, rightIcon2, titleBackIcon).forEach {
                it.setColorFilter(WResourcesUtil.getColor(R.color.white))
            }
        }

        //在标题栏上插入第三个按钮
        val view = ImageView(titleBar.context)
        view.setImageResource(R.drawable.ic_custom_floating)
        view.scaleType = ImageView.ScaleType.CENTER
        KIM.getService().getConfig<KIMCfgPageGlobal>()?.let {
            view.setColorFilter(WResourcesUtil.getColor(it.titleBarIconColor))
        }
        view.setOnClickListener {
            WToastUtil.show("The custom button has been clicked.")
        }
        titleBar.insertViewIntoRightIconsLayer(view)
    }

    override fun updateTitleBar(titleBar: CommonTitleBar) {
        // 此处拦截标题栏更新,可自定义标题栏内容
        titleBar.setTitleText(getTitle())
    }

    // 标题栏搜索按钮点击事件拦截。
    override fun onClickedAddButton(button: View, defaultHandler: () -> Unit) {
        WToastUtil.show("添加按钮被点击")
        // 实现自定义事件,如果需要执行默认事件操作,可以执行 defaultHandler.invoke()
        defaultHandler.invoke()
    }

    // 标题栏加号按钮点击事件拦截。
    override fun onClickedSearchButton(button: View, defaultHandler: () -> Unit) {
        WToastUtil.show("搜索按钮被点击")
        // 实现自定义事件,如果需要执行默认事件操作,可以执行 defaultHandler.invoke()
        defaultHandler.invoke()
    }

    private fun getTitle(): String {
        val state = KIM.getControl().connectStatusLiveData.value
        if (state == Constant.CONNECT_STATUS.CONNECTING) {
            return "会话(连接中)"
        } else if (state == Constant.CONNECT_STATUS.DIS_CONNECT) {
            return "会话(未连接)"
        } else {
            val unreadCount = KIM.chatModule.messageUnreadCount.value ?: 0
            if (unreadCount > 99) {
                return "会话(99+)"
            } else if (unreadCount <= 0){
                return "会话"
            } else {
                return "会话($unreadCount)"
            }
        }
    }
}

自定义加号弹窗面板

会话列表标题栏右上角加号弹窗面板默认添加了”发起群聊“和”通讯录“菜单,用户可以通过实现KIMExpChatListPageupdatePopupMenusonClickedPopupMenuItem 等接口方法来自定义加号弹窗面板菜单项及点击事件。

代码示例

  1. 继承 KIMExpPageChatList, 重写 updatePopupMenusonClickedPopupMenuItem 方法
Kotlin
class CustomPageChatList : KIMExpChatListPage() {
    // 更新会话列表加号弹窗面板数据源
    override fun updatePopupMenus(context: Context, menus: List<KIMMenuItem>): List<KIMMenuItem> {
        val list = ArrayList<KIMMenuItem>(menus)
        // list.removeAt(0)
        list.add(0, KIMMenuItem().apply {
            setTitle(R.string.kim_ui_chat_create_group)
            setIcon(R.drawable.kim_ui_chat_list_ic_menu_group_chat)
            setClickListener {
                WToastUtil.show("menu clicked")
                if (context !is FragmentActivity) {
                    return@setClickListener
                }
                KIM.chatModule.navigateToCreateGroupChat(context)
            }
        })
        return list
    }

    // 加号弹窗面板item点击事件拦截
    override fun onClickedPopupMenuItem(
        context: Context,
        item: KIMMenuItem,
        index: Int,
        defaultHandler: () -> Unit
    ) {
        WToastUtil.show("菜单${index}被点击")
         defaultHandler.invoke()
    }
}

拦截会话列表点击事件

可通过实现KIMExpChatListPageonClickedChatItem 协议方法拦截会话列表点击事件。

代码示例

Kotlin
class CustomPageChatList : KIMExpChatListPage() {
    override fun onClickedChatItem(
        context: Context,
        item: KIMChatItem,
        defaultHandler: () -> Unit
    ) {
        if (item.isChatItem()) {
            WToastUtil.show("会话${item.chat?.chatId}被点击")
        } else if (item.isBoxItem()) {
            WToastUtil.show("盒子${item.box?.boxType}被点击")
        }
        // defaultHandler.invoke()
    }
}

自定义会话列表长按事件

可通过实现KIMExpChatListPageonLongClickedChatItem 协议方法拦截会话列表点击事件。

代码示例

Kotlin
class CustomPageChatList : KIMExpChatListPage() {
    override fun onLongClickedChatItem(
        context: Context,
        item: KIMChatItem,
        defaultHandler: () -> Unit
    ) {
        if (item.isChatItem()) {
            WToastUtil.show("会话${item.chat?.chatId}被长按")
        } else if (item.isBoxItem()) {
            WToastUtil.show("盒子${item.box?.boxType}被长按")
        }
        // defaultHandler.invoke()
    }
}

顶部提示条

SDK会话列表默认提供了网络异常提示条的展示

如果想要自定义顶部提示条,可以通过以下方法:

代码示例

  1. 隐藏默认的顶部网络异常提示条。
Swift
KIM.chatModule.chatListConfig.isEnabledChatListNetworkTips = false
  1. 实现KIMExpChatListPagecustomTopViewchatListHeaderView 方法。
Kotlin
class CustomPageChatList : KIMExpChatListPage() {

    // 自定义会话列表顶部视图,返回的自定义视图会固定在会话列表顶部与导航栏底部之间,不随会话列表滚动。
    override fun customTopView(context: Context, parent: ViewGroup): View? {
        return ChatListStickView().createStickView(context, parent)
    }

    // 自定义会话列表 headerView。返回的自定义视图会被添加到会话列表的顶部并跟随列表滚动。
    override fun chatListHeaderView(context: Context, parent: ViewGroup): View? {
        return ChatListHeader1().createHeaderView(context, parent)
    }
}

插入自定义会话

SDK 提供扩展会话加载接口来支持插入自定义的会话数据。


需要实现如下几个方法:

接口定义

方法参数返回值说明
extChatLoaderKExtChatItemLoader自定义会话数据加载,无需求无需实现,这里需要实现返回自己的实现即可。更新会话数据时会通过此接口请求数据
来自KIMExpChatList
loadChatscallback: (List<KExtChatItem>?) -> Unit加载自定义会话,加载成功通过callback回调
来自KExtChatItemLoader
addOnExtChatUpdatedTriggertrigger: KOnExtChatUpdatedTriggerSDK 向接入方注册会话变化监听,接入方缓存tigger。
当会话数据发生变化需要更新UI时,通过KOnExtChatUpdatedTrigger通知sdk,不存在数据更新情况可以不实现
来自KExtChatItemLoader
removeOnExtChatUpdatedTrigger移除监听,若addOnExtChatUpdatedTrigger有实现,注意实现移除,否则会造成内存泄露
来自KExtChatItemLoader
notifyExtChatItemUpdatedchatItem: KExtChatItem
会话数据
payload: Any?
通过payload局部更新,RecyclerView#ViewHolder机制
会话数据发生变化,通知UI刷新,不会触发重排序
来自KOnExtChatUpdatedTrigger
notifyExtChatDataSetChanged会话整体数据发生变化【增/删/重排序】
来自KOnExtChatUpdatedTrigger
isContentSameother: KExtChatItembool数据比对,参考[androidx.recyclerview.widget.DiffUtil]机制。
该方法必现实现
来自KExtChatItem
getChangePayloadother: KExtChatItemAny?数据比对,参考[androidx.recyclerview.widget.DiffUtil]机制
来自KExtChatItem
stableId数据唯一标识id,用于数据区分及刷新
来自KExtChatItem

KExtChatItem :

方法参数返回值说明
pinOrderint固定顶部顺序,优先级最高。默认-1 表示不固定顶部。>= 0 固定顶部拍讯,值越小位置越前
来自KExtChatItem
isStickbool是否置顶,优先级次之,显示在固定顶部会话后,同时置顶的会话通过updateTime排序
来自KExtChatItem
updateTimelong默认排序,时间越大,排序靠前,默认会话使用更新时间[纳秒ns]排序
来自KExtChatItem
unreadCountInt会话cell设置未读数

代码示例

  1. 实现Cell:
Kotlin
//模板类声明View的实现类与数据类, 自定义会话Cell绘制需要实现KIMExpChatBaseCell扩展接口
class TodoChatCell : KIMExpChatBaseCell<TodoItemView, TodoChatItem>(TodoChatItem::class.java) {
    override fun onCreateView(parent: ViewGroup): TodoItemView {
        return TodoItemView(ItemChatTodoBinding.inflate(parent.inflater, parent, false))
    }
}

//View实现,这里使用了viewBinding,也可以直接使用view,xml布局自己控制
class TodoItemView(val binding: ItemChatTodoBinding) : ChatListItemView<TodoChatItem>(binding.root) {
    //绑定数据
    override fun onBindData(data: TodoChatItem, payloads: MutableList<Any>) {
        binding.lastMessage.setText("您有 ${data.num} 条新的待办消息")
        binding.chatTime.setText(KDateUtil.formatMsgTime(data.time, true))
        ...
    }

    override fun onItemClick(data: TodoChatItem): Boolean {
        //处理点击事件
        ...
        return true
    }

    override fun onItemLongClick(data: TodoChatItem): Boolean {
        //处理长按事件
        ...
        return true
    }
}
  1. 可以在KExtChatItem通过重写以下方法来修改在会话列表中的位置
Kotlin
//实现 KIMExpChatList 扩展
class MyChatList : KIMExpChatList() { 
    override fun extChatLoader(): KExtChatItemLoader? {
        //返回自定义会话加载
        return MyExtChatLoader.instance
    }
}

//实现自己的数据
class TodoChatItem : KExtChatItem("my-todo") {

    override fun pinOrder(): Int {
        //固定顶部
        return PinOrder.TODO_ORDER
    }

    override fun updateTime(): Long {
        //固定了updateTime可以不关注
        return 0
    }

    override fun isContentSame(other: KExtChatItem): Boolean {
        //数据比对
        return this == other
    }
}

//会话加载demo
class MyExtChatLoader : KExtChatItemLoader() {

    private val listeners = mutableListOf<KOnExtChatUpdatedTrigger>()
    // 可动态设置未读数
    private val chats = mutableListOf(TodoChatItem().apply { unreadCount = 3 },TodoChatItem().apply { unreadCount = 30 })

    companion object {
        val instance by lazy { MyExtChatLoader() }
    }

    override fun loadChats(callback: (List<KExtChatItem>?) -> Unit) {
        //这里演示加载会话情况,可以直接callkack返回,也可以从本地获取远端读取后返回
        ThreadManager.getInstance().networkIO().execute {
            Thread.sleep(50L)
            callback.invoke(chats)
        }
    }

    override fun addOnExtChatUpdatedTrigger(trigger: KOnExtChatUpdatedTrigger) {
        this.listeners.add(trigger)
    }

    override fun removeOnExtChatUpdatedTrigger(trigger: KOnExtChatUpdatedTrigger) {
       this.listeners.remove(trigger)
    }

    //发生更新
    fun updateMyChat(it: TodoChatItem) {
        // it.update()
        //数据内存值直接更新,通知更新
        listeners.forEach { l ->  l.notifyExtChatItemUpdated(it, "update") }
        //若影响排序或需要重新返回会话数据,可以通知数据变化,sdk会重新通过loadChats请求
        //listeners.forEach { l -> l.notifyExtChatDataSetChanged() }
    }
}

注册自定义实现

实现了会话列表自定义相关接口后,可通过以下方式注册自定义实现,达到替换SDK默认实现的效果。

代码示例

  1. 实现KIMSdkApplication,并注册自定义实现:
Kotlin
class AppCustomizeApplication : KIMSdkApplication() {
    override fun confMeatsOnBone(): KConf<Meat> {
        return kConfOf(
            // 注册自定义cell
            KIMExpChatBaseCell::class.java confTo TodoChatCell::class.java,
            // 注册自定义会话加载器
            KIMExpChatList::class.java confTo MyChatList::class.java,
            // 注册自定义会话列表扩展接口
            KIMExpChatListPage::class.java confTo CustomPageChatList::class.java
            ...
        )
    }
}
  1. AndroidManifest中声明:
Xml
<meta-data
   android:name="{your-package-name}.AppCustomizeSdkApplication"
   android:value="@string/kim_sdk_application" />

至此,完成了自定义会话列表的注册。