惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
MongoDB | Blog
MongoDB | Blog
博客园_首页
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
B
Blog RSS Feed
D
Docker
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
Recent Announcements
Recent Announcements
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
G
Google Developers Blog
V
V2EX
量子位
雷峰网
雷峰网
月光博客
月光博客
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog

博客园 - 翻滚的咸鱼

Ops权限行为记录源码解析 TaskView方案框架原理 视频解码器问题 常用命令 扫光动效 引导记录 性能优化 View共享动效 常用脚本文件 Launcher 卡片框架多模块集成 Launcher 桌面源码笔记二 Launcher 桌面源码笔记一 氛围灯动态屏保取色方案二 氛围灯动态屏保取色方案一 自定义最近任务管理器 anr问题 TV RecyclerView 焦点处理笔记 多屏下字体自动取色 阻尼拉伸方案 图片区域点击处理 RecyclerView设置默认焦点跟多页面焦点抢占 TV RecyclerView 滑动后操作保持落焦在左侧第一个View Fragment翻盖动效 当个View下,使用Drawable入场退场动画
多类型适配器
翻滚的咸鱼 · 2025-07-01 · via 博客园 - 翻滚的咸鱼

优势:

1、适配器通用,无论针对什么列表样式

2、viewholder分离,业务逻辑拆分到具体的item

3、复用性扩展性更强

首先看viewholder,没有复杂的业务逻辑

open class BaseViewHolder(val mBinding: ViewBinding) : RecyclerView.ViewHolder(mBinding.root) {

    fun setClick(clickRoot: View, clickListener: ((Int) -> Unit)?) {
        AnimatorUtil.pressView(
            clickRoot, Runnable { clickListener?.invoke(bindingAdapterPosition) }
        )
        clickRoot.setOnClickListener { clickListener?.invoke(bindingAdapterPosition) }
    }

}

View Code

适配器想要通用,就不能有业务逻辑,需要解耦

import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.zeekr.screensaver.home.holder.ScreenMainHolderFactory

/**
 * @author liuzhen
 * 多类型适配器
 * */
open class BaseAdapter : RecyclerView.Adapter<BaseViewHolder>() {

    var itemClickListener: ((Int) -> Unit)? = null
    private val list = mutableListOf<IBaseItem>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        ScreenMainHolderFactory.onCreateViewHolder(parent, viewType, itemClickListener)

    override fun getItemCount() = list.size

    fun getItem(position: Int) = list[position]

    fun getItems() = list

    override fun getItemViewType(position: Int) = getItem(position).itemType.value

    override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
        getItem(position).bind(holder, position)
    }

    override fun onBindViewHolder(holder: BaseViewHolder, position: Int, payloads: List<Any?>) {
        getItem(position).bind(holder, position, payloads)
    }

    fun setItems(newList: MutableList<IBaseItem>) {
        list.clear()
        list.addAll(newList)
    }

}

View Code

可以看到,简化了viewholder跟adapter,那么逻辑去哪了,在bean的上面加了一层item,可以把这个bean看成是item

首先需要定义一个接口,从adapter中看到所有数据类型都是 IBaseItem

import com.zeekr.screensaver.home.holder.ViewType

interface IBaseItem {

    val itemType: ViewType

    var isShowShimmer: Boolean

    fun bind(holder: BaseViewHolder, position: Int, payloads: List<Any?>? = null)

}

View Code

多类型安全,最好使用枚举

enum class ViewType(val value: Int) {
    TITLE(1), ITEM(2), BANNER(3), ME_ADD(4), ME_ITEM(5);

    companion object {
        fun fromValue(value: Int) = values().firstOrNull { it.value == value }
    }
}

View Code

统一管理ViewHolder

object ScreenMainHolderFactory {

    @JvmStatic
    fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int,
        itemClickListener: ((Int) -> Unit)?
    ): BaseViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return when (ViewType.fromValue(viewType)) {
            ViewType.TITLE -> BaseViewHolder(
                ItemMainSubTitleBinding.inflate(inflater, parent, false)
            )

            ViewType.BANNER -> BaseViewHolder(
                ItemMainBannerBinding.inflate(inflater, parent, false)
            )

            ViewType.ME_ADD -> {
                val binding = ItemMeAddBinding.inflate(inflater, parent, false)
                BaseViewHolder(binding).also {
                    it.setClick(binding.root, itemClickListener)
                }
            }

            ViewType.ME_ITEM -> {
                val binding = ItemMePhotoCardBinding.inflate(inflater, parent, false)
                BaseViewHolder(binding).also {
                    it.setClick(binding.clRoot, itemClickListener)
                }
            }

            else -> {
                val binding = ItemMainPhotoCardBinding.inflate(inflater, parent, false)
                BaseViewHolder(binding).also {
                    it.setClick(binding.clRoot, itemClickListener)
                }
            }
        }
    }

}

View Code

viewholder中的逻辑解耦到了item类中,先定义接口IBaseItem 

import com.zeekr.screensaver.home.holder.ViewType

interface IBaseItem {

    val itemType: ViewType

    var isShowShimmer: Boolean

    fun bind(holder: BaseViewHolder, position: Int, payloads: List<Any?>? = null)

}

View Code

结合viewbinding,实现接口

import androidx.viewbinding.ViewBinding
import com.zeekr.screensaver.home.holder.ViewType

abstract class BaseBindItem<V : ViewBinding>(val viewType: ViewType) : IBaseItem {

    override val itemType = viewType
    override var isShowShimmer = true

    abstract fun onBindViewHolder(position: Int, mBinding: V)

    open fun onBindViewHolder(position: Int, mBinding: V, payloads: List<Any?>) {}

    override fun bind(holder: BaseViewHolder, position: Int, payloads: List<Any?>?) {
        @Suppress("UNCHECKED_CAST")
        val binding = holder.mBinding as V
        if (payloads.isNullOrEmpty()) {
            onBindViewHolder(position, binding)
        } else {
            onBindViewHolder(position, binding, payloads)
        }
    }

}

View Code

这样,已经根据多类型封装好了,只要根据视图,创建类型item,直接将viewholder的业务转移到了item中

比如新建一个banner类型

class MainBannerItem(
    private val list: List<ScreenItemBean>
) : BaseBindItem<ItemMainBannerBinding>(ViewType.BANNER) {

    override fun onBindViewHolder(position: Int, mBinding: ItemMainBannerBinding) {
        mBinding.apply {
            if (isShowShimmer) bannerPage.onClear()
            bannerPage.start(list, bannerCarousel)
            groupContent.isInvisible = isShowShimmer
            shimmerView.isVisible = isShowShimmer
            shimmerView.setImageResource(R.drawable.icon_main_banner_loading)
            if (isShowShimmer) shimmerView.startShimmer() else shimmerView.stopShimmer()
        }
    }

}

View Code

在新建一个卡片类型

class MePhotoCardItem(var bean: ScreenItemBean, private val currentId: Int) :
    BaseBindItem<ItemMePhotoCardBinding>(ViewType.ME_ITEM) {

    override fun onBindViewHolder(position: Int, mBinding: ItemMePhotoCardBinding) {
        mBinding.apply {
            tvTitle.text = bean.getTitle()
            tvTitle.setPXTextSize(R.dimen.common_sp_32)
            tvTitle.setExtTextColor(R.color.main_card_title_color)
            if (bean.isCustom()) {
                GlideCacheUtils.getInstance(root.context).glideLoadPicturePath(
                    bean.customBitmap, ivPhoto
                )
            } else {
                GlideCacheUtils.getInstance(root.context).loadHomeItemUrl(bean.getUrl(), ivPhoto)
            }
            linSuite.isVisible = bean.isCabin()
            linSuite.setExtBackground(R.drawable.shape_item_main_card_suite)
            ivPhotoSuite.setExtSrc(R.drawable.icon_main_card_photo_suite)
            tvSuiteTitle.setPXTextSize(R.dimen.common_sp_20)
            tvSuiteTitle.setExtTextColor(R.color.main_card_title_suite_color)
            groupMask.isInvisible = bean.getTitle()?.isEmpty() == true
            ivCheck.isVisible = bean.isCheck(currentId)
            clRoot.isInvisible = isShowShimmer
            shimmerView.isVisible = isShowShimmer
            shimmerView.setImageResource(R.drawable.icon_main_photo_card_loading)
            if (isShowShimmer) shimmerView.startShimmer() else shimmerView.stopShimmer()
        }
    }

}

