使用Kotlin对RecycleView的Adapter进行封装

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

在12月05日凌晨的 Flutter Live 2018 上,Google 宣布 Flutter 1.0 正式发布。这是一个基于 Dart 的移动开发平台,旨在帮助开发者在 iOS 和 Android 两个平台上开发高质量的原生应用界面。此外,Google 还宣布了 Flutter 运行时基于 Web 的实验性实现,旨在将 Flutter 应用引入标准 Web 浏览器。

使用Kotlin对RecycleView的Adapter进行封装

丫丫.jpeg

丫丫真美,Kotlin真香

下面给大家介绍一个简单实用的`RecyclerAdapter`的封装

日常开发中,我们几乎不可能不使用RecyclerView这个列表,但是如果每次都去手动去实现它的Adapter,相比是极其痛苦的。每次固定的复写它的三个方法也是很无奈,所以笔者就想着封装一个既简单又使用的Adapter,满足日常的开发需求,又不冗余。在使用Kotlin封装的过程中,进一步体会下Kotlin语言的魅力。

重要的方法和类
  1. getItemCount():返回Int值,代表列表的长度

  2. onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder:通过item布局得到View对象,返回我们需要的ViewHolder对象

  3. onBindViewHolder(holder: BaseHolder, position: Int):通过position获取当前的item数据传给ViewHolder对象内控件

  4. RecyclerView.ViewHolder(itemView):通过获取的数据对itemView及其子View进行操作

对上面方法和类进行复写和继承

首先贴出对ViewHolder封装的整体代码

 1package com.taonce.onitemclick.BaseAdapter
2
3import android.support.v4.util.SparseArrayCompat
4import android.support.v7.widget.RecyclerView
5import android.view.View
6import org.jetbrains.anko.find
7
8
9/**
10 * Author: taoyongxiang
11 * Date: 2018/12/4
12 * Project: BaseAdapter
13 * Desc: RecyclerView.ViewHolder基类
14 */

15
16class BaseHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
17
18    // 使用`SparseArrayCompat`是为了复用view,不用每次`getView`都去`find`
19    private val mViews = SparseArrayCompat<View>()
20
21    /**
22     * 通过resId获取view
23     * 将获取到的View转换成具体的View,比如:TextView、Button等等
24     * 这里主要用到的是Kotlin里面的`as`操作符
25     */

26     fun <V : View> getView(id: Int): V {
27        var view = mViews[id]
28        if (view == null) {
29            view = itemView.find(id)
30            mViews.put(id, view)
31        }
32        return view as V
33    }
34}

BaseHolder中主要用到了两点:

  1. SparseArrayCompat是Android提供的一个工具类,它可以用来代替HashMap进行对象的存储,这样我们每次getView可以有效的进行复用View,不用每次都去find
    这里的find是Kotlin中的anko库,用来代替findViewById()

  2. getView()方法中我使用了泛型T,最终的返回用了as操作符,Kotlin中的as操作符很强大,比如返回的是一个ImageView,我们在使用这个T的时候不用在强转,直接可以调用ImageView的方法:

1    // 获取item中的ImageView
2    val image = holder.getView<ImageView>(R.id.item_image)
3    image.setImageResource(R.mipmap.ic_launcher)

其次我们来看看对Adapter封装的整体代码:

  1package com.taonce.onitemclick.BaseAdapter
 2
 3import android.content.Context
 4import android.support.v7.widget.RecyclerView
 5import android.util.Log
 6import android.view.LayoutInflater
 7import android.view.ViewGroup
 8
 9
10/**
11 * Author: taoyongxiang
12 * Date: 2018/12/4
13 * Project: BaseAdapter
14 * Desc: RecyclerAdapter基类
15 */

16
17abstract class BaseAdapter<T>(private val ctx: Context, private val layoutRes: Intval mData: MutableList<T>)
18    : RecyclerView.Adapter<BaseHolder>() {
19
20    private lateinit var mListener: OnItemClickListener
21    private lateinit var mLongListener: OnItemLongClickListener
22
23    fun setOnItemClickListener(mListener: OnItemClickListener) {
24        this.mListener = mListener
25    }
26
27    fun setOnItemLongClickListener(mLongListener: OnItemLongClickListener) {
28        this.mLongListener = mLongListener
29    }
30
31    /**
32     * 数据和item的绑定交给了convert()方法,将ViewHolder和position传进去
33     */

34    override fun onBindViewHolder(holder: BaseHolder, position: Int) {
35        convert(holder, position)
36        holder.itemView.setOnClickListener {
37            mListener.onItemClick(position)
38        }
39        holder.itemView.setOnLongClickListener { mLongListener.onItemLongClick(position) }
40    }
41
42    /**
43     * 通过layout的id生成ViewHolder对象
44     */

45    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder {
46        return BaseHolder(LayoutInflater.from(ctx).inflate(layoutRes, parent, false))
47    }
48
49    override fun getItemCount()Int {
50        return mData.size
51    }
52
53    /**
54     * 用来给具体Adapter实现逻辑的抽象方法
55     */

56    abstract fun convert(holder: BaseHolder, position: Int)
57
58    /**
59     * 添加一项数据
60     * item:添加的数据
61     * position:默认为最后一项
62     */

63    fun addData(item: T, position: Int = mData.size) {
64        mData.add(position, item)
65        notifyDataSetChanged()
66    }
67
68    /**
69     * 添加数据
70     * listData:添加的数据
71     * isDelete:是否删除原来的数据
72     */

73    fun addListData(listData: MutableList<T>, isDelete: Boolean) {
74        if (isDelete) {
75            mData.clear()
76        }
77        mData.addAll(listData)
78        notifyDataSetChanged()
79    }
80
81    /**
82     * 删除指定项数据
83     * position:从0开始
84     */

85    fun deletePositionData(position: Int) {
86        // 防止position越界
87        if (position >= 0 && position < mData.size) {
88            mData.remove(mData[position])
89            notifyDataSetChanged()
90        } else {
91            Log.d("taonce""delete item failed, position error!")
92        }
93    }
94
95    /**
96     * 删除所有数据
97     */

98    fun deleteAllData(){
99        mData.removeAll(mData)
100        notifyDataSetChanged()
101    }
102
103    /**
104     * item的点击事件
105     */

106    interface OnItemClickListener {
107        fun onItemClick(position: Int)
108    }
109
110    /**
111     * item的长按事件
112     */

113    interface OnItemLongClickListener {
114        fun onItemLongClick(position: Int)Boolean
115    }
116}

BaseAdapter中,同样使用了泛型T,这个泛型代表的是数据源的类型。

主要实现了以下几个功能:

  1. item的单击事件和长按事件,分别用了OnItemClickListenerOnItemLongClickListener两个接口。

  2. addData(item: T, position: Int = mData.size):用来向列表添加一项数据,添加的位置默认是最后一项,也可以通过position指定具体位置

  3. addListData(listData: MutableList, isDelete: Boolean):用来向列表添加多项数据,这里isDelete参数是用来判断是否删除之前的所有数据,比如在分页时,上拉加载,是不需要删除之前的数据;而刷新当前界面时,是需要重新加载数据的。所以加了一个标志位。

  4. deletePositionData(position: Int):这个方法是用来删除某项数据的

  5. deleteAllData():删除列表所有数据

这里我们把itemViewdata的绑定都交给了convert(holder: BaseHolder, position: Int)方法。下面看看具体的实现:

 1package com.taonce.onitemclick
2
3import android.content.Context
4import android.util.Log
5import android.widget.Button
6import android.widget.ImageView
7import android.widget.TextView
8import com.taonce.onitemclick.BaseAdapter.BaseAdapter
9import com.taonce.onitemclick.BaseAdapter.BaseHolder
10import kotlin.random.Random
11
12
13/**
14 * Author: taoyongxiang
15 * Date: 2018/12/4
16 * Project: BaseAdapter
17 * Desc: demo
18 */

19
20class MainAdapter(ctx: Context, layoutRes: Int, mData: MutableList<String>) : BaseAdapter<String>(ctx, layoutRes, mData) {
21    override fun convert(holder: BaseHolder, position: Int) {
22        // 获取item中的TextView
23        val text = holder.getView<TextView>(R.id.item_text)
24        text.text = this.mData[position]
25
26        // 获取item中的Button
27        val button = holder.getView<Button>(R.id.item_button)
28        button.text = "${Random.nextBoolean()}"
29        button.setOnClickListener {
30            Log.d("taonce""button click item is $position")
31        }
32
33        // 获取item中的ImageView
34        val image = holder.getView<ImageView>(R.id.item_image)
35        image.setImageResource(R.mipmap.ic_launcher)
36    }
37
38}

通过实现BaseAdapter类,复写它的convert()方法,直接在方法里面获取ItemView的子View,然后将数据和它绑定。

以后我们在用Adapter的时候只需要简单的一步就可以完成之前复写那么多的方法了。

在Activity实现封装的方法
 1package com.taonce.onitemclick
2
3import android.os.Bundle
4import android.support.v7.app.AppCompatActivity
5import android.support.v7.widget.LinearLayoutManager
6import android.util.Log
7import android.view.View
8import com.taonce.onitemclick.BaseAdapter.BaseAdapter
9import kotlinx.android.synthetic.main.activity_main.*
10
11class MainActivity : AppCompatActivity(), View.OnClickListener {
12
13    val mData = mutableListOf("1")
14    val adapter = MainAdapter(this, R.layout.item, mData)
15
16    override fun onCreate(savedInstanceState: Bundle?) {
17        super.onCreate(savedInstanceState)
18        setContentView(R.layout.activity_main)
19
20        bt_add.setOnClickListener(this)
21        bt_addAll.setOnClickListener(this)
22        bt_reduce.setOnClickListener(this)
23        bt_deleteAll.setOnClickListener(this)
24
25        recyclerView.layoutManager = LinearLayoutManager(this)
26        recyclerView.adapter = adapter
27        // 点击事件
28        adapter.setOnItemClickListener(object : BaseAdapter.OnItemClickListener {
29            override fun onItemClick(position: Int) {
30                Log.d("taonce""click item position is $position, value is ${mData[position]}")
31            }
32        })
33        // 长按事件
34        adapter.setOnItemLongClickListener(object : BaseAdapter.OnItemLongClickListener {
35            override fun onItemLongClick(position: Int)Boolean {
36                Log.d("taonce""long click position is $position, value is ${mData[position]}")
37                return true
38            }
39        })
40    }
41
42    override fun onClick(p0: View?) {
43        when (p0!!.id) {
44            // 添加一个数据
45            R.id.bt_add -> adapter.addData("add")
46            // 添加多项数据
47            R.id.bt_addAll -> adapter.addListData(mutableListOf("android""ios""kotlin""flutter"), true)
48            // 删除一项数据
49            R.id.bt_reduce -> adapter.deletePositionData(0)
50            // 删除全部数据
51            R.id.bt_deleteAll -> adapter.deleteAllData()
52        }
53    }
54}

效果图如下:

简单实现

源码已经上传至https://github.com/Taonce/Kotlin-BaseAdapter,可点击下方阅读原文查看

写在最后

每个人不是天生就强大,你若不努力,如何证明自己,加油!

Thank You!

--Taonce

如果你觉得这篇文章对你有所帮助,那么就动动小手指,长按下方的二维码,关注一波吧~~非常期待大家的加入

使用Kotlin对RecycleView的Adapter进行封装

专注Kotlin和Android知识的公众号


原文始发于微信公众号(Taonce):使用Kotlin对RecycleView的Adapter进行封装