乐趣区

关于前端:后现代化RecyclerView-Adapter稳定版本终于来了

背景

置信大家都曾经在应用 kotlin 了,可咱们应用最频繁的 Adapter 缺很少有人用 kotlin 做扩大,即便有,但给我的感觉还是不够,第一不够简洁,第二性能耦合在一起,第三不够欠缺,于是我决定本人做一个,通过这段时间的钻研,后面也写了三篇博客了,都是我这段时间的劳动成果,可之前的设计还是会有一些不好的中央,也是通过几次的验证后,目前有了稳定版,对于这个版本我还是比较满意的,上面有请我厚脸皮给你们讲一讲

源码地址

https://github.com/ibaozi-cn/…

Gradle 依赖

allprojects {
    repositories {
        // 首先我的项目根目录的 build.gradle 文件中退出这一行 
        maven {url 'https://jitpack.io'}
    }
}

def adapterVersion = 'v1.2.0'

// 外围库
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-core:$adapterVersion"

// 上面都是可选项

//anko layout 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-anko:$adapterVersion"
//diffutil 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-diff:$adapterVersion"
//data binding 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-binding:$adapterVersion"
// paging3 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-paging:$adapterVersion"
// sortedlist 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-sorted:$adapterVersion"
// flexbox 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-flex:$adapterVersion"
// UI 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-ui:$adapterVersion"
// Selectable 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-selectable:$adapterVersion"
// Expandable 扩大
implementation "com.github.ibaozi-cn.RecyclerViewAdapter:adapter-expandable:$adapterVersion"

以后版本库大小

| 名字 | release aar size | 其余 |
| —- | —- | —- |
| Core | 28kb | 外围库目前蕴含 ListAdapter 的实现,最根底且最实用的扩大 |
| Anko | 13kb | 实用本我的项目所有 Adapter 扩大 |
| DataBinding | 20kb | 适配 DataBinding 布局,实用本我的项目所有 Adapter 扩大 |
| Sorted | 10kb | SortedListAdapter 扩大实现 |
| Paging | 13kb | PagingListAdapter 扩大适配 |
| Diff | 6kb | 适配 DiffUtil,目前实用 ListAdapter |
| FlexBox | 9kb | 适配 FlexBox 布局 |
| Selectable | 8kb | 动静扩大单选、多选、最大可选项性能 |
| Expandable | 8kb | 动静扩大可开展性能,反对仅单开展或多开展配置 |
| UI | 17kb | 扩大空布局 |

对 Adapter 扩大类图

下面的内容我大抵形容一下

  • IAdapter 最底层的形象接口
  • ViewHolderCacheAdapter 对立解决 ViewHolder 的缓存和 ViewModel 的回调
  • ListAdapter 扩大 ViewHolderCacheAdapter,实现对 ArrayList 数据结构的解决
  • SortedListAdapter 扩大 ViewHolderCacheAdapter,实现对 SortedList 数据结构的解决
  • PagingListAdapter 扩大 ViewHolderCacheAdapter,实现对 AsyncPagingDataDiffer 的解决
  • IAdapter Expandable 动静扩大 这里用的 Kotlin 的扩大函数实现,相似组合继承,解耦不便
  • IAdapter Selectable 动静扩大 同上
  • ViewModel 这个是对 Adapter 每一个 Item 的高度形象,负责配置 Model 数据,负责获取 ViewHolder 的实例,缓存 itemViewType 等
  • DefaultViewModel 负责创立 DefaultViewHolder,并提供 ViewHolder 初始化 InitView 的回调,这个很要害,这是咱们能够间接在 ViewModelDSl 中 findView 的要害
  • LayoutViewModel 扩大自 DefaultViewModel,实现 LayoutInflater 加载 View
  • AnkoViewModel 扩大自 DefaultViewModel,实现 AnkoComponent 加载 View
  • BindingViewModel 扩大自 DefaultViewModel,实现 DataBindingUtil 加载 View 和 ViewDataBinding 绑定数据
  • WrapAdapter 专门负责装璜老的 Adapter 适配器,一种很好的设计模式,比起继承实现要好太多了,EmptyAdapter 就是一个很好的实现,只须要将以前的 Adapter 包裹一层 EmptyAdapter,就能够轻松实现空布局,这里我想啰嗦一句,如果是头尾布局,不倡议这么做,咱们 RecyclerView 降级 1.2.0 当前就会有一个 ConcatAdapter,这个适配器才是咱们实现头尾布局的神器,期待 ing

这么好的框架如何应用呢?

源码中提供全面的例子,Demo 目录如图哦

一些效果图

下载源码,跑一下 Demo 就行了哦,上面教你如何疾速的上手

疾速上手

LayoutViewModel 例子

//xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/cardItem"
    android:layout_margin="5dp">

    <LinearLayout
        android:background="?attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="25dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_title"
            android:text="@string/app_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@color/colorPrimary"
            android:textSize="22sp" />

        <TextView
            android:id="@+id/tv_subTitle"
            android:text="@string/test"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@color/colorAccent"
            android:layout_marginStart="10dp"
            android:textSize="18sp" />

    </LinearLayout>


</androidx.cardview.widget.CardView>
// 传入布局 和 Model 数据
layoutViewModelDsl(R.layout.item_test, ModelTest("title", "subTitle")) {
        // 初始化 View,这里只会调用一次哦,释怀初始化,释怀的 setOnClickListener
        val titleText = getView<TextView>(R.id.tv_title)
        val subTitleText = getView<TextView>(R.id.tv_subTitle)
        itemView.setOnClickListener {val vm = getViewModel<LayoutViewModel<ModelTest>>()
            // 批改 Model 数据
            vm?.model?.title = "测试更新 ${Random.nextInt(10000)}"
            // 用 Adapter 更新数据
            getAdapter<ListAdapter>()?.set(adapterPosition, vm)
        }
        // 数据触发更新的时候,绑定新的 Model 数据
        onBindViewHolder {val model = getModel<ModelTest>()
            titleText.text = model?.title
            subTitleText.text = model?.subTitle
        }
    }

AnkoViewModel 例子

// view
class AnkoItemView : AnkoComponent<ViewGroup> {

    var tvTitle: TextView? = null
    var tvSubTitle: TextView? = null
    var view: View? = null

    @SuppressLint("ResourceType")
    override fun createView(ui: AnkoContext<ViewGroup>) = with(ui) {

        cardView {

            layoutParams = FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT
            ).apply {margin = dip(5)
            }

            verticalLayout {val typedValue = TypedValue()
                context.theme
                    .resolveAttribute(android.R.attr.selectableItemBackground, typedValue, true)
                val attribute = intArrayOf(android.R.attr.selectableItemBackground)
                val typedArray =
                    context.theme.obtainStyledAttributes(typedValue.resourceId, attribute)

                background = typedArray.getDrawable(0)

                layoutParams = FrameLayout.LayoutParams(
                    FrameLayout.LayoutParams.MATCH_PARENT,
                    FrameLayout.LayoutParams.WRAP_CONTENT
                ).apply {padding = dip(10)
                }

                tvTitle = textView {textSize = px2dip(60)
                    textColorResource = R.color.colorPrimary
                }.lparams(matchParent, wrapContent)

                tvSubTitle = textView {textSize = px2dip(45)
                    textColorResource = R.color.colorAccent
                }.lparams(matchParent, wrapContent)

            }
        }

    }
}

// 传入 Model 和 AnkoView 对象
ankoViewModelDsl(ModelTest("title", "ankoViewModelDsl"), {AnkoItemView() }) {
        // 数据更新
        onBindViewHolder { _ ->
            val model = getModel<ModelTest>()
            val ankoView = getAnkoView<AnkoItemView>()
            ankoView?.tvTitle?.text = model?.title
            ankoView?.tvSubTitle?.text = model?.subTitle
        }
        // 点击事件处理
        itemView.setOnClickListener {val viewModel = getViewModel<AnkoViewModel<ModelTest, AnkoItemView>>()
            viewModel?.model?.title = "点击更新 ${Random.nextInt(10000)}"
            getAdapter<ListAdapter>()?.set(adapterPosition, viewModel)
        }
    }

与 LayoutViewModel 的不同就在于无需在 DSL 中初始化 View,因为曾经在 AnkoView 中做了缓存,它惟一的劣势就是比 LayoutViewModel 更快的加载速度,但 Anko Layout 曾经不保护了,你是不是不敢用了呢?在我看来,问题不大,因为我能够自定义 AnkoView,本人来做扩大,性能的晋升远大于代码的数量,你说呢?

BindingViewModel 例子

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    >

    <data>
        <variable
            name="model"
            type="com.julive.adapter_demo.sorted.ModelTest" />
    </data>

    <androidx.cardview.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/cardItem"
        android:layout_margin="5dp">

        <LinearLayout
            android:background="?attr/selectableItemBackground"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="25dp"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_title"
                android:text="@{model.title}"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/colorPrimary"
                android:textSize="22sp" />

            <TextView
                android:id="@+id/tv_subTitle"
                android:text="@{model.subTitle}"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/colorAccent"
                android:layout_marginStart="10dp"
                android:textSize="18sp" />

        </LinearLayout>

    </androidx.cardview.widget.CardView>

</layout>
// 传入 layout、BR、Model
bindingViewModelDsl(R.layout.item_binding_layout, BR.model, ModelTest("title", "bindingViewModelDsl")) {
        // 设置点击事件
        itemView.setOnClickListener {val viewModel = getViewModel<BindingViewModel<ModelTest>>()
            viewModel?.model?.title = "${java.util.Random().nextInt(100)}"
            getAdapter<ListAdapter>()?.set(adapterPosition, viewModel)
        }
    }

没有 findView,没有 onBindViewHolder,代码缩减了很多,如果你谋求的就是高效率,请应用它,准没错,三种加载 ItemView 的形式就完了

如何加载到 Adapter 中呢

listAdapter {addAll(createViewModelList(3))
       addAll(createAnkoViewModelList(3))
       addAll(createBindingViewModelList(3))
       // 绑定 RecyclerView
       into(rv_list_dsl)
}
        
fun createViewModelList(max: Int = 10) = (0..max).map { _ ->
    layoutViewModelDsl(R.layout.item_test, ModelTest("title", "subTitle")) {val titleText = getView<TextView>(R.id.tv_title)
        val subTitleText = getView<TextView>(R.id.tv_subTitle)
        itemView.setOnClickListener {val vm = getViewModel<LayoutViewModel<ModelTest>>()
            // 批改 Model 数据
            vm?.model?.title = "测试更新 ${Random.nextInt(10000)}"
            // 用 Adapter 更新数据
            getAdapter<ListAdapter>()?.set(adapterPosition, vm)
        }
        onBindViewHolder {val model = getModel<ModelTest>()
            titleText.text = model?.title
            subTitleText.text = model?.subTitle
        }
    }
}
省略 Anko、Binding...

如何实现一个 Selectable 呢

class SelectableActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
        supportActionBar?.title = "ListAdapter"
        setContentView(R.layout.activity_selectable)
        // 同样是 ListAdapter
        val adapter = listAdapter {
            // 增加一堆 ViewModel 数据
            (0..10).forEach { _ ->
                add(
                    layoutViewModelDsl(
                        R.layout.item_test,
                        ModelTest("title", "subTitle")
                    ) {
                        // 初始化 View
                        val title = getView<TextView>(R.id.tv_title)
                        val subTitle = getView<TextView>(R.id.tv_subTitle)
                        // 设置监听
                        itemView.setOnClickListener {
                            // 扭转抉择状态
                            toggleSelection(adapterPosition) {if (it) {longToast("可选项已达到最大值")
                                }
                            }
                            Log.d("isMultiSelectable", "isMultiSelectable$isMultiSelect")
                        }
                        onBindViewHolder {val model = getModel<ModelTest>()
                            title.text = model?.title
                            subTitle.text = model?.subTitle
                            // 获取抉择状态,来适配不同 UI
                            val isSelect = isSelected(adapterPosition)
                            if (isSelect) {itemView.setBackgroundResource(R.color.cardview_dark_background)
                                title.textColorResource = R.color.cardview_light_background
                            } else {itemView.setBackgroundResource(R.color.cardview_light_background)
                                title.textColorResource = R.color.cardview_dark_background
                            }
                        }
                    })
            }
            into(rv_list_selectable)
        }
        btn_left.setText("切换单选").setOnClickListener {
            // 多选和单选之间切换
            if (!adapter.isMultiSelect) {btn_left.setText("切换单选")
            } else {btn_left.setText("切换多选")
            }
            adapter.setMultiSelectable(!adapter.isMultiSelect)
        }
        btn_middle.isVisible = false
        btn_right.setText("设置最大可选").setOnClickListener {
            // 配置最大的可选择项
            val random = Random().nextInt(6)
            btn_right.setText("设置最大可选 $random")
            adapter.setSelectableMaxSize(random)
        }
    }
}

有没有前所未有简略呢?这就是 Kotlin 动静扩大函数带来的便当,上面请看下实现的源码:

// 依据列表实例缓存已选择项,只缓存选中的,未选中的会被清理掉节俭内存,用弱援用来进步内存回收率
private val selectedItemsCache = SparseArray<WeakReference<SparseBooleanArray?>?>()
private val selectConfigCache = SparseArray<WeakReference<SparseArray<Any>?>?>()
// 可选项默认配置
private val defaultSelectedConfig by lazy {SparseArray<Any>().apply {append(0, true) // is Multi Selectable
        append(1, Int.MAX_VALUE) // Selectable Max Size Default Int.Max
    }
}
// 获取已抉择列表
private fun getSelectedItems(key: Int): SparseBooleanArray {val wr = selectedItemsCache[key]
    val sba by lazy {SparseBooleanArray()
    }
    return if (wr == null) {selectedItemsCache.append(key, WeakReference(sba))
        sba
    } else {val expandedItems = wr.get()
        if (expandedItems == null) {selectedItemsCache.append(key, WeakReference(sba))
        }
        expandedItems ?: sba
    }
}
// 获取选择项配置信息
private fun getSelectConfig(key: Int): SparseArray<Any> {val wr = selectConfigCache[key]
    return if (wr == null) {selectConfigCache.append(key, WeakReference(defaultSelectedConfig))
        defaultSelectedConfig
    } else {val expandConfig = wr.get()
        if (expandConfig == null) {selectConfigCache.append(key, WeakReference(defaultSelectedConfig))
        }
        expandConfig ?: defaultSelectedConfig
    }
}
// 动静扩大 IAdapter 判断以后是否多选
var IAdapter<*>.isMultiSelect
    get() = getSelectConfig(hashCode())[0] as Boolean
    private set(value) {}
// 动静扩大 IAdapter 获取最大可抉择数
var IAdapter<*>.selectedMaxSize: Int
    get() = getSelectConfig(hashCode())[1] as Int
    private set(value) {}
// 动静扩大 IAdapter 获取已选择项的大小
var IAdapter<*>.selectedCount: Int
    get() = getSelectedItems(hashCode()).size()
    private set(value) {}
// 动静扩大 IAdapter 配置多选和单选的状态
fun IAdapter<*>.setMultiSelectable(enable: Boolean) {getSelectConfig(hashCode()).setValueAt(0, enable)
    if (!enable && selectedCount > 1) {clearSelection()
    }
}
// 动静扩大 IAdapter 配置最大可抉择数
fun IAdapter<*>.setSelectableMaxSize(size: Int) {getSelectConfig(hashCode()).setValueAt(1, size)
}
// 动静扩大 IAdapter 获取已抉择列表
fun IAdapter<*>.getSelectedItems(): List<Int> {val si = getSelectedItems(hashCode())
    val itemSize = si.size()
    val items: MutableList<Int> = ArrayList(itemSize)
    for (i in 0 until itemSize) {items.add(si.keyAt(i))
    }
    return items
}
// 动静扩大 IAdapter 判断以后是否已抉择
fun IAdapter<*>.isSelected(position: Int) = getSelectedItems().contains(position)
// 动静扩大 IAdapter 清空已选择项
fun IAdapter<*>.clearSelection() {val selection = getSelectedItems()
    getSelectedItems(hashCode()).clear()
    for (i in selection) {notifyItemChanged(i)
    }
}
// 动静扩大 IAdapter 扭转抉择状态
fun IAdapter<*>.toggleSelection(position: Int, isMaxSelect: ((Boolean) -> Unit)? = null) {val si = getSelectedItems(hashCode())
    val isSelect = si.get(position, false)
    if (selectedCount >= selectedMaxSize && !isSelect) {isMaxSelect?.invoke(true)
        return
    }
    isMaxSelect?.invoke(false)
    if (!isMultiSelect) {clearSelection()
    }
    if (isSelect) {si.delete(position)
    } else {si.put(position, true)
    }
    notifyItemChanged(position)
}

没有继承,没有组合,就是动静扩大,这样的解耦形式,是不是比以前更加的好用呢?我认为还能够,不晓得你怎么想,欢送留言。Expandable 实现原理同上,就不再形容了哦

WrapAdapter 的实现

为什么要有 WrapAdapter?还是以前的例子,来看下那个截图

看到没,这外面就有一个 EMPTY_VIEW,不光这些还有头尾布局,这样的逻辑你敢用吗?如果有了 WrapAdapter 是什么样子呢?

    override fun getItemViewType(position: Int): Int {return if (displayEmptyView(emptyState)) {viewModel.itemViewType} else {super.getItemViewType(position)
        }
    }

就这样就行了,简单明了,这么简洁的代码你不点个赞吗?哈哈,其实这就是装璜者模式的魅力,其实它的外围就是将实在适配器的调用权交给了 WrapAdapter,而后在适合的机会再调用实在的 Adapter 来展现数据。其实 WrapAdapter 的实现很简略,来看下一段代码

// 继承自 RecyclerView.Adapter 能够传入一个新的 Adapter
open class WrapAdapter<VH : RecyclerView.ViewHolder>(private var mWrappedAdapter: RecyclerView.Adapter<VH>) :
    RecyclerView.Adapter<VH>()
    
// 注册 observer,实现 notifyDataChange 一系列相干回调
mWrappedAdapter.registerAdapterDataObserver(wrapAdapterDataObserver)

// 一些要害函数的调用实现,这里没写全哦,具体还请跳入源码查看
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {return mWrappedAdapter.onCreateViewHolder(parent, viewType)
    }
    override fun getItemId(position: Int): Long {return mWrappedAdapter.getItemId(position)
    }

    override fun getItemViewType(position: Int): Int {return mWrappedAdapter.getItemViewType(position)
    }

    override fun onBindViewHolder(holder: VH, position: Int, payloads: List<Any>) {mWrappedAdapter.onBindViewHolder(holder, position, payloads)
    }

WrapAdapter 这么好,空布局如何用的呢?

class EmptyActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_empty)
        val emptyAdapter = EmptyAdapter(
            listAdapter {addAll(createViewModelList())
            }
        ).apply {into(rv_list_empty)
        }
        btn_left.setText("空布局").setOnClickListener {emptyAdapter.emptyState = EmptyState.NotLoading}
        btn_middle.setText("加载中").setOnClickListener {
            emptyAdapter.emptyState = EmptyState.Loading
            Handler().postDelayed({emptyAdapter.emptyState = EmptyState.Loaded},2000)
        }
        btn_right.setText("加载失败").setOnClickListener {emptyAdapter.emptyState = EmptyState.Error}
    }
}

也是超级简略是吧,很容易就了解了对吗?

如何应用 SortedListAdapter 呢?

咱们先来看一段代码

/**
 * sortedId 排序用
 * title 作为 uniqueId,RecyclerView ItemView 更新的时候,惟一值,留神列表是能够呈现一样的 uniqueId 的,* 如果想更新请调用 Adapter updateItem 这样能力保障列表中 uniqueId 惟一
 */
data class SortedModelTest(
    val sortedId: Int, var title: String, var subTitle: String,
    override var uniqueId: String = title
) : SortedModel {override fun <T : SortedModel> compare(model: T): Int {if (sortedId > (model as? SortedModelTest)?.sortedId ?: 0) return 1
        if (sortedId < (model as? SortedModelTest)?.sortedId ?: 0) return -1
        return 0
    }
}

class SortedItemViewModelTest : LayoutViewModel<SortedModelTest>(R.layout.item_test) {
    init {
        onCreateViewHolder {
            itemView.setOnClickListener {
                val vm =
                    getAdapter<SortedListAdapter>()?.getItem(adapterPosition) as SortedItemViewModelTest
                vm.model?.subTitle = "刷新本人 ${Random.nextInt(100)}"
                getAdapter<SortedListAdapter>()?.set(adapterPosition, vm)
            }
        }
    }
    override fun bindVH(viewHolder: DefaultViewHolder, payloads: List<Any>) {viewHolder.getView<TextView>(R.id.tv_title).text = model?.title
        viewHolder.getView<TextView>(R.id.tv_subTitle).text = model?.subTitle
    }
}

咱们形象的 ViewModel 是在任何 Adapter 中都能够做到通用的,这点你能够释怀哦,SorteList 咱们都晓得它是须要对数据进行比拟的,所以咱们提供了 SortedModel 接口, 你只须要实现 SortedModel 接口就能够将其放入 ViewModel 中,而后再放入 Adapter 中就行了,SortedModel 实现 SameModel,这里是接口继承,在 kotlin 外面接口是能够有实现的,

interface SameModel {
    var uniqueId: String
    // 是否是同一个 Model
    fun <T : SameModel> isSameModelAs(model: T): Boolean {return this.uniqueId == model.uniqueId}
    // 同一个 Model 的话,数据是否有变动
    fun <T : SameModel> isContentTheSameAs(model: T): Boolean {return this == model}
    // 部分刷新时应用
    fun <T : SameModel> getChangePayload(newItem: T): Any? = null
}
interface SortedModel : SameModel {
    /**
     * 排序应用
     */
    fun <T : SortedModel> compare(model: T): Int
}

因为是继承接口实现,所以侵入性不高,对于个别的业务都能够实用,你能够放心大胆的应用哦。在 Activity 中应用办法如下:

class SortedActivity : AppCompatActivity() {

    private val mSortedListAdapter by lazy {SortedListAdapter()
    }

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
        supportActionBar?.title = "SortedListAdapter"
        setContentView(R.layout.activity_array_list)
        mSortedListAdapter.into(rv_list)
        // 初始化数据
        (0..10).map {mSortedListAdapter.add(SortedItemViewModelTest().apply {model = SortedModelTest(it, "题目 $it", "副标题 $it")
            })
        }
        var index = 100
        btn_left.setText("新增").setOnClickListener {
            // 要想依据 uniqueId 更新数据,须要调用 updateItem 办法
            mSortedListAdapter.add(SortedItemViewModelTest().apply {model = SortedModelTest(index++, "题目 $index", "新增 $index")
            })
        }
        btn_middle.setText("删除").setOnClickListener {if (mSortedListAdapter.size > 0) {val randomInt = Random.nextInt(0, mSortedListAdapter.size)
                mSortedListAdapter.removeAt(randomInt)
            }
        }
        btn_right.setText("替换").setOnClickListener {
            // 依据 uniqueId 替换 如果 sortId 不一样就会触发排序
            if (mSortedListAdapter.size > 0) {val randomInt = Random.nextInt(0, mSortedListAdapter.size)
                mSortedListAdapter.set(randomInt, mSortedListAdapter.getItem(randomInt).also {
                    it as SortedItemViewModelTest
                    it.model?.subTitle = "替换副标题"
                })
            }
        }
    }
}

将来更多的布局

  • 上啦加载更多,滚动底部或头部回调、获取可见项
  • 拖动解决、滑动删除
  • 根底动画
  • Item 边距解决
  • 树的开展扩大,目前开展只是反对了一层,将来实现多层开展
  • StickyHeader 扩大,列题目实现
  • 等等吧

这么全面的 Adapter 你见过几个?还不动动小手关注一哈,嘿嘿,谢谢????

总结

我这期针对稳固版本,写的不是很多,次要就是为了让你们晓得如何应用,以及一些源码的展现,其实咱们在做开发的同时,真的会遇到各种各样的列表,当然它不能笼罩业务中的各个场景,但我心愿能在某些实现的角度上能让你收益,用更加正当的实现形式来解决业务中各种各样的简单场景。

感激

https://github.com/mikepenz/F…

https://github.com/DevAhamed/…

https://github.com/davideas/F…

https://github.com/liangjingk…

https://github.com/h6ah4i/and…

https://github.com/evant/bind…

特别感谢这些优良设计者的我的项目,是他们的教训积攒,让我有了更多的想法和实现。

开发者

i 校长

  • 简书 https://www.jianshu.com/u/776…
  • 掘金 https://juejin.im/user/131597…
  • 集体网站 http://jetpack.net.cn、http://ibaozi.cn
退出移动版