【问题标题】:How can I get Activity from RecyclerView.ViewHolder(itemView) in Android Studio 3.5.2?如何从 Android Studio 3.5.2 中的 RecyclerView.ViewHolder(itemView) 获取 Activity?
【发布时间】:2019-11-08 21:26:29
【问题描述】:

函数Navigation.findNavController() 需要代码A 中的Activity 参数,但我只能通过itemView.context 获得context

如何在 Android Studio 中从RecyclerView.ViewHolder(itemView) 获取Activity

目前,我必须将private val mActivity: Activity从代码B传递到代码A,它可以工作,但我认为这不是一个好方法。

添加内容:

致 Miriana Itani:谢谢!

您的代码运行良好。我粘贴FrameLayout xml文件,你可以看到Code C

我很奇怪Navigation.findNavController(temView)...的代码是怎么找到NavController控件的? 你能告诉我吗?

你知道使用代码Navigation.findNavController(mActivity, R.id.fragment_container)很容易理解,因为NavController的ID是fragment_container,而且是唯一的。

代码 A

class VideoListAdapter (private val videoList: List<File>, private val mActivity: Activity) : RecyclerView.Adapter<VideoListAdapter.MyViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoListAdapter.MyViewHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.layout_fragment_video_list_recycleview, parent, false)
        return MyViewHolder(v)
    }

    override fun onBindViewHolder(holder: VideoListAdapter.MyViewHolder, position: Int) {
        holder.bindItems(videoList[position])
    }

    override fun getItemCount(): Int {
        return videoList.size
    }

    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindItems(aFile: File) {           
            itemView.tvVideoFileName.text=aFile.name
            itemView.btnPlay.setOnClickListener {
                Navigation.findNavController(mActivity, R.id.fragment_container).navigate(
                    UIFragmentVideoListDirections.aVideoList2Video((aFile.absolutePath)) )

            }
        }
    }

}

代码 B

class UIFragmentVideoList : Fragment() {

    private lateinit var listFile: MutableList<File>

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.layout_fragment_video_list, container, false)


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val outputDirectoryOfVideo = getVideoOutputDirectory(requireContext())
        listFile =outputDirectoryOfVideo.listFiles{file -> VIDEO_EXTENSION_WHITELIST.contains(file.extension.toUpperCase())}
                   .sorted().reversed().toMutableList()

        videoRecyclerView.layoutManager =  LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
        val aVideoListAdapter=VideoListAdapter(listFile,requireActivity())
        videoRecyclerView.adapter=aVideoListAdapter

    }

}

代码 C

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    >

    <fragment
            android:id="@+id/fragment_container"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

</FrameLayout>

【问题讨论】:

  • 只需使用接口将点击处理逻辑移至您的片段,然后在您的UIFragmentVideoList 中调用Navigation.findNavController()

标签: android android-studio kotlin


【解决方案1】:

事实上,我认为你不需要传递活动来让 NavController 工作:

我认为更好的方法如下:

Navigation.findNavController(temView).navigate(
            UIFragmentVideoListDirections.aVideoList2Video((aFile.absolutePath))
        )

请尝试告诉我。

添加答案:

NavHostFragment — 实现用于创建片段目标的 NavHost。它有自己的 NavController 和导航图。通常每个活动都有一个 NavHostFragment。

Navigation — 用于获取 NavController 实例并将导航连接到 UI 事件(如按钮单击)的辅助类。

NavigationUI — 用于将抽屉、底部导航或操作栏等应用导航模式与 NavController 连接起来的类。

【讨论】:

  • 谢谢!您能在我的问题中看到我添加的内容吗?
  • 虽然我经常直接从 ViewHolder 导航,但最好将控制权传回 ViewModel(通过接口)。
  • @HelloCW 让我知道是否可以解释
  • @AjahnCharles 实际上我发现这种方式更好,因为没有接口传递。如果有任何官方文档不推荐该问题,请纠正我。
  • 它的代码更少,但它混合了责任。不过,我完全赞成不抢先重构!我想这取决于项目的规模。 (我确实赞成!)
【解决方案2】:

您可以像这样从视图的上下文中检索 Activity:

tailrec fun Context.findActivity(): Activity {
    if (this is Activity) {
        @Suppress("UNCHECKED_CAST")
        return this
    } else {
        if (this is ContextWrapper) {
            return this.baseContext.findActivity()
        }
        throw IllegalStateException("The context chain does not contain Activity!")
    }
}

现在你可以做

val activity = view.context.findActivity()

【讨论】:

  • 这鼓励将所有东西都耦合到 Activity 而不是使用接口,这将使单元测试更加困难。
  • 但它确实回答了我的初衷How can I get Activity from RecyclerView.ViewHolder(itemView) 的问题。如果我们像往常一样处理它,您当然可以使用接口或函数类型,如stackoverflow.com/a/51867792/2413303
【解决方案3】:

您可以在片段中将函数传递给适配器。这样你就不必通过活动了

class VideoListAdapter (private val videoList: List<File>, val btnPlayClick: (File) -> Unit) : RecyclerView.Adapter<VideoListAdapter.MyViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoListAdapter.MyViewHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.layout_fragment_video_list_recycleview, parent, false)
        return MyViewHolder(v)
    }

    override fun onBindViewHolder(holder: VideoListAdapter.MyViewHolder, position: Int) {
        holder.bindItems(videoList[position], btnPlayClicked )
    }

    override fun getItemCount(): Int {
        return videoList.size
    }

    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindItems(aFile: File, click -> (File) -> Unit) {           
            itemView.tvVideoFileName.text=aFile.name
            itemView.btnPlay.setOnClickListener {
                 click(aFile)
            }
        }
    }
}

并在您的片段中像这样构建适配器

val aVideoListAdapter = VideoListAdapter(listFile) { myFile ->
    Navigation.findNavController(activity!!, R.id.fragment_container).navigate(
                    UIFragmentVideoListDirections.aVideoList2Video((myFile.absolutePath)) 
    )
}

【讨论】:

  • 我喜欢在 ViewHolder 文件中创建一个typealias 以提高可读性:typealias OnItemClickListener = (File) -&gt; Unit)。然后您可以将构造函数参数更改为val itemClickListener: OnItemClickListener
猜你喜欢
  • 1970-01-01
  • 2021-11-19
  • 2020-09-17
  • 1970-01-01
  • 2021-06-10
  • 2012-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多