背景

置信大家都曾经在应用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 例子

// viewclass 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、ModelbindingViewModelDsl(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 能够传入一个新的Adapteropen 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