【问题标题】:How can I assign value to variable declared by Fragment, value from onBindViewHolder?如何为 Fragment 声明的变量赋值,来自 onBindViewHolder 的值?
【发布时间】:2022-01-22 19:57:14
【问题描述】:

我想从位置的值中为 Fragment 声明的变量赋值:从 onBindViewHolder 设置的 Int 变量。我的策略是获取 position: Int 的值来传递从 RecyclerView 列表中单击的项目的值分配,而 ListHolder 的 onClick 函数将值用于进一步的任务。

我的源代码导致异常:

androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment info.shutterpress.idols.DetailFragment: calling Fragment constructor caused an exception

caused by
java.lang.IllegalStateException: Fragment DetailFragment{f172e1b} (89c2cac6-2fcc-45ba-993c-e955b8a8fd6f) has null arguments

我认为 onBindViewHolder 在 Fragment 的 onClick 执行之前没有执行,或者出现问题,所以 bind.apply { _position = position } 不起作用。因此,_position 未设置,Safe Args 不起作用并导致上述异常。

源码如下:

package info.shutterpress.idols

import android.content.res.TypedArray
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView
import info.shutterpress.idols.databinding.FragmentRecyclerviewBinding

class ListFragment : Fragment() {
    lateinit var bind: FragmentRecyclerviewBinding
    var title: Array<String>? = null
    var image: TypedArray? = null
    var _position: Int = 0

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        bind = DataBindingUtil.inflate(inflater, R.layout.fragment_recyclerview, container, false)
        title = resources.getStringArray(R.array.idols_title_array)
        image = resources.obtainTypedArray(R.array.idols_list_image_array)
        var adapter = IdolsListAdapter(image, title)
        bind.idolsList.adapter = adapter
        return bind.root
    }

    inner class ListHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
        var titleTextView: TextView = view.findViewById(R.id.detail_item_title)
        var imageView: ImageView = view.findViewById(R.id.detail_item_image)

        init {
            view.setOnClickListener(this)
        }

        override fun onClick(v: View) {
            val action = ListFragmentDirections.actionRecyclerviewFragmentToDetailFragment().setPositionDatum(_position)
            v.findNavController().navigate(action)
        }

        fun bind(position: Int) {
            _position = position
        }
    }

    inner class IdolsListAdapter(var image: TypedArray?, var title: Array<String>?) : RecyclerView.Adapter<ListHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : ListHolder {
            val view = layoutInflater.inflate(R.layout.item_view, parent, false)
            return ListHolder(view)
        }

        override fun getItemCount() = title!!.size

        override fun onBindViewHolder(holder: ListHolder, position: Int) {
            var mImage = image!!.getResourceId(position, -1)
            var mTitle = title!![position]
            holder.apply {
                titleTextView.text = mTitle
                imageView.setImageResource(mImage)
            }
            holder.bind(_position)
        }
    }
}

XML:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_main"
    app:startDestination="@id/main_fragment">

    <fragment
        android:id="@+id/main_fragment"
        android:name="info.shutterpress.idols.MainFragment"
        tools:layout="@layout/fragment_main">
        <action
            android:id="@+id/action_main_fragment_to_recyclerview_fragment"
            app:destination="@id/recyclerview_fragment" />
    </fragment>
    <fragment
        android:id="@+id/recyclerview_fragment"
        android:name="info.shutterpress.idols.ListFragment"
        tools:layout="@layout/fragment_recyclerview">
        <action
            android:id="@+id/action_recyclerview_fragment_to_detail_fragment"
            app:destination="@id/detail_fragment" />
    </fragment>
    <fragment
        android:id="@+id/detail_fragment"
        android:name="info.shutterpress.idols.DetailFragment"
        tools:layout="@layout/fragment_detail">
        <argument
            android:name="positionDatum"
            app:argType="integer"
            android:defaultValue="0" />
    </fragment>
</navigation>

如何使用 ListHolder 中的 onClick 函数将位置和 Int 数据发送给它?

【问题讨论】:

    标签: android kotlin android-safe-args


    【解决方案1】:

    尝试使用 click 监听器或使用 lamba 监听器。将侦听器从 ListFragment 传递到 IdolsListAdapter。单击事件将传递给片段,您可以在其中导航到具有位置的 DetailFragment

    例如:

    inner class IdolsListAdapter(var image: TypedArray?, var title: Array<String>?) : RecyclerView.Adapter<ListHolder>() {
            
            var clickListener: (Int) -> Unit = {}
    
            // other code
    
            override fun onBindViewHolder(holder: ListHolder, position: Int) {
                var mImage = image!!.getResourceId(position, -1)
                var mTitle = title!![position]
                holder.apply {
                    titleTextView.text = mTitle
                    imageView.setImageResource(mImage)
                }
                holder.bind(_position)
                holder.itemView.setOnClickListener {
                     clickListener(position)
                }
            }
        }
    

    在片段中:

     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            super.onCreateView(inflater, container, savedInstanceState)
            // other code
            var adapter = IdolsListAdapter(image, title).apply {
                clickListener = { position -> navigateToDetailFragment(position) }
            }
            bind.idolsList.adapter = adapter
            return bind.root
        }
     
     fun navigateToDetailFragment(position: Int) {
          val action = ListFragmentDirections.actionRecyclerviewFragmentToDetailFragment().setPositionDatum(position)
          v.findNavController().navigate(action)
    }
    

    【讨论】:

    • 感谢您的关心回答,但是,同样的错误发生了。我有干净的构建和重建,以及 API 重新安装。如果我改用 bundle ,则显示内容,但仅显示分配的数组的索引 0。对此有何建议?
    • 我已经解决了。我使用了avinsharma.com/android-recyclerview-onclick指定的高阶函数,并将navArgs相关语句移到DetailFragment的onCreateView中,显示内容非常好。
    猜你喜欢
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-17
    • 2022-11-08
    相关资源
    最近更新 更多