Poi*_*hat 4 android interface listener android-fragments android-recyclerview
有人可以解释一下如何处理这个问题的逻辑:
我有一个片段,在 WebSocket 调用之后会膨胀 2 个 Recyclerviews。
Child Recyclerview 嵌套在 Parent Recyclerview 中,父适配器调用子适配器。我想为单击侦听器放置一个接口,该接口处理 Fragment 中子项中的单击。
我应该把接口放在哪里,哪个类应该实现它?
您尝试做的事情已经进行了多次。
您可以尝试多种方法,但一般而言,职责如下所示:
YourContext
(片段/活动)RecyclerView
.YourAdapter
YourAdapter
.interface YourThingClickHandler {
fun onThingWasClicked(thing: Thing) // and any other thing you may need.
}
Run Code Online (Sandbox Code Playgroud)
YourContext: YourThingClickHandler
或者如果你愿意,你可以保留一个匿名/本地实例。我通常先做前者,然后fun onThingWasClicked(...)
在片段/活动中实现,这取决于单击项目时您需要做什么。YourAdapter
Things
和一个YourThingClickHandler
实例。因此,在您的 Fragment/Activity 中,您会这样做,例如(伪代码):// This is called once your ViewModel/Presenter/Repository/etc. makes the data available.
fun onThingsLoaded(things: List<Thing>) {
adapter.setClickHandler(this) // this can be passed when you construct your adapter too via constructor like adapter = YourAdapter(this)
adapter.submitList(things) // if the adapter extends a `ListAdapter` this is all you need.
}
Run Code Online (Sandbox Code Playgroud)
现在您已经传递了一个外部点击处理程序,您需要处理内部列表。现在您有几个选择: 1. 一直传递相同的点击处理程序并让innerAdapter
直接与此对话。2. 将outerAdapter
行为作为innerAdapter 中发生的点击之间的中间件,并通过您刚刚提供的这个点击处理程序将它们冒泡。
你选择哪一个,很大程度上取决于你想用它做什么,以及你想如何处理它。在我看来没有对与错。
不管你做什么,你仍然需要从视图持有者到这个点击处理程序......
所以YourAdapter
你应该有另一个接口:
interface InternalClickDelegate {
fun onItemTappedAt(position: Int)
}
Run Code Online (Sandbox Code Playgroud)
这个内部处理程序将用于从 viewHolder 对话,回到您的适配器,并将点击冒泡到外部点击处理程序。
现在你可以有一个这样的实例,在你的适配器类中像这样定义(记住这是伪代码):
private val internalClickHandler: InternalClickDelegate? = object : InternalClickDelegate {
override fun onItemTappedAt(position: Int) {
externalClickHandler?.run {
onThingWasClicked(getItem(position))
}
}
}
Run Code Online (Sandbox Code Playgroud)
因此,如果外部单击处理程序(YourThingClickHandler
您提供的)不为空,则从适配器的数据源中获取该项目,并将其传递。
当你这样做时onCreateViewHolder
,有一个 ViewHolder 需要......你猜对了,一个InternalClickDelegate
实例等等......
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
// decide which viewHolder you're inflating... and...
return YourViewHolder(whateverViewYouInflate, internalClickHandler)
Run Code Online (Sandbox Code Playgroud)
现在你的 ViewHolder(s) 有一个对这个内部点击处理程序的引用......
因此,当您这样做时,onBindViewHolder(...)
您可能会调用您选择的通用 ViewHolder 方法,例如,如果您的 View 持有者可以是不同类型,那么您可能有一个抽象 viewHolderfun bind(thing: Thing)
方法或类似方法,每个具体的 viewHolder 子类型都必须实现...在那里,你会做这样的事情:
override fun bind(thing: Thing) {
if (clickHandler != null) {
someViewYourViewHolderInflated.setOnClickListener(this) // this assumes your ViewHolder implements View.OnClickListener from the framework
}
}
Run Code Online (Sandbox Code Playgroud)
因为您的 ViewHolder 实现了View.OnClickListener
,所以您必须在其中实现 onClick 方法:
override fun onClick(v: View?) {
clickHandler?.onItemTappedAt(adapterPosition)
}
Run Code Online (Sandbox Code Playgroud)
这就是您的 ViewHolder 将在onClick
方法中接收来自 Android 的点击/点击事件的方式,如果您提供了点击处理程序(您在传递 internalClickHandler 时在适配器 onCreateViewHolder 中做了),它将简单地使水龙头冒泡,传递位置. adapterPosition
是 Kotlin 中调用getAdapterPosition()
RecyclerView 适配器的等价物。
itemView
小部件还是您想要使其可点击的任何小部件,如果您希望整个单元格都可点击,只需使用itemView
ViewHolder 的“整体”视图。当 viewHolder 的视图被点击时,android 调用点击监听器的onClick
方法。在那里,因为您在 ViewHolder 中,您可以执行 getAdapterPosition() 并将其传递给internal
您收到的点击处理程序。
然后 Adapter 可以将该位置转换回数据,并且因为您提供了一个 External clickListener
,它可以将实际项目传回给外部点击侦听器。
这没什么特别的,你只需要提供相同的机制,并不断地传递东西。你做什么或者你有多少这样的接口,完全取决于你想要实现什么;就像我一开始说的,每个解决方案都是不同的,在做出架构决策时必须考虑其他因素。
一般而言,请记住以下几点:关注点分离:保持小事一桩。例如:拥有这个双界面可能看起来很疯狂,但很清楚每个界面的作用。内部的,只关心“视图”中的“点击”,并提供列表中发生所述点击的位置。
这就是适配器获取数据并对真正挖掘的项目做出明智的猜测所需的“全部”。
片段不知道(或关心)“位置”,这是适配器的实现细节;positions
存在的事实,对 Fragment 一无所知;但是 Fragment 很高兴,因为它Thing
在回调中接收,这是最有可能需要知道的(如果您出于任何原因需要知道位置,请裁剪和修改externalCallback
以具有您选择的签名。
现在将“传递手”从您的 OuterAdapter 复制到您的 InnerAdapter,您已经完成了您想做的事情。
祝你好运!
归档时间: |
|
查看次数: |
2324 次 |
最近记录: |