多类型适配器
优势: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
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) {
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? = 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 Codeviewholder中的逻辑解耦到了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? = 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) {}
override fun bind(holder: BaseViewHolder, position: Int, payloads: List?) {
@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
) {
when (payloads) {
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
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]