1. 前语

由于最近没有什么比较好的技能想要共享,所以来水一下文章,啊不对,是共享一下一些思路,和大家交流一下想法,没准能产生一些新的想法。这篇文章不具备威望,不一定正确,简略来说全都是我瞎吹。

开发这么久,多多少少也有了解过适配器形式。我仍是自始自终的建议学习规划形式,不能光靠看别人的文章,得靠自己的堆集和了解源码的做法来得更切实际。

2. 浅谈适配器

咱们都知道,适配器形式是一种结构型形式,结构型形式我的了解都有一个特点,简略来说便是封装了一些操作,便是躲藏细节嘛,比方说署理便是躲藏了经过署理调用实体的细节。

我仍是觉得咱们要重头去考虑它是怎样的一个思路。假如你去查“适配器形式”,相信你看到许多关于插头的说法,并且你会觉得这东西很简略,但如同看完之后又感觉学了个孤寂。

仍是得从源码中去了解。先看看源码中最经典的适配器形式的运用当地,没错,RecyclerView的Adapter。可是它又不仅仅只运用Adapter形式,假如拿它的源码来做剖析反而会有些绕。可是咱们能够进行笼统。想想Adapter其实首要的流程便是输入数据然后输出View给RecyclerView

然后又能够斗胆的去考虑一下,假如不界说一个Adapter,没有ViewHolder,要怎么完成这个作用?写一个循环,然后在循环里边做一些逻辑判别,然后创立对应的子View,添加到父View中 ,大概会这样做吧。那其实Adapter就帮咱们做了这一步,并且还做了许多比较经典的优化操作,其实大概便是这样。

然后从这样的模型中,我大概是能看出了一些东西,比方运用Adapter是为了输入一些东西,也能够说是为了让它封装一些逻辑,然后到达某些意图,假如用笼统的眼光去看,我不care你封装的什么东西,我只要你到达我的意图(输出我的东西),RecyclerView的adapter便是输出View。这是其一,另外,在这个模型中我有一个运用者去运用这个Adaper,这个Apdater在这儿不是一个概念,而是一个目标,我这个运用者经过Adaper这个目标给它一些输入,以此来完成我的某些方针

ok,结合Adapter形式的结构图看看(随意从网上找一张图)

天马行空使用适配器模式

能够看到这个模型中有个Client,有个Tagrget,有个Adapter。拿RecyclerView的Adapter来说,Client是RecyclerView (当然具体的addView操作是在LayoutManager中,这儿是笼统去看),Adapter便是RecyclerView.Adapter,而Tagrget笼统去看便是对view的操作。

3. 天马行空的运用

首要一般运用官方的RecyclerView啊这些,都会提供自己的Adapter,可是会让人会简单觉得Adapter便是在复用View的情况下运用。而我的了解是,RecyclerView的复用是ViewHolder的思维,不是Adapter的思维,比方前期的ListView也有Adapter啊,其时出RecyclerView之前也没有说用到ViewHolder的这种做法(这是许多年前的事),我说这个是想要表达不要把Adapter和RecyclerView绑一起,这样会让思维受到局限。

对我而言,Adapter在RecyclerView中的作用是 “Data To View” ,适配数据而产出对应的View。

那我能够把Apdater的作用了解成“Object To Object” ,关于我来说,Object它能够是Data,能够是View,甚至能够是业务逻辑。

所以当我跳出RecyclerView这种传统的 “Data To View” 的思维形式之后,Adapter适配器形式能够做到的场景就许多,正如上面我了解的,Adapter简略来说便是 “Data To View” ,那我能够用适配器形式去做 “Data To Data” ,能够去做 “View To Data” ,能够去做 “Business To Business” , “Business To View” 等等,完成多种作用。

假定我这儿做个Data To Data的场景 (强行举的例子可能不是很好)

我请求后台拿到个人的基本信息数据

data class PeopleInfo(
    var name: String?,
    var sex: Int?,
    ......
)

然后经过个人的ID,再请求服务端另外一个接口拿到成果数据

data class ScoreInfo(
    var language : Int,
    var math : Int,
    ......
)

然后我有个数据比赛的报名表目标。

data class MathCompetition(
    var math : Int,
    var name : String
)

然后一般咱们运用到的时分就会这样赋值,假定这段代码在一个Competition类中进行

val people = getPeopleInfo()
val score = getScoreInfo()
val mathTable = MathCompetition(
    score.math?,
    people.name?,
    ......
)

便是深复制的一种赋值,我相信许多人的代码里边必定会有

两个目标 AB
A.1 = B.1
A.2 = B.2
A.3 = B.3
......

这样的代码,然后目标的了解多的话这个代码就会写得很长。当然这不会形成什么大问题,可是你看着看着,总觉得短少一些美感,可是如同这玩意没封装不起来这种感觉。

假如用适配器来做的话,首要清晰要做的流程是从Competition这个类中,将所有数据整组成MathCompetition目标,方针便是输出MathCompetition目标。那从适配器形式的模型上去看,client便是Competition,是它的需求,Taget便是getMathCompetition,输出mathTable,然后咱们能够写一个Adapter。

class McAdapter {
    var mathCompetition : MathCompetition? = null
    init {
        // 给默认值
        mathCompetition = MathCompetition(0, "name", ......)
    }
    fun setData(people : PeopleInfo? = null, score : ScoreInfo? = null){
        people?.let {
            mathCompetition?.name = it.name
            ......
        }
        score?.let {
            mathCompetition?.math = it.math
            ......
        }
    }
    fun getData() : MathCompetition?{
        return mathCompetition
    }
}

然后在Competition中就不需要直接引用MathCompetition,而是设置个setAdapter办法,然后需要拿数据时再调用adapter的getData()办法,这样就适可而止,不会把这些深复制办法的赋值代码搞得到处都是。 这个Demo看着如同没什么,可是真碰到了N合1这样的数据场景的时分,运用Adapter显然会更安全。

我再简略举一个Business To View的例子吧。假定你的app中有很几套EmptyView,比方你有嵌入到页面的EmptyView,也有做弹窗类型的EmptyView,咱们一般的做法便是对应的页面的xml文件中直接写EmptyView,那这些EmptyView的代码就会很分散是吧。OK,你也想整合起来,所以你会写个EmptyHelper,大概是这个意思,用单例写一个办理EmptyView的类,然后里边统一封装对EmptyView的操作,一般都会这样写。 其实假如你让我来做,我可能就会用适配器形式去完成。 ,当然也有其他办法能很好的办理,这具体的得看心情。

写一个Adapter,我这儿写一段伪代码,应该比较简单能看懂

class EmptyAdapter() {
    // 这样语法要伴生,懒得写了,看得懂就行
    const val STATUS_NORMAL = 0
    const val STATUS_LOADING = 1
    const val STATUS_ERROR = 2
    private var type: Int = 0
    var parentView: ViewGroup? = null
    private var mEmptyView: BaseEmptyView? = null
    private var emptyStatus = 0 // 有个状况,0是不显示,1是显示加载中,2是加载失利......
    init {
        createEmptyView()
    }
    private fun createEmptyView() {
        // 也能够判别是否有parentView决议运用哪个EmptyView等逻辑
        mEmptyView = when (type) {
            0 -> AEmptyView
            1 -> BEmptyView
            2 -> CEmptyView
            else -> {
                AEmptyView
            }
        }
    }
    fun setData(status: Int) {
        when (status) {
            0 -> parentView?.removeView(mEmptyView)
            1 -> mEmptyView?.showLoading()
            2 -> mEmptyView?.showError()
        }
    }
    fun updateType(type: Int) {
        setData(0)
        this.type = type
        createEmptyView()
    }
}

然后在具体的Activity调用的时分,能够

val emptyAdapter = EmptyAdapter(getContentView())
// 然后在每次要loading的时分去设置adapter的的状况
emptyAdapter.setData(EmptyAdapter.STATUS_LOADING)
emptyAdapter.setData(EmptyAdapter.STATUS_NORMAL)
emptyAdapter.setData(EmptyAdapter.STATUS_ERROR)

能够看出这样做就有几个好处,其一便是不必每个xml都写EmptyView,然后也能做到统一的办理,和一些人写的Helper这种的作用相似,最终调用的办法也很简略,你只需要创立一个Adaper,然后调用它的setData就行,咱们的RecyclerView也是这样的,在外层去创立然后调用Adapter就行。

4. 总结

写这篇文章首要是为了水,啊不对,是为了想阐明几个问题:
(1)开发时要善于跳出一些约束去考虑,比方RecyclerView你可能觉得适配器形式就和它绑定了,就和View绑定了,有View的当地才干运用适配器形式,至少我觉得不是这样。
(2)学习规划形式,只去看一些介绍是很难了解的,当然要知道它的一个大致的思维,然后要灵活运用到开发中,这样学它才有用。
(3)我对适配器形式的了解便是 Object To Object,我能够去写ViewAdapter,能够去写DataAdapter,也能够去写BusinessAadpter,能够用这个形式去适配不同的场景,运用这个思维来使代码更加合理。

当然最终仍是要强调一下,我不敢保证我说的便是对的,我必定不是威望的。但至少我运用这招之后的的新代码作用要比一些旧代码更简单保护,更简单扩展。