多类型适配器

优势:

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

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

3、复用性扩展性更强

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

图片[1]-多类型适配器-牛翰网

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

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

图片[1]-多类型适配器-牛翰网

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

图片[1]-多类型适配器-牛翰网

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

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

图片[1]-多类型适配器-牛翰网

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

图片[1]-多类型适配器-牛翰网

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 

图片[1]-多类型适配器-牛翰网

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,实现接口

图片[1]-多类型适配器-牛翰网

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类型

图片[1]-多类型适配器-牛翰网

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

在新建一个卡片类型

图片[1]-多类型适配器-牛翰网

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

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

图片[1]-多类型适配器-牛翰网

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的使用更加简单

图片[1]-多类型适配器-牛翰网

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完全不用修改,一站式复用

局部刷新

图片[1]-多类型适配器-牛翰网

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

图片[1]-多类型适配器-牛翰网

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

 

来源链接:https://www.cnblogs.com/LiuZhen/p/18959361

© 版权声明
THE END
支持一下吧
点赞11 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容