如何处理嵌套的RecyclerView中的单击 [英] How to handle click in a nested RecyclerView

查看:92
本文介绍了如何处理嵌套的RecyclerView中的单击的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以解释如何处理此事的逻辑:

Can someone explain the logic on how to handle this matter:

我有一个片段,在WebSocket调用之后,它会使2个Recyclerviews膨胀.

I have a fragment that after a WebSocket call inflates 2 Recyclerviews.

Child Recyclerview嵌套到Parent Recyclerview,并且父适配器调用子适配器.我想为单击侦听器提供一个接口,该接口可以处理片段中子项"中的单击.

Child Recyclerview is nested to Parent Recyclerview and the parent adapter calls the child adapter. I want to put an Interface for a click listener which handles the click in the Child Items in the Fragment.

我应该在哪里放置接口,以及应该在哪个类中实现它?

Where should I put the interface and which class should implement it?

推荐答案

您尝试执行的操作已完成多次.

What you're trying to do has been done multiple times.

您可以尝试多种方法,但是总的来说,责任看起来像这样:

There are various approaches you can try, but in general, responsibilities would look something like this:

  • 使用 RecyclerView 扩大布局.
  • 实例化 YourAdapter
  • 订阅,请求,等待数据,并将其传递到 YourAdapter .
  • 维护用于点击处理的界面,例如:
interface YourThingClickHandler {
   fun onThingWasClicked(thing: Thing) // and any other thing you may need.
}

  • 可以是 YourContext:YourThingClickHandler ,也可以根据需要保留其匿名/本地实例.我通常先做一个,然后在片段/活动中实现 fun onThingWasClicked(...),这取决于单击该项目时需要执行的操作.
    • Can be YourContext: YourThingClickHandler or if you want, you can keep an anonymous/local instance of that. I usually do the former and then implement the fun onThingWasClicked(...) in the fragment/activity, it depends what you need to do when the item was clicked.
      • 期望一列 Things 一个 YourThingClickHandler 实例.因此,您将在片段/活动"中执行类似(伪代码)的操作:
      • Expects a list of Things and one YourThingClickHandler instance. So in your Fragment/Activity you'd do, something like (pseudo code):
      // 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 直接与此进行通讯.2.让 outerAdapter 充当innerAdapter中发生的两次单击之间的中介,并通过刚刚提供的此单击处理程序将它们冒泡.

      Now that you've passed an outer click handler, you need to deal with the inner list. Now you have a few choices: 1. pass the same click handler all the way in and let the innerAdapter directly talk to this. 2. Have the outerAdapter act as an intermediate between the clicks happening in the innerAdapter and bubble them up via this click handler you just supplied.

      您选择哪种,很大程度上取决于您要使用它的方式以及处理方式.我认为没有对与错.

      Which one you chose, will depend largely on what you want to do with it, and how you want to handle it. There's no right or wrong in my opinion.

      无论您做什么,都仍然需要从视图持有人那里获取 到此点击处理程序...

      Regardless of what you do, you still need to get from the view holder to this click handler...

      因此,在 YourAdapter 中,您应该具有另一个接口:

      So in YourAdapter you should have another Interface:

          interface InternalClickDelegate {
              fun onItemTappedAt(position: Int)
          }
      

      此内部处理程序将用于从viewHolder进行交谈,返回到您的适配器,并将气泡弹出到外部单击处理程序.

      This internal handler, will be used to talk from the viewHolder, back to your Adapter, and to bubble the tap up to the external click handler.

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

      Now you can have a single instance of this, defined like so in your adapter class (remember this is Pseudo-Code):

          private val internalClickHandler: InternalClickDelegate? = object : InternalClickDelegate {
      
              override fun onItemTappedAt(position: Int) {
                  externalClickHandler?.run {
                      onThingWasClicked(getItem(position))
                  }
              }
          }
      

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

      So if the external click handler (the YourThingClickHandler you supplied) is not null, then fetch the item from the adapter's data source, and pass it along.

      在onCreateViewHolder 上执行操作时,请使用一个ViewHolder,它带有您猜到的...,一个 InternalClickDelegate 实例,等等...

      When you do onCreateViewHolder, have a ViewHolder that takes... you guessed, a InternalClickDelegate instance and so...

       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具有对该内部点击处理程序的引用...

      Now your ViewHolder(s) have a reference to this internal click handler...

      因此,当您执行 onBindViewHolder(...)时,您可能会调用您选择的通用ViewHolder方法,例如,如果您的View持有人可以是不同类型,则您可能有一个Abstract ViewHolder与一个 fun bind(thing:Thing)方法或类似方法,每个具体的viewHolder子类型都必须实现...在那里,您将执行以下操作:

      so when you do onBindViewHolder(...) you probably call a common ViewHolder method of your choice, for example if your View holder can be of different types, you probably have an Abstract viewHolder with a fun bind(thing: Thing) method or similar that each concrete viewHolder subType will have to implement... in there, you'd do something like this:

      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方法:

      Because your ViewHolder implements View.OnClickListener, you must implement the onClick method in it:

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

      这是您的ViewHolder如何通过 onClick 方法从Android接收点击/单击事件的方式(如果您提供了Click Handler)(您在传递internalClickHandler时在onCreateViewHolder适配器中所做的操作),它只会使水龙头冒泡,从而传递位置. adapterPosition 与Kotlin等效,相当于在RecyclerView适配器中调用 getAdapterPosition().

      And this is how your ViewHolder, will receive the tap/click event from Android in the onClick method, if you supplied a click Handler (you did in the adapter onCreateViewHolder when you passed the internalClickHandler), it will simply bubble the tap, passing the position. adapterPosition is the Kotlin equivalent of calling getAdapterPosition() in a RecyclerView adapter.

      • 片段:ExternalClickListener->将其实例传递给适配器.
      • 适配器:接收ExternalClickListener,将InternalClickListener传递给每个ViewHolder.
      • ViewHolder :接收内部的Click Listener,将其自身设置为Clickable(如果要整个单元格,则整个 itemView 或只是要使其可单击的任何小部件)若要单击,只需使用 itemView ,它是ViewHolder的整个"视图.
      • Fragment: ExternalClickListener -> passes an instance of it to the Adapter.
      • Adapter: Receives the ExternalClickListener, passes an InternalClickListener to each ViewHolder.
      • ViewHolder: Receives the internal Click Listener, sets itself as Clickable (either the entire itemView or just any widgets you want to make clickable, if you want the whole cell to be clickable, simply use itemView which is the "whole" view of the ViewHolder.

      点击viewHolder的视图后,Android会调用点击监听器的 onClick 方法.在其中,因为您在ViewHolder中,所以可以执行getAdapterPosition()并将其传递给收到的内部点击处理程序.

      When the viewHolder's view is tapped, android calls the click listener's onClick method. In there, and because you are in a ViewHolder, you can do getAdapterPosition() and pass this to the internal click handler you received.

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

      The Adapter then can transform that position back into data, and because you supplied an External clickListener, it can pass the actual item back to the external click listener.

      没什么特别的,您只需要提供相同的机制,并不断传递信息即可.您做什么或拥有多少这些接口,完全取决于您要实现的目标.就像我刚开始所说的那样,每种解决方案都是不同的,并且在制定体系结构决策时必须考虑其他因素.

      There's nothing special about that, you simply need to provide the same mechanism, and keep passing things around. What you do or how many of these interfaces you have, depends entirely on what you're trying to achieve; like I said at the beginning, each solution is different and other factors must be taken into account when making architectural decisions.

      通常,请牢记以下内容:关注点分离:将内容保持在很小的范围内,并保持重点.例如:拥有这个双重界面似乎很疯狂,但是很清楚每个界面都有什么作用.内部的只是关注视图"中的点击",并在所述点击发生的列表中提供位置.

      In general, keep this thing in mind: Separation of Concerns: keep things small and to the point. For E.g.: it may seem crazy to have this double interface, but it's very clear what each does. The internal one, is simply concerned about a "tap" in a "view", and to provide the position in a list where said tap occurred.

      这是适配器需要的所有信息,以获取数据并就什么东西真正被窃听做出明智的猜测.

      This is "all" the adapter needs to fetch the data and make an informed guess at what item was truly tapped.

      该片段不了解(或不在乎)位置",这是Adapter的实现细节.片段遗忘了 position 的事实;但是Fragment很高兴,因为它在回调中收到 Thing ,这很可能是您需要知道的(如果出于某种原因需要知道位置,请定制并修改 externalCallback以具有您选择的签名.

      The fragment doesn't know (or care) about "positions", that's an Adapter's implementation detail; the fact that positions exist, is oblivious to the Fragment; but the Fragment is happy, because it receives the Thing in the callback, which is what most likely needs to know (if you needed to know the position for whatever reason, tailor and modify the externalCallback to have the signature of your choice.

      现在将传递手"从您的OuterAdapter复制到您的InnerAdapter,并且您已经完成了想做的事情.

      Now replicate the "passing hands" from your OuterAdapter to your InnerAdapter, and you have done what you wanted to do.

      祝你好运!

      这篇关于如何处理嵌套的RecyclerView中的单击的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