如何在 Kotlin 中使用与 Activity 和 Fragment 相同的 RecyclerView 适配器?

Posted

技术标签:

【中文标题】如何在 Kotlin 中使用与 Activity 和 Fragment 相同的 RecyclerView 适配器?【英文标题】:How to use the same RecyclerView adapter with Activity and Fragment in Kotlin? 【发布时间】:2021-12-26 06:04:13 【问题描述】:

我创建了一个完美运行的应用程序。我决定对我的应用程序进行一些更改,即在某些地方使用Fragment 而不是Activity。此活动中有一个RecyclerView,用于加载从Firestore 获取的数据。除了使用Fragment 代替Activity 之外,一切都将保持不变。

现在我遇到的问题是 recyclerView 适配器,它需要上下文。

open class CargoListAdapter(
    private val context: Context,
    private var list: ArrayList<Cargo>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() 

单击按钮时,我必须检查上下文以采取相应的进一步操作。

            holder.binding.ibDelete.setOnClickListener 

            when (context) 
                is OrderActivity -> 
                    val activity = holder.itemView.context as? OrderActivity

                    activity?.viewSpecialRequest(
                        model.custom_request
                    )
                

                is MyOrderDetailsActivity -> 
                    val activity = holder.itemView.context as? MyOrderDetailsActivity

                    activity?.viewSpecialRequest(
                        model.custom_request
                    )
                

                is CargoFragment -> 
                    val activity = holder.itemView.context as? CargoFragment

                    activity?.editDeleteSpecialRequestDialog(
                        holder.binding.ibEditDeleteNote,
                        holder.binding.ibAddNote,
                        model.id,
                        model.custom_request
                    )

                
            
        

这就是我从其中一项活动中调用的方式。

val cargoListAdapter = CargoListAdapter(this@OrderActivity, mCargoList)

我尝试从fragment 拨打电话,如下所示,但我在is CargoFragment -&gt; 看到错误“不兼容的类型:CargoFragment 和上下文”

val cargoListAdapter = CargoListAdapter(requireContext(), mCargoList)

编辑: 这不是问题的一部分,而是显示当recyclerView 中有几个按钮时我做了什么,并根据单击的按钮采取行动。有人已经在接受的答案的评论框中询问过它。

现在适配器类的onClickItem 已根据我的要求进行更改。如您所见,我在哪里有action:String,它将让我知道单击了哪个按钮以及需要进一步执行哪些功能/操作。

open class CargoListAdapter(
    private val context: Context,
    private var list: ArrayList<Cargo>,
    private val onClickItem: (pos: Int,addButton: ImageButton,deleteButton: ImageButton,model: Cargo,action:String) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>()

onBindViewHolder我有以下内容。

 holder.binding.ibRemoveItem.setOnClickListener 
                        onClickItem(holder.adapterPosition,
                            holder.binding.ibAddNote,
                            holder.binding.ibDeleteNote,
                            model,
                            "removeItem")

每当单击一个按钮时,一个按钮onClickListener 的设置如上所述,并且根据按钮我更改“操作”的值。如您所见,在上面的示例中,它是“removeItem”,因此从下面的代码中将发生“removeItem”下的操作。 以下是我在“CargoFragment”中的内容

when (action) 
"DeleteNote" -> 
 ///TAKE SOME ACTION HERE

"AddNote" -> 
///TAKE SOME ACTION HERE
 
"removeItem" -> 
///TAKE SOME ACTION HERE

这就是我为处理recyclerView 中有多个按钮的情况所做的工作,并且它可以正常工作。希望高手给点意见,让初学者知道这是否可以遵循。

【问题讨论】:

我认为您需要将requireContext() 替换为this@CargoFragment,因为该片段不是上下文,因此context is CargoFragment -&gt; 会出现类型不匹配 您为什么不尝试类似于我在this 回答您的问题时提出的建议?将回调 lambda 传递给您的适配器,并让调用者决定调用该 lambda 时要做什么。 正如我在那里所说的,这对我作为初学者来说是新事物。我按照您的建议更改了几个类似的功能。但是,这是完全不同的东西,一个适配器,我不想破坏我所有的代码,试图自己实现你的版本。顺便说一句,我喜欢你的代码,我会尽可能为所有新代码使用相同的版本。 @Zain 为此,我必须将适配器类的参数“context”更改为CargoFragment。我不能这样做,因为活动也使用相同的适配器。 【参考方案1】:

您可以使用 Kotlin 高阶函数来使适配器独立并在代码中的任何位置使用,而不是使用接口并通过使用 Activity 或 Fragment 实例来访问函数。

open class CargoListAdapter(
    private val context: Context,
    private var list: ArrayList<Cargo>,
    private val onClickItem:(pos: Int, viewId: Int) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() 

点击时在 ViewHolder 中调用 onClickItem :

    holder.binding.ibDelete.setOnClickListener 
        onClickItem(adapterPosition, it.id)
    

    holder.binding.ibEdit.setOnClickListener 
        onClickItem(adapterPosition, it.id)
    

在 Activity 或 Fragment 中:

adapter = CargoListAdapter(context, list)position, viewId->
   //receive click here
    when(viewId)
        R.id.ibDelete -> //Delete Action
        R.id.ibEdit -> //Delete Action
        // You can use sam click listener for multiple views by passing id
    

查看此要点以获取完整的适配器示例:https://github.com/Noddy20/Gists/blob/master/RecyclerViewAdapter-1.kt

【讨论】:

如果我需要单击另外三个按钮,例如不只是删除按钮,我该怎么做?写三个 kotlin 函数? @Yura 请在我的问题中的“编辑”之后查找详细信息。希望这会有所帮助。 就像我传递了 pos: Int for item position 一样,您也可以为单击的视图 id 传递另一个参数。比在 Activity/Fragment 中使用该视图 ID(带有条件 when/if/else)来执行您需要的操作。 @Yura 更新了多次查看点击的答案。【参考方案2】:

您检查上下文,然后将其转换为创建适配器的活动或片段,然后在调用活动/片段方法viewSpecialRequest 这不是很正确,如果您想在单击后调用活动/片段方法ibDelete 你需要添加例如一个接口。

open class CargoListAdapter(
    private val context: Context,
    private var list: ArrayList<Cargo>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() 

interface OnItemActionsListener 
        fun onDeleteClicked()
    

var onItemActionsListener:OnItemActionsListener? =null

 holder.binding.ibDelete.setOnClickListener 
     onItemActionsListener?.onDeleteClicked()
  

********* 
 

订单活动

adapter = CargoListAdapter(this, arrayListOf())
adapter.onItemActionsListener = object  : CargoListAdapter.OnItemActionsListener 
                    override fun onDeleteClicked() 
                        // TODO logic
                    
                

货物片段

adapter = CargoListAdapter(requireActivity(), arrayListOf())
adapter.onItemActionsListener = object  : CargoListAdapter.OnItemActionsListener 
                    override fun onDeleteClicked() 
                        // TODO logic
                    
                

如果你需要在适配器中做逻辑,而不是上下文检查,你可以使用枚举。

public enum DeleteCaseEnum 
    NONE, CASE_1, CASE_2, CASE_3


open class CargoListAdapter(
    private val context: Context,
    private var list: ArrayList<Cargo>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() 

var deleteCase:DeleteCaseEnum  = DeleteCaseEnum .NONE

   holder.binding.ibDelete.setOnClickListener 

            when (deleteCase) 
                is DeleteCaseEnum.CASE_1-> 
                 // TODO
                
              
                is DeleteCaseEnum.CASE_2-> 
                 // TODO
                
            

订单活动

adapter = CargoListAdapter(this, arrayListOf())
adapter.deleteCase = DeleteCaseEnum.CASE_1

货物片段

adapter = CargoListAdapter(requireActivity(), arrayListOf())
adapter.deleteCase = DeleteCaseEnum.CASE_2

【讨论】:

以上是关于如何在 Kotlin 中使用与 Activity 和 Fragment 相同的 RecyclerView 适配器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Kotlin 中将数据从 Service 传递到 Activity?

如何通过单击 Image Kotlin Android 来移动 Activity

Kotlin 上的 Android:如何将数据从 Activity 传递到事件监听器?

Kotlin BaseNodeProvider()如何转跳到Activity

在 Kotlin 中将值从 Activity 传递到 Fragment

如何在 Kotlin 中注入原始变量?