View Code

可以观察到,它其实充当bean的角色,又是独立的item

在创建一个特殊的自定义功能卡片类型

class MeAddItem(@MeAddItemType val type: Int) : BaseBindItem<ItemMeAddBinding>(ViewType.ME_ADD) {

    companion object {
        const val TYPE_AI = 0
        const val TYPE_PHOTO = 1

        @IntDef(TYPE_AI, TYPE_PHOTO)
        @Retention(AnnotationRetention.SOURCE)
        annotation class MeAddItemType
    }

    override fun onBindViewHolder(position: Int, mBinding: ItemMeAddBinding) {
        mBinding.apply {
            when (type) {
                TYPE_AI -> ivPhoto.setImageResource(R.drawable.icon_me_card_ai)
                else -> ivPhoto.setImageResource(R.drawable.icon_me_card_photo)
            }
        }
    }

}

View Code

随意扩展

而adapter的使用更加简单

private val mAdapter = BaseAdapter()
init {
        val inflater = LayoutInflater.from(context)
        mBinding = LayoutScreenRecyclerBinding.inflate(inflater, this, true).apply {
            recyclerView.adapter = mAdapter
            recyclerView.layoutManager = ScreenMainGridManager(mAdapter, context)
            recyclerView.addItemDecoration(ScreenMainItemDecoration())
        }
}

    private fun loadData() {
        LogUtil.d(tag, "loadData")
        lifecycleScope.launch(Dispatchers.Main) {
            val items = withContext(Dispatchers.IO) {
                val data = LocalResourceManager.getHistorySuits()
                val items = mutableListOf<IBaseItem>()
                items.add(MeAddItem(MeAddItem.TYPE_AI))
                items.add(MeAddItem(MeAddItem.TYPE_PHOTO))
                items.add(MainSubTitleItem(MainSubTitleItem.HISTORY))
                data.map { items.add(MePhotoCardItem(ScreenItemBean(suit = it), currentId)) }
                items
            }
            setItems(items)
        }
    }

    private fun loadData(list: List<TestBean>) {
        mBinding.root.isVisible = true
        val items = mutableListOf<IBaseItem>()
        list.forEach {
            items.addAll(it.map { MainPhotoCardItem(it) })
        }
        mAdapter.setItems(items)
    }

View Code

只要是基于RecyclerView,都不需要另外创建adapter,只需要新建你的类型视图,而adapter跟viewholder完全不用修改,一站式复用

局部刷新

override fun onBindViewHolder(position: Int, mBinding: ItemMainPhotoCardBinding) {
        mBinding.apply {
            tvTitle.text = bean.getTitle()
            updateProgress(this)
        }
    }

    private fun updateProgress(mBinding: ItemMainPhotoCardBinding) {
        mBinding.apply {
            ivDown.isVisible = !bean.isLocal && bean.progress <= 0
            progress.isVisible = bean.progress > 0
            progress.setProgressValue(bean.progress)
        }
    }

    override fun onBindViewHolder(
        position: Int,
        mBinding: ItemMainPhotoCardBinding,
        payloads: List<Any?>
    ) {
        when (payloads[0]) {
            PAYLOAD_PROGRESS_TYPE -> updateProgress(mBinding)
        }
    }

View Code

自定义Decoration

class ScreenMainItemDecoration : RecyclerView.ItemDecoration() {

    private val bottomSideMargin =
        Utils.getApp().resources.getDimensionPixelOffset(R.dimen.common_dp_128)

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        val position = parent.getChildAdapterPosition(view)
        val count = parent.adapter?.itemCount ?: 0
        val isLastItem = position == count - 1
        outRect.bottom = if (isLastItem) bottomSideMargin else 0
    }
}

View Code