Android Kotlin get data from a clicked item in Recyclerview and pass it between fragments

╄→尐↘猪︶ㄣ 提交于 2020-06-17 09:11:46

问题


So I have two data classes (OutletListDataClass and ProductListDataClass) that are currently used by two different fragments (OutletListFragment.kt and ProductListFragment.kt, directly connected in navigation graph.).

Both of there fragments are RecyclerView. I want to extract the information from clicked item in OutletListFragment.kt (That contained in the two data classes) and pass it to ProductListFragment.kt.

These are the data classes I use in both fragments:

OutletListDataClass

data class OutletListPOJODataClasses(

    @SerializedName("data")
    @Expose
    var data: List<OutletListPOJODataClassesDataItem>? = null,

    @SerializedName("error")
    val error: OutletListDataClassError? = null
)



data class OutletListPOJODataClassesDataItem(
    @SerializedName("stk_prodcode")//Both data classes have this variable
    @Expose
    val stkProdcode: String? = null,

    @SerializedName("stk_allqty")
    @Expose
    val stkAllqty: Int? = null,

    @SerializedName("skt_lastupdate")
    @Expose
    val sktLastupdate: String? = null,

    @SerializedName("outlet_address")
    @Expose
    val outletAddress: String? = null,

    @SerializedName("outlet_name")
    @Expose
    val outletName: String? = null,

    @SerializedName("stk_outcode")
    @Expose
    val stkOutcode: String? = null
)

ProductListDataClass

data class ProductListPOJODataClasses(

    @field:SerializedName("data")
    val data: List<ProductListPOJODataClassesDataItem?>? = null,

    @field:SerializedName("error")
    val error: ProductListDataClassError? = null
)

data class ProductListDataClassError(

    @field:SerializedName("msg")
    val msg: String? = null,

    @field:SerializedName("code")
    val code: Int? = null,

    @field:SerializedName("status")
    val status: Boolean? = null
)

data class ProductListPOJODataClassesDataItem(
    @field:SerializedName("stk_prodcode")  //Both data classes have this variable
    val stkProdcode: String? = null,

    @field:SerializedName("stk_allqty")
    val stkAllqty: Int? = null,

    @field:SerializedName("pro_saleprice")
    val proSaleprice: Int? = null,

    @field:SerializedName("skt_lastupdate")
    val sktLastupdate: String? = null,

    @field:SerializedName("stk_outcode")
    val stkOutcode: String? = null,

    @field:SerializedName("pro_name")
    val proName: String? = null
)

As You can see, these two data Classes contains the sa.me variable (StkOutcode). And That's what I want to extract (From OutletListDataItem)

Here's the code snippet of the current OutletListFragment.kt:

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    (activity as AppCompatActivity).supportActionBar?.title = "Product List"

    binding = DataBindingUtil.inflate(
        inflater,
        R.layout.fragment_product_stock_outlet_list,
        container,
        false
    )

    //Show Progressbar While loading data
    showProgressBar()

    //Apply layout manager
    binding.rvOutletList.layoutManager = LinearLayoutManager((activity as AppCompatActivity))

    //Fetch Outlet List Data
    fetchOutletListData()

    // Declare that this fragment has menu
    setHasOptionsMenu(true)

    // Set action bar title to "Outlet List"
    (activity as AppCompatActivity).supportActionBar?.title = "Outlet List"

    return binding.root
}

And this is the recyclerview adapter I created for OutletListFragment. OutletListAdapter

 class OutletListItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    fun bind(
        outlet: OutletListPOJODataClassesDataItem,
        clickListener: OutletListOnItemClickListener?
    ) {
        itemView.outletName.text = outlet.outletName

        itemView.setOnClickListener {
            clickListener?.onItemClicked(outlet)
        }
    }
}

class OutletListAdapter(private var outletList: OutletListPOJODataClasses, private val itemClickListener: OutletListOnItemClickListener)
: RecyclerView.Adapter<OutletListItemHolder>() {

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

    override fun getItemCount(): Int = outletList.data!!.size

    override fun onBindViewHolder(holder: OutletListItemHolder, position: Int) {
        val outlet = outletList.data?.get(position)
        if (outlet != null) {
            holder.bind(outlet, itemClickListener)
        }
    }
}


interface OutletListOnItemClickListener{
    fun onItemClicked(outlet: OutletListPOJODataClassesDataItem)
}

How I can get the variable I want from the Data class in OutletListFragment.kt and pass it to ProductListFragment.kt ? If there's anything unclear, let me know.

Edit:

This is the snippet of the navigation.xml:

<fragment
    android:id="@+id/productStockOutletList"
    android:name="com.example.switchingandroidappproject.mainFragments.ProductStockOutletListFragment"
    android:label="ProductStockOutletList" >
    <action
        android:id="@+id/action_productStockOutletList_to_productListFragment"
        app:destination="@id/productListFragment"
        app:enterAnim="@android:anim/slide_in_left"
        app:exitAnim="@android:anim/slide_out_right"
        app:popEnterAnim="@android:anim/slide_in_left"
        app:popExitAnim="@anim/slide_out_left" />
</fragment>

<fragment
    android:id="@+id/productListFragment"
    android:name="com.example.switchingandroidappproject.mainFragments.ProductListFragment"
    android:label="ProductListFragment" />

回答1:


If you are using Jetpack Navigation (and as you are referring to things like "navigation graphs", so clearly you are using Jetpack Navigation), then to pass arguments between navigation destinations, you must define an <action with the right arguments that is then passed as some variant of parcelable data to the other fragment.

This is the example from the docs:

<action android:id="@+id/startMyFragment"
    app:destination="@+id/myFragment">
    <argument
        android:name="myArg"
        app:argType="integer"
        android:defaultValue="1" />
</action>

So in your case, it should like you need something like

<navigation ...>
    <action android:id="@+id/toProductListFragment"
        app:destination="@+id/productListFragment">
        <argument
            android:name="stkProdcode"
            app:argType="string" />
    </action>

Apparently this stuff only works if you also enable the safeargs plugin (as otherwise you have to "remember" this key yourself, or write the NavDirections class by hand, or put together the Bundle yourself - either way, Jetpack Navigation expects you to use Safeargs to pass arguments):

buildscript {
    dependencies {
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.2.2"

And

apply plugin: "androidx.navigation.safeargs.kotlin"

And then you should be able to do

class OutletListFragment: Fragment(), OutletListOnItemClickListener {
    ....

    override fun onItemClicked(outlet: OutletListPOJODataClassesDataItem) {
        Navigation.findNavController(view).navigate(NavigationDirections.toProductListFragment(outlet.stkProdcode))
    }
}

And due to limitations of Jetpack Navigation, you also have to duplicate the argument definition into the <fragment tag as well:

<fragment
    android:id="@+id/productListFragment"
    android:name="com.example.switchingandroidappproject.mainFragments.ProductListFragment"
    android:label="ProductListFragment" >
    <argument
        android:name="stkProdcode"
        app:argType="string" />
</fragment>

Make sure not to make a typo (android:name mismatch) as you'd run into unexpected failures).

Now you can obtain your arguments on the other side as

val args = ProductListFragmentArgs.fromBundle(getArguments())
val code = args.stkProdcode

I don't really use Jetpack Navigation, so I'm surprised to see that you have to duplicate the fragment args to the action args, but there you have it.


...to think with what I use in place of Jetpack Navigation, this is as simple as

// send
backstack.goTo(ProductListScreen(stkProdCode))
// receive
val prodCode = getKey<ProductListScreen>().stkProdCode

And is just as (if not more) type-safe. 🙄 *grumble* *grumble* but that doesn't help your case, that's not Jetpack Navigation.



来源:https://stackoverflow.com/questions/62151721/android-kotlin-get-data-from-a-clicked-item-in-recyclerview-and-pass-it-between

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!