繁体   English   中英

如何处理嵌套 RecyclerView 中的点击

[英]How to handle click in a nested RecyclerView

有人可以解释如何处理这个问题的逻辑:

我有一个片段,在 WebSocket 调用后膨胀了 2 个 Recyclerviews。

Child Recyclerview 嵌套在 Parent Recyclerview 中,父适配器调用子适配器。 我想为单击侦听器放置一个接口,该接口处理片段中子项中的单击。

我应该把接口放在哪里,哪个 class 应该实现它?

您尝试做的事情已经多次完成。

您可以尝试多种方法,但总的来说,职责如下所示:

YourContext (片段/活动)

  • 使用RecyclerView扩展布局。
  • 实例化YourAdapter
  • 订阅、请求、等待您的数据并将其传递给YourAdapter
  • 维护一个用于点击处理的界面,例如:
interface YourThingClickHandler {
   fun onThingWasClicked(thing: Thing) // and any other thing you may need.
}
  • 可以是YourContext: YourThingClickHandler或者如果你愿意,你可以保留一个匿名/本地实例。 我通常做前者,然后在片段/活动中实现fun onThingWasClicked(...) ,这取决于单击项目时您需要做什么。

YourAdapter

  • 需要一个Things列表一个YourThingClickHandler实例。 所以在你的片段/活动中你会做的事情,比如(伪代码):
// 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.
}

现在您已经传递了一个外部点击处理程序,您需要处理内部列表。 现在你有几个选择: 1. 一直传入相同的点击处理程序,让innerAdapter直接与 this 对话。 2.让outerAdapter充当innerAdapter中发生的点击之间的中间人,并通过您刚刚提供的这个点击处理程序将它们冒泡。

你选择哪一个,很大程度上取决于你想用它做什么,以及你想如何处理它。 在我看来没有对错之分。

不管你做什么,你仍然需要从视图持有者到这个点击处理程序......

所以在YourAdapter你应该有另一个接口:

    interface InternalClickDelegate {
        fun onItemTappedAt(position: Int)
    }

这个内部处理程序将用于从 viewHolder 与您的适配器对话,并将点击冒泡到外部单击处理程序。

现在你可以有一个这样的实例,在你的适配器 class 中定义(记住这是伪代码):

    private val internalClickHandler: InternalClickDelegate? = object : InternalClickDelegate {

        override fun onItemTappedAt(position: Int) {
            externalClickHandler?.run {
                onThingWasClicked(getItem(position))
            }
        }
    }

因此,如果外部点击处理程序(您提供的YourThingClickHandler )不是 null,则从适配器的数据源获取项目,并将其传递。

你如何将这个内部处理程序与每个视图持有者联系起来?

当你做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)

现在你的 ViewHolder(s) 有一个对这个内部点击处理程序的引用......

因此,当您执行onBindViewHolder(...)时,您可能会调用您选择的通用 ViewHolder 方法,例如,如果您的 View Holder 可以是不同类型,您可能有一个带有fun bind(thing: Thing)方法的 Abstract viewHolder 或类似于每个具体的 viewHolder 子类型都必须实现......在那里,你会做这样的事情:

override fun bind(thing: Thing) {
   if (clickHandler != null) {
       someViewYourViewHolderInflated.setOnClickListener(this) // this assumes your ViewHolder implements View.OnClickListener from the framework
   }
}

因为您的 ViewHolder 实现了View.OnClickListener ,所以您必须在其中实现 onClick 方法:

    override fun onClick(v: View?) {
        clickHandler?.onItemTappedAt(adapterPosition)
    }

这就是您的 ViewHolder 将如何在onClick方法中接收来自 Android 的点击/点击事件,如果您提供了一个点击处理程序(您在传递 internalClickHandler 时在适配器 onCreateViewHolder 中做了),它只会冒泡点击,传递position。 adapterPosition是 Kotlin 等效于在 RecyclerView 适配器中调用getAdapterPosition()

太长了,没看图

  • 片段:ExternalClickListener -> 将它的一个实例传递给适配器。
  • Adapter :接收 ExternalClickListener,将 InternalClickListener 传递给每个 ViewHolder。
  • ViewHolder :接收内部 Click Listener,将自身设置为 Clickable(整个itemView或您想要使其可点击的任何小部件,如果您希望整个单元格可点击,只需使用itemView即可,它是 ViewHolder 的“整体”视图.

当viewHolder的view被点击时,android调用点击监听的onClick方法。 在那里,因为您在 ViewHolder 中,您可以执行 getAdapterPosition() 并将其传递给您收到的internal点击处理程序。

然后适配器可以将该 position 转换回数据,并且由于您提供了外部clickListener ,它可以将实际项目传递回外部点击侦听器。

等等,但是 NESTED RecyclerView 怎么样。

没有什么特别的,你只需要提供相同的机制,并不断传递东西。 你做什么或者你有多少这些接口,完全取决于你想要实现什么; 就像我在开始时所说的那样,每个解决方案都是不同的,在做出架构决策时必须考虑其他因素。

一般来说,请记住这一点:关注点分离:保持小事和重点 例如:拥有这个双重界面可能看起来很疯狂,但很清楚每个界面的作用。 内部的,只是关心“视图”中的“点击”,并在发生所述点击的列表中提供 position。

这是适配器需要获取数据并对真正被点击的项目做出明智猜测的“全部”。

片段不知道(或关心)“位置”,这是适配器的实现细节; positions存在的事实对 Fragment 是无视的; 但是 Fragment 很高兴,因为它在回调中接收到Thing ,这是最有可能需要知道的(如果您出于任何原因需要了解 position,请定制和修改externalCallback以具有您选择的签名。

现在将 OuterAdapter 中的“传递手”复制到 InnerAdapter,您已经完成了您想做的事情。

祝你好运!

1)您应该将接口放在子适配器中并在父适配器中实现,然后传递另一个接口(长时间)

2)使用本地广播管理器

您将在父适配器中添加 ClickListener 并将其添加到适配器的构造函数中

public interface HandleClickListener
 {
    void onItemClicked(int position, SurveysListModel surveysListModel);
   }

创建一个 clicklistener 实例,然后在 holderclick listner 上从 model 列表中获取项目的 position 及其值

holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                handleClickListener.onItemClicked(position, yourModelList.get(position));
});

并像这样进入您的活动,制作您的适配器实例

adapter = new InstrumentsSearchAdapter(yourModelsList, activity.this, new SearchAdapter.HandleClickListener() {
                    @Override
                    public void onItemClicked(int position, Model listModel) {
                        instumentChild = listModel.getInstrument_title();
                        Intent intent = new Intent(Activity.this, Listing.class);
                        intent.putExtra("selectedQuestions", listModel.getQuestions());
                        startActivityForResult(intent, 5);
                    }
                });

如果你想 go 到父 recyclerview class 实现 onActivityResutlt 方法并通过意图从孩子那里取回数据并在 onActivityResutlt 方法中获取该意图

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM