下面给大家介绍一个简单实用的RecyclerAdapter
的封装 日常开发中,我们几乎不可能不使用RecyclerView
这个列表,但是如果每次都去手动去实现它的Adapter
,相比是极其痛苦的。每次固定的复写它的三个方法也是很无奈,所以笔者就想着封装一个既简单又使用的Adapter
,满足日常的开发需求,又不冗余。在使用Kotlin封装的过程中,进一步体会下Kotlin语言的魅力。
getItemCount()
:返回Int
值,代表列表的长度
onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder
:通过item
布局得到View
对象,返回我们需要的ViewHolder
对象
onBindViewHolder(holder: BaseHolder, position: Int)
:通过position
获取当前的item
数据传给ViewHolder
对象内控件
RecyclerView.ViewHolder(itemView)
:通过获取的数据对itemView
及其子View
进行操作
首先贴出对ViewHolder
封装的整体代码
package baseimport android.support.v4.util.SparseArrayCompatimport android.support.v7.widget.RecyclerViewimport android.view.Viewimport org.jetbrains.anko.find/** * Author: taoyongxiang * Date: 2018/12/4 * Project: BaseAdapter * Desc: RecyclerView.ViewHolder基类 */class BaseHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {// 使用`SparseArrayCompat`是为了复用view,不用每次`getView`都去`find`private val mViews = SparseArrayCompat<View>()/** * 通过resId获取view * 将获取到的View转换成具体的View,比如:TextView、Button等等 * 这里主要用到的是Kotlin里面的`as`操作符 */ fun <V : View> getView(id: Int): V {var view = mViews[id]if (view == null) {view = itemView.find(id)mViews.put(id, view)}return view as V}}
在BaseHolder
中主要用到了两点:
SparseArrayCompat
是Android提供的一个工具类,它可以用来代替HashMap
进行对象的存储,这样我们每次getView
可以有效的进行复用View
,不用每次都去find
。
这里的find
是Kotlin中的anko
库,用来代替findViewById()
。
在getView()
方法中我使用了泛型T
,最终的返回用了as
操作符,Kotlin中的as
操作符很强大,比如返回的是一个ImageView
,我们在使用这个T
的时候不用在强转,直接可以调用ImageView
的方法:
// 获取item中的ImageView val image = holder.getView<ImageView>(R.id.item_image) image.setImageResource(R.mipmap.ic_launcher)
其次我们来看看对Adapter
封装的整体代码:
package baseimport android.content.Contextimport android.support.v7.widget.RecyclerViewimport android.util.Logimport android.view.LayoutInflaterimport android.view.ViewGroup/** * Author: taoyongxiang * Date: 2018/12/4 * Project: BaseAdapter * Desc: RecyclerAdapter基类 */abstract class BaseAdapter<T>(private val ctx: Context, private val layoutRes: Int, val mData: MutableList<T>) : RecyclerView.Adapter<BaseHolder>() { private lateinit var mListener: OnItemClickListener private lateinit var mLongListener: OnItemLongClickListener fun setOnItemClickListener(mListener: OnItemClickListener) { this.mListener = mListener } fun setOnItemLongClickListener(mLongListener: OnItemLongClickListener) { this.mLongListener = mLongListener } /** * 数据和item的绑定交给了convert()方法,将ViewHolder和position传进去 */ override fun onBindViewHolder(holder: BaseHolder, position: Int) { convert(holder, position) holder.itemView.setOnClickListener { mListener.onItemClick(position) } holder.itemView.setOnLongClickListener { mLongListener.onItemLongClick(position) } } /** * 通过layout的id生成ViewHolder对象 */ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder { return BaseHolder(LayoutInflater.from(ctx).inflate(layoutRes, parent, false)) } override fun getItemCount(): Int { return mData.size } /** * 用来给具体Adapter实现逻辑的抽象方法 */ abstract fun convert(holder: BaseHolder, position: Int) /** * 添加一项数据 * item:添加的数据 * position:默认为最后一项 */ fun addData(item: T, position: Int = mData.size) { mData.add(position, item) notifyDataSetChanged() } /** * 添加数据 * listData:添加的数据 * isDelete:是否删除原来的数据 */ fun addListData(listData: MutableList<T>, isDelete: Boolean) { if (isDelete) { mData.clear() } mData.addAll(listData) notifyDataSetChanged() } /** * 删除指定项数据 * position:从0开始 */ fun deletePositionData(position: Int) { // 防止position越界 if (position >= 0 && position < mData.size) { mData.remove(mData[position]) notifyDataSetChanged() } else { Log.d("taonce", "delete item failed, position error!") } } /** * 删除所有数据 */ fun deleteAllData(){ mData.removeAll(mData) notifyDataSetChanged() } /** * item的点击事件 */ interface OnItemClickListener { fun onItemClick(position: Int) } /** * item的长按事件 */ interface OnItemLongClickListener { fun onItemLongClick(position: Int): Boolean }}
在BaseAdapter
中,同样使用了泛型T
,这个泛型代表的是数据源的类型。
主要实现了以下几个功能:
item
的单击事件和长按事件,分别用了OnItemClickListener
和OnItemLongClickListener
两个接口。
addData(item: T, position: Int = mData.size)
:用来向列表添加一项数据,添加的位置默认是最后一项,也可以通过position
指定具体位置
addListData(listData: MutableList<T>, isDelete: Boolean)
:用来向列表添加多项数据,这里isDelete
参数是用来判断是否删除之前的所有数据,比如在分页时,上拉加载,是不需要删除之前的数据;而刷新当前界面时,是需要重新加载数据的。所以加了一个标志位。
deletePositionData(position: Int)
:这个方法是用来删除某项数据的
deleteAllData()
:删除列表所有数据
这里我们把itemView
和data
的绑定都交给了convert(holder: BaseHolder, position: Int)
方法。下面看看具体的实现:
package com.taonce.onitemclickimport android.content.Contextimport android.util.Logimport android.widget.Buttonimport android.widget.ImageViewimport android.widget.TextViewimport base.BaseAdapterimport base.BaseHolderimport kotlin.random.Random/** * Author: taoyongxiang * Date: 2018/12/4 * Project: BaseAdapter * Desc: demo */class MainAdapter(ctx: Context, layoutRes: Int, mData: MutableList<String>) : BaseAdapter<String>(ctx, layoutRes, mData) {override fun convert(holder: BaseHolder, position: Int) {// 获取item中的TextViewval text = holder.getView<TextView>(R.id.item_text)text.text = this.mData[position]// 获取item中的Buttonval button = holder.getView<Button>(R.id.item_button)button.text = "${Random.nextBoolean()}"button.setOnClickListener {Log.d("taonce", "button click item is $position")}// 获取item中的ImageViewval image = holder.getView<ImageView>(R.id.item_image)image.setImageResource(R.mipmap.ic_launcher)}}
通过实现BaseAdapter
类,复写它的convert()
方法,直接在方法里面获取ItemView
的子View
,然后将数据和它绑定。
以后我们在用Adapter
的时候只需要简单的一步就可以完成之前复写那么多的方法了。
package com.taonce.onitemclickimport android.os.Bundleimport android.support.v7.app.AppCompatActivityimport android.support.v7.widget.LinearLayoutManagerimport android.util.Logimport android.view.Viewimport base.BaseAdapterimport kotlinx.android.synthetic.main.activity_main.*class MainActivity : AppCompatActivity(), View.OnClickListener { val mData = mutableListOf("1") val adapter = MainAdapter(this, R.layout.item, mData) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) bt_add.setOnClickListener(this) bt_addAll.setOnClickListener(this) bt_reduce.setOnClickListener(this) bt_deleteAll.setOnClickListener(this) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = adapter // 点击事件 adapter.setOnItemClickListener(object : BaseAdapter.OnItemClickListener { override fun onItemClick(position: Int) { Log.d("taonce", "click item position is $position, value is ${mData[position]}") } }) // 长按事件 adapter.setOnItemLongClickListener(object : BaseAdapter.OnItemLongClickListener { override fun onItemLongClick(position: Int): Boolean { Log.d("taonce", "long click position is $position, value is ${mData[position]}") return true } }) } override fun onClick(p0: View?) { when (p0!!.id) { // 添加一个数据 R.id.bt_add -> adapter.addData("add") // 添加多项数据 R.id.bt_addAll -> adapter.addListData(mutableListOf("android", "ios", "kotlin", "flutter"), true) // 删除一项数据 R.id.bt_reduce -> adapter.deletePositionData(0) // 删除全部数据 R.id.bt_deleteAll -> adapter.deleteAllData() } }}
效果图如下:
每个人不是天生就强大,你若不努力,如何证明自己,加油!
Thank You!
--Taonce
如果你觉得这篇文章对你有所帮助,那么就动动小手指,长按下方的二维码,关注一波吧~~非常期待大家的加入
Kotlin封装RecycleView的Adapter