Android 点击回调传递

概述

在使用MultiTypeAdapter实现RecyclerView多类型显示的时候,会创建一个ViewHolder和ViewBinder,此时如果要在Activity或者Fragment相应点击事件的时候,需要在ViewHolder和ViewBinder之间做传递。如果一个ViewHolder下有RecyclerView,然后也使用了MultiTypeAdapter,那么这个点击事件的回调将会是一件相当头疼的事情。

启发

在使用LifeCycle时,发现他只需要当前类实现LifecycleObserver,然后通过调用addObserver方法即可实现事件传递。由此想到点击事件是否也可以使用此种形式来实现。

DEMO

DEMO

效果图

demo (1).gif

实现

在受到LifeCycle的启发下,模仿这写了几个类。

  • OnItemClick
abstract class OnItemClick {
    abstract fun addObserver(onItemClickObserver: OnItemClickObserver)
    abstract fun addObserver(list: List<OnItemClickObserver>?)
    abstract fun getObserver(): List<OnItemClickObserver>?
    abstract fun dispatchObserver(onItemClickObserver: OnItemClickObserver)
    abstract fun dispatchObserver(list: List<OnItemClickObserver>?)
    abstract fun removeObserver(onItemClickObserver: OnItemClickObserver)
    abstract fun removeAll()
}
定义一系列的操作方法,包括添加、移除、传递等。
  • OnItemClickOwner
interface OnItemClickOwner {
    fun getOnItemClick(): OnItemClick
}

一个点击事件如果在某个类中如果需要做操作,那么需要实现该接口。

  • OnItemClickRegistry
class OnItemClickRegistry() : OnItemClick() {
    // 省略具体代码
}

继承OnItemClick,并实现具体的方法。

  • OnItemClickObserver
interface OnItemClickObserver

基本点击事件Observer

ViewBinder/ViewHolder 改造

  • BaseViewHolder
abstract class BaseViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView), OnItemClickOwner {
    var mT: T? = null
    var mContext: Context = itemView.context
    abstract fun setData(t: T)
}
  • BaseViewHolderWrapper
open class BaseViewHolderWrapper<T>(itemView: View) :
        BaseViewHolder<T>(itemView), View.OnClickListener {

    private val onItemRegistry = OnItemClickRegistry(this)

    override fun setData(@NonNull t: T) {
        mT = t
        itemView?.setOnClickListener(this)
    }

    override fun onClick(v: View) {
    }

    fun <R : OnItemClickObserver> getObserver(clazz: Class<R>): R? {
        getOnItemClick().getObserver()?.forEach {
            if (clazz.isInstance(it)) {
                @Suppress("UNCHECKED_CAST")
                return it as R
            }
        }
        return null
    }


    override fun getOnItemClick(): OnItemClick {
        return onItemRegistry
    }
}
  • BaseViewBinder
abstract class BaseViewBinder<T, R : BaseViewHolderWrapper<T>> : ItemViewBinder<T, R>(), OnItemClickOwner {

    private val onItemRegistry = OnItemClickRegistry(this)

    override fun getOnItemClick(): OnItemClick {
        return onItemRegistry
    }

    @LayoutRes
    private val layoutId: Int

    @LayoutRes
    abstract fun getLayoutId(): Int

    init {
        layoutId = this.getLayoutId()
    }

    fun getItemView(inflater: LayoutInflater, parent: ViewGroup): View {
        if (layoutId == 0) {
            throw IllegalArgumentException("You must return a layout id.")
        }
        return inflater.inflate(layoutId, parent, false)
    }

    override fun onBindViewHolder(holder: R, item: T) {
        holder.getOnItemClick().dispatchObserver(getOnItemClick().getObserver())
        holder.setData(item)
    }
}

这一层,对事件在OnBindViewHolder中做了一次传递,通过dispatchObserver方法,将ViewBinder中的事件传递到了ViewHolder中。

使用

Observer

interface Observer1 : OnItemClickObserver {
    fun onClick1()
}

interface Observer2 : OnItemClickObserver {
    fun onClick2()
}

定义两个点击事件。

Bean

data class Bean1(var name: String)

class Bean2

创建实体类,和Binder相对应。

ViewHolder

  • ViewHolder1
class ViewHolder1(itemView: View) : BaseViewHolderWrapper<Bean1>(itemView) {
    private val textView = itemView.findViewById<TextView>(R.id.textView)
    override fun setData(t: Bean1) {
        super.setData(t)
        textView.text = t.name
    }

    override fun onClick(v: View) {
        getObserver(Observer1::class.java)?.onClick1()
    }
}

在覆写setData方法的时候,一定要调用super。不然事件无法传递。

响应事件的回调,只需要调用getObserver方法,然后传入对应的Observer,如果有,就直接调用方法。

  • ViewHolder2
class ViewHolder2(itemView: View) : BaseViewHolderWrapper<Bean2>(itemView) {
    var recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView2)
    private val adapter = RecyclerViewAdapter2(mContext)

    init {
        recyclerView.layoutManager = LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false)
        recyclerView.adapter = adapter
        adapter.submitList(listOf(
            Bean1("第二层"),
            Bean1("第二层"),
            Bean1("第二层")
        ))
    }

    override fun setData(t: Bean2) {
        super.setData(t)
        adapter.getOnItemClick().dispatchObserver(getOnItemClick().getObserver())
    }

    override fun onClick(v: View) {
        getObserver(Observer2::class.java)?.onClick2()
    }
}

adapter传递事件,也是通过dispatchObserver方法。

ViewBinder

ViewBinder其实就不需要做什么事情了,比较简单。

class Binder1 : BaseViewBinder<Bean1, ViewHolder1>() {
    override fun getLayoutId(): Int {
        return R.layout.item_1
    }

    override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder1 {
        return ViewHolder1(getItemView(inflater, parent))
    }
}

class Binder2 : BaseViewBinder<Bean2, ViewHolder2>() {
    override fun getLayoutId(): Int {
        return R.layout.item_2
    }

    override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder2 {
        return ViewHolder2(getItemView(inflater, parent))
    }
}

Activity

  • 注册
val binder1 = Binder1()
val binder2 = Binder2()
multiTypeAdapter.register(Bean1::class.java, binder1)
multiTypeAdapter.register(Bean2::class.java, binder2)
  • 添加点击事件
        binder1.getOnItemClick().addObserver(object : Observer1 {
            override fun onClick1() {
                Toast.makeText(this@MainActivity, "binder1 onClick1", Toast.LENGTH_SHORT).show()
            }
        })
        binder2.getOnItemClick().addObserver(object : Observer1 {
            override fun onClick1() {
                Toast.makeText(this@MainActivity, "binder2 onClick1", Toast.LENGTH_SHORT).show()
            }
        })
        binder2.getOnItemClick().addObserver(object : Observer2 {
            override fun onClick2() {
                Toast.makeText(this@MainActivity, "binder2 onClick2", Toast.LENGTH_SHORT).show()
            }
        })
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249