Simple Android RecyclerView example

前端 未结 11 1414
名媛妹妹
名媛妹妹 2020-11-22 02:03

I\'ve made a list of items a few times using Android\'s RecyclerView, but it is a rather complicated process. Going through one of the numerous tutorials online

11条回答
  •  旧巷少年郎
    2020-11-22 02:50

    You can use abstract adapter with diff utils and filter

    SimpleAbstractAdapter.kt

    abstract class SimpleAbstractAdapter(private var items: ArrayList = arrayListOf()) : RecyclerView.Adapter() {
       protected var listener: OnViewHolderListener? = null
       private val filter = ArrayFilter()
       private val lock = Any()
       protected abstract fun getLayout(): Int
       protected abstract fun bindView(item: T, viewHolder: VH)
       protected abstract fun getDiffCallback(): DiffCallback?
       private var onFilterObjectCallback: OnFilterObjectCallback? = null
       private var constraint: CharSequence? = ""
    
    override fun onBindViewHolder(vh: VH, position: Int) {
        getItem(position)?.let { bindView(it, vh) }
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
        return VH(parent, getLayout())
    }
    
    override fun getItemCount(): Int = items.size
    
    protected abstract class DiffCallback : DiffUtil.Callback() {
        private val mOldItems = ArrayList()
        private val mNewItems = ArrayList()
    
        fun setItems(oldItems: List, newItems: List) {
            mOldItems.clear()
            mOldItems.addAll(oldItems)
            mNewItems.clear()
            mNewItems.addAll(newItems)
        }
    
        override fun getOldListSize(): Int {
            return mOldItems.size
        }
    
        override fun getNewListSize(): Int {
            return mNewItems.size
        }
    
        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return areItemsTheSame(
                    mOldItems[oldItemPosition],
                    mNewItems[newItemPosition]
            )
        }
    
        abstract fun areItemsTheSame(oldItem: T, newItem: T): Boolean
    
        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return areContentsTheSame(
                    mOldItems[oldItemPosition],
                    mNewItems[newItemPosition]
            )
        }
    
        abstract fun areContentsTheSame(oldItem: T, newItem: T): Boolean
    }
    
    class VH(parent: ViewGroup, @LayoutRes layout: Int) : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(layout, parent, false))
    
    interface OnViewHolderListener {
        fun onItemClick(position: Int, item: T)
    }
    
    fun getItem(position: Int): T? {
        return items.getOrNull(position)
    }
    
    fun getItems(): ArrayList {
        return items
    }
    
    fun setViewHolderListener(listener: OnViewHolderListener) {
        this.listener = listener
    }
    
    fun addAll(list: List) {
        val diffCallback = getDiffCallback()
        when {
            diffCallback != null && !items.isEmpty() -> {
                diffCallback.setItems(items, list)
                val diffResult = DiffUtil.calculateDiff(diffCallback)
                items.clear()
                items.addAll(list)
                diffResult.dispatchUpdatesTo(this)
            }
            diffCallback == null && !items.isEmpty() -> {
                items.clear()
                items.addAll(list)
                notifyDataSetChanged()
            }
            else -> {
                items.addAll(list)
                notifyDataSetChanged()
            }
        }
    }
    
    fun add(item: T) {
        items.add(item)
        notifyDataSetChanged()
    }
    
    fun add(position:Int, item: T) {
        items.add(position,item)
        notifyItemInserted(position)
    }
    
    fun remove(position: Int) {
        items.removeAt(position)
        notifyItemRemoved(position)
    }
    
    fun remove(item: T) {
        items.remove(item)
        notifyDataSetChanged()
    }
    
    fun clear(notify: Boolean=false) {
        items.clear()
        if (notify) {
            notifyDataSetChanged()
        }
    }
    
    fun setFilter(filter: SimpleAdapterFilter): ArrayFilter {
        return this.filter.setFilter(filter)
    }
    
    interface SimpleAdapterFilter {
        fun onFilterItem(contains: CharSequence, item: T): Boolean
    }
    
    fun convertResultToString(resultValue: Any): CharSequence {
        return filter.convertResultToString(resultValue)
    }
    
    fun filter(constraint: CharSequence) {
        this.constraint = constraint
        filter.filter(constraint)
    }
    
    fun filter(constraint: CharSequence, listener: Filter.FilterListener) {
        this.constraint = constraint
        filter.filter(constraint, listener)
    }
    
    fun getFilter(): Filter {
        return filter
    }
    
    interface OnFilterObjectCallback {
        fun handle(countFilterObject: Int)
    }
    
    fun setOnFilterObjectCallback(objectCallback: OnFilterObjectCallback) {
        onFilterObjectCallback = objectCallback
    }
    
    inner class ArrayFilter : Filter() {
        private var original: ArrayList = arrayListOf()
        private var filter: SimpleAdapterFilter = DefaultFilter()
        private var list: ArrayList = arrayListOf()
        private var values: ArrayList = arrayListOf()
    
    
        fun setFilter(filter: SimpleAdapterFilter): ArrayFilter {
            original = items
            this.filter = filter
            return this
        }
    
        override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
            val results = Filter.FilterResults()
            if (constraint == null || constraint.isBlank()) {
                synchronized(lock) {
                    list = original
                }
                results.values = list
                results.count = list.size
            } else {
                synchronized(lock) {
                    values = original
                }
                val result = ArrayList()
                for (value in values) {
                    if (constraint!=null && constraint.trim().isNotEmpty() && value != null) {
                        if (filter.onFilterItem(constraint, value)) {
                            result.add(value)
                        }
                    } else {
                        value?.let { result.add(it) }
                    }
                }
                results.values = result
                results.count = result.size
            }
            return results
        }
    
        override fun publishResults(constraint: CharSequence, results: Filter.FilterResults) {
            items = results.values as? ArrayList ?: arrayListOf()
            notifyDataSetChanged()
            onFilterObjectCallback?.handle(results.count)
        }
    
    }
    
    class DefaultFilter : SimpleAdapterFilter {
        override fun onFilterItem(contains: CharSequence, item: T): Boolean {
            val valueText = item.toString().toLowerCase()
            if (valueText.startsWith(contains.toString())) {
                return true
            } else {
                val words = valueText.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                for (word in words) {
                    if (word.contains(contains)) {
                        return true
                    }
                }
            }
            return false
        }
      }
    }
    

    And extend abstract adapter with implements methods

    TasksAdapter.kt

    import android.annotation.SuppressLint
      import kotlinx.android.synthetic.main.task_item_layout.view.*
    
    class TasksAdapter(private val listener:TasksListener? = null) : SimpleAbstractAdapter() {
    override fun getLayout(): Int {
        return R.layout.task_item_layout
    }
    
    override fun getDiffCallback(): DiffCallback? {
        return object : DiffCallback() {
            override fun areItemsTheSame(oldItem: Task, newItem: Task): Boolean {
                return oldItem.id == newItem.id
            }
    
            override fun areContentsTheSame(oldItem: Task, newItem: Task): Boolean {
                return oldItem.items == newItem.items
            }
        }
    }
    
    @SuppressLint("SetTextI18n")
    override fun bindView(item: Task, viewHolder: VH) {
        viewHolder.itemView.apply {
            val position = viewHolder.adapterPosition
            val customer = item.customer
            val customerName = if (customer != null) customer.name else ""
            tvTaskCommentTitle.text = customerName + ", #" + item.id
            tvCommentContent.text = item.taskAddress
            ivCall.setOnClickListener {
                listener?.onCallClick(position, item)
            }
            setOnClickListener {
                listener?.onItemClick(position, item)
            }
        }
    }
    
     interface TasksListener : SimpleAbstractAdapter.OnViewHolderListener {
        fun onCallClick(position: Int, item: Task)
     }
    }
    

    Init adapter

    mAdapter = TasksAdapter(object : TasksAdapter.TasksListener {
                override fun onCallClick(position: Int, item:Task) {
                }
    
                override fun onItemClick(position: Int, item:Task) {
    
                }
            })
    rvTasks.adapter = mAdapter
    

    and fill

    mAdapter?.addAll(tasks)
    

    add custom filter

    mAdapter?.setFilter(object : SimpleAbstractAdapter.SimpleAdapterFilter {
                override fun onFilterItem(contains: CharSequence, item:Task): Boolean {
                    return contains.toString().toLowerCase().contains(item.id?.toLowerCase().toString())
                }
        })
    

    filter data

    mAdapter?.filter("test")
    

提交回复
热议问题