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閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370