RecyclerAdapter icon indicating copy to clipboard operation
RecyclerAdapter copied to clipboard

简单易懂的 RecyclerView adapter 封装

RecyclerAdapter

简单易懂的 RecyclerView 通用 adapter 封装

Intention

需要迁移到 AndroidX,封装实现方法很简单,代码也很少。

Installation

Add it in your root build.gradle at the end of repositories:

	allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}

add the dependency to your build.gradle:

kotlinx.android.synthetic (LayoutContainer) Version: v1.0

    // ViewBinding Version
    implementation 'com.github.nukc:RecyclerAdapter:dsl-v1.2.1'

Usage

DSL 写法

// 返回 adapter
recycler_view.setup(LinearLayoutManager(this)) {
    hasStableIds = true
    
    renderItem<Int, ItemPureBinding> {
    	res(R.layout.item_pure)
        getViewBinding = {
            ItemPureBinding.bind(it)
        }
    }
    
    renderItem<NumberItem, ItemLeftBinding> {
        res(R.layout.item_left, R.layout.item_right) // 支持同个类型多个布局
        getViewBinding = {
            ItemLeftBinding.bind(it)
        }
        getItemViewType {
            when (data.number % 2) {
                0 -> R.layout.item_left
                else -> R.layout.item_right
            }
        }
        bind {
            // 可在这里更新视图
            binding.tvText.text = data.number.toString()
        }
        partialUpdate {
            // payloads 不为空的时候,在此局部更新
        }
    }
    
    renderItem<List<Banner>, ViewBannerBinding> {
        type = Banner::class.java  // 当 item 的数据是数组,需要设置 type
        getViewBinding = {
            ViewBannerBinding.bind(it)
        }
        res(R.layout.view_banner) {
            // 可在这里对试图进行设置,比如点击事件
        }
        bind {
           // ...
        }
        
        // ... 还有对应 Adapter 的其它一些方法
    }
    
}

常规写法

recycler_view.adapter = RecyclerAdapter.explosion()
            .register(bannerProvider)
            .register(chosenProvider)
            .register(object : PureLayoutProvider<Int>(Integer::class.java) {
                override fun getLayoutResId(): Int {
                    return R.layout.item_pure
                }

                override fun initHolder(holder: RecyclerView.ViewHolder, itemView: View) {
                    itemView.findViewById<View>(R.id.layout_likest).setOnClickListener {
                        // do something
                    }
                    // ..
                }
            })
            .register(multiProvider)
            .setItems(arrayListOf(banners, arrayListOf(chosen), 1))
            .build()

Provider

BaseProvider,以下为具体实现类

  • PureLayoutProvider 纯布局,不用绑定数据

  • SimpleProvider 不想自定义 ViewHolder(在 bind() 方法中 findViewById)

  • HolderProvider 大多数需求都可直接用此 Provider

  • MultiTypeProvider 遇到同个数据类型且有多个 viewType 的时候(比如聊天界面)

PureLayoutProvider

	object : PureLayoutProvider<Int>(Integer::class.java) {
                override fun getLayoutResId(): Int {
                    return R.layout.item_pure
                }

                override fun initHolder(holder: RecyclerView.ViewHolder, itemView: View) {
                    itemView.findViewById<View>(R.id.layout_likest).setOnClickListener {
                        // do something
                    }
                    // ..
                }
            }

MultiProvider

Model and Viewholder

public class NumberItem {
    private int number;

    public NumberItem(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}
private class MultiHolder(view: View) : RecyclerView.ViewHolder(view) {
    val tvText: TextView = view.findViewById(R.id.tv_text)
    var data: NumberItem? = null

    init {
        itemView.setOnClickListener {
            data?.let {
                // do something
            }
        }
    }
}

provider

    private val multiProvider = object
        : MultiTypeProvider<NumberItem, MultiHolder>(NumberItem::class.java) {
        /**
        * 需要先提供全部类型的 layoutResId
        * @see Builder.register
        */
        override fun providerAllLayoutResId(): IntArray {
            return intArrayOf(R.layout.item_left, R.layout.item_right)
        }

        /**
        * 根据 position 或 data 返回该位置的 layoutResId,同时当做该 item 的 view Type
        * @see RecyclerAdapter.getItemViewType
        */
        override fun getLayoutResId(position: Int, data: NumberItem): Int {
            return when (data.number % 2) {
                0 -> R.layout.item_left
                else -> R.layout.item_right
            }
        }

        /**
        * 可根据 viewType 返回 ViewHolder
        * @param itemView Inflate a new view hierarchy from the viewType
        * @param viewType = getLayoutResId
        */
        override fun provideHolder(itemView: View, viewType: Int): MultiHolder {
            return MultiHolder(itemView)
        }

        override fun bind(holder: MultiHolder, data: NumberItem) {
            holder.data = data
            holder.tvText.text = data.number.toString()
        }

    }

Other methods

Provider: 其它一些可覆写方法

fun getItemId(position: Int): Long = position.toLong()
fun onViewRecycled(holder: VH)
fun onFailedToRecycleView(holder: VH): Boolean = false
fun onViewAttachedToWindow(holder: VH)
fun onViewDetachedFromWindow(holder: VH)

Adapter: 之后都执行响应的 notify 方法

方法名 备注
refresh(items) 清空原先的数据再加入新的数据后刷新
add(position, item) 在指定位置插入
add(item) 在最后位置插入
addAll(positionStart, items) 在指定开始位置插入一个集合
addAll(items) 在最后位置插入一个集合
move(fromPosition, toPosition) 把 fromPosition 的 item 移动到 toPosition
change(position, item) 改变指定位置的数据,然后刷新 item
remove(position) 移除指定位置的 item
clear() 清空

License

Apache License, Version 2.0