Android RecylerView BaseAdapter 更新(kotlin)

废话不多说直接上代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package com.qijin.xiaohui.base


import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.IntDef
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.qijin.xiaohui.R
import java.util.*


/**
 * RecylerView 数据绑定适配器
 * @param T
 * @property mList List<T>? 对应的数据列表
 * @property layoutId Int? RecylerView子item的布局id
 * @property brId Int? RecylerView子item中声明的model的名称(这个不会有代码提示, 直接写)
 * @property itemClick ItemClick? 项目item点击事件, 如果要对item内部的按钮设定时间, 重载forInnerControl即可
 * @constructor
 */
open class QJBaseAdapter<T> :
    Adapter<QJBaseAdapter.QJBaseViewHolder> {
    var mList: List<T>?
    var layoutId: Int?
    var brId: Int?

    constructor(context: BaseActivity, mList: List<T>?, layoutId: Int?, brId: Int?) : super() {
        this.mList = mList
        this.layoutId = layoutId
        this.brId = brId
        this.ctx = context
        this.layoutInflater = context.layoutInflater
        this.state = STATE_NORMAL
    }

    constructor(context: BaseFragment, mList: List<T>?, layoutId: Int?, brId: Int?) : super() {
        this.mList = mList
        this.layoutId = layoutId
        this.brId = brId
        this.ctx = context.context
        this.layoutInflater = context.layoutInflater
        this.state = STATE_NORMAL
    }
    var ctx:Context?=null
    open val layoutInflater:LayoutInflater?

    var itemClick: ItemClick? = null
    /**
     * 更新数据
     * @param items ArrayList<T>?
     */
    fun updateData(items: ArrayList<T>?) {
        this.mList = items
        if (LoadingView==null || EmptyView == null || ErrorView==null) {
            notifyDataSetChanged()
        } else {
            endLoading()
        }
    }

    fun startLoading() {
        if (LoadingView==null || EmptyView == null || ErrorView==null) {
            return
        }
        this.state = STATE_LOADING
        notifyDataSetChanged()
    }

    fun endLoading() {
        if (mList.orEmpty().size<=0) {
            this.state = STATE_EMPTY
        } else {
            this.state = STATE_NORMAL
        }
        if (state != STATE_NORMAL) {
            notifyDataSetChanged()
        } else {
            mListView.post({
                notifyDataSetChanged()
            })
        }

    }

    fun showError() {
        this.state = STATE_ERROR
        notifyDataSetChanged()
    }

    interface ItemClick {
        fun OnItemClick(v: View, position: Int)
    }

    fun setItemClickListener(itemClick: ItemClick) {
        this.itemClick = itemClick
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QJBaseViewHolder {
        if (LoadingView!=null && EmptyView != null && ErrorView!=null) {
            when (viewType) {
                TYPE_LOADING -> {
                    return QJBaseViewHolder(LoadingView!!)
                }
                TYPE_EMPTY -> {
                    return QJBaseViewHolder(EmptyView!!)
                }
                TYPE_ERROR -> {
                    return QJBaseViewHolder(ErrorView!!)
                }
            }
        }

        val binding: ViewDataBinding = DataBindingUtil.inflate(
            LayoutInflater.from(parent.context), this.layoutId!!, parent, false
        )
        return QJBaseViewHolder(binding)
    }

    override fun getItemCount(): Int {
        when (state) {
            STATE_LOADING, STATE_EMPTY, STATE_ERROR -> return 1
        }
        return mList!!.size
    }


    override fun onBindViewHolder(holder: QJBaseViewHolder, position: Int) {
        if (state in arrayOf(STATE_LOADING,STATE_EMPTY,STATE_ERROR)) {return}

        var item = mList!![position] // 这里必须为var 因为需要修改列表的数据
        doBeforeShow(holder.binding.root, item)
        holder.binding.setVariable(this.brId!!, mList!![position])
        holder.binding.executePendingBindings()
        // item点击事件
        holder.binding.root.setOnClickListener {
            itemClick?.OnItemClick(holder.binding.root, position)
        }
        forInnerControl(holder.binding.root, item,position)
        forInnerControl(holder.binding.root, item)
    }




    /**
     * 在绑定数据之前执行, 可以修改数据 item 的值
     * @param itemView View?
     * @param item T
     */
    open fun doBeforeShow(itemView: View?, item: T) {

    }

    /**
     * 用于给ItemView内部的组件绑定事件
     * @param itemView View?
     * @param item T
     */
    open fun forInnerControl(itemView: View?, item: T,position: Int = 0) {

    }

    open fun forInnerControl(itemView: View?, item: T) {

    }


    class QJBaseViewHolder : ViewHolder {
        lateinit var binding: ViewDataBinding
        lateinit var view:View

        constructor(binding: ViewDataBinding) : super(binding.root) {
            this.binding = binding
        }

        constructor(view:View):super(view) {
            this.view = view
        }
    }


    var LoadingView: View? = null
        set(value) {
            field = value
        }
    var EmptyView: View? = null
        set(value) {
            field = value
        }
    var ErrorView: View? = null
        set(value) {
            field = value
        }

    @State
    private var state: Int = STATE_NORMAL

    companion object {
        const val STATE_NORMAL = 0
        const val STATE_LOADING = 1
        const val STATE_EMPTY = 2
        const val STATE_ERROR = 3
        const val TYPE_LOADING = 1000
        const val TYPE_EMPTY = 1001
        const val TYPE_ERROR = 1002

        @IntDef(STATE_NORMAL, STATE_LOADING, STATE_EMPTY, STATE_ERROR)
        @Retention(AnnotationRetention.SOURCE)
        annotation class State
    }

    @State
    open fun getState(): Int {
        return this.state
    }

    open fun setState(@State state: Int) {
        this.state = state
        notifyDataSetChanged()
    }

    override fun getItemViewType(position: Int): Int {
        return when (state) {
            STATE_LOADING -> TYPE_LOADING
            STATE_EMPTY -> TYPE_EMPTY
            STATE_ERROR -> TYPE_ERROR
            else -> super.getItemViewType(position)
        }
    }

    lateinit var mListView:RecyclerView

    fun setView(listView:RecyclerView) {
        LoadingView = layoutInflater!!.inflate(R.layout.rv_loading,listView,false)
        ErrorView = layoutInflater!!.inflate(R.layout.error,listView,false)
        EmptyView = layoutInflater!!.inflate(R.layout.empty,listView,false)
        listView.adapter = this
        mListView = listView
    }
}

更新

  1. 新增 fun setView(listView:RecyclerView) 用于为 RecyclerView 设置"加载中” “加载失败” “无数据” 三种视图. 只需要 在调用数据开始的时候 mAdapter.startLoading(), 在 mAdapter.updateData(mList) 时, 将自动停止加载. 同时不用调用 listView.adapter = mAdapter
  2. 增加另外一个 forInnerControl的版本多一个 position的参数, 用于简单实现"动态的单选RadioButton"功能, 两个版本重载一个即可
  3. 增加一个构造函数, 传入两种类型的context
  4. 不断更新….