1
votes

Kotlin: Comment passer des données de recyclerview dans un fragment à un autre fragment en utilisant viewmodel / interface?

Je n'ai pas pu trouver d'exemple de transmission de données d'un adaptateur recyclerview à un fragment à l'aide d'un viewmodel ou d'une interface dans kotlin . J'ai essayé d'utiliser juste un bundle, mais je reçois un bundle nul dans le fragment de réception. Voici mon extrait de code Recyclerview Adapter .

class QnAMyProfileEditCRUDFragment : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //Get bundle data and set Edit Text values


        val bundle:Bundle? = arguments //Bundle == null!!!!
        val qnaQuestionData: String? = bundle?.getString("currentQnAQuestion")
        val qnaAnswerData: String? = bundle?.getString("currentQnAAnswer")
        et_qna_question.setText(qnaQuestionData)
        et_qna_answer.setText(qnaAnswerData)


    }

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



        return inflater.inflate(R.layout.fragment_qn_a_my_profile_edit_c_r_u_d, container, false)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)


    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        //Pop fragment if grey area(outside the box) is tapped
        imv_overlay.setOnClickListener {
            fragmentManager?.popBackStack()
        }
    }

}

Voici mon fragment qui est supposé recevoir les données ( bundle ):

class QnAMyProfileEditRecyclerViewAdapter(private val context: Context) : RecyclerView.Adapter<QnAMyProfileEditRecyclerViewAdapter.Holder>() {


    private var currentUserQnADataMyProfileEdit = emptyList<QnAData>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {

        val view: View = if (viewType == R.layout.rv_item_qn_a_my_profile) {

            LayoutInflater.from(parent.context)
                .inflate(R.layout.rv_item_qn_a_my_profile, parent, false)

        } else {

            LayoutInflater.from(parent.context)
                .inflate(R.layout.rv_footer_new_qna_my_profile, parent, false)

        }
        return Holder(view)
    }

    override fun getItemCount(): Int {
        return currentUserQnADataMyProfileEdit.size + 1
    }

    //implement Floating Action Button at the bottom of the recyclerview
    override fun getItemViewType(position: Int): Int {

        return if (position == currentUserQnADataMyProfileEdit.size) {
            R.layout.rv_footer_new_qna_my_profile
        } else {
            R.layout.rv_item_qn_a_my_profile
        }
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {

        if (position == currentUserQnADataMyProfileEdit.size) {
            holder.viewHolderNewQnA?.setOnClickListener {
                Toast.makeText(context, "New item intent", Toast.LENGTH_SHORT).show()
            }
        } else {
            //Normal bind for RecyclerView
            holder.bindData(currentUserQnADataMyProfileEdit[position])
            holder.viewHolderCRUDQnAMyProfile?.setOnClickListener {


                //Give QnACrud current QnAData --- send bundle
                val bundle = Bundle()
                bundle.putString(
                    "currentQnAQuestion",
                    currentUserQnADataMyProfileEdit[position].question
                )
                bundle.putString(
                    "currentQnAAnswer",
                    currentUserQnADataMyProfileEdit[position].answer
                )
                QnAMyProfileEditCRUDFragment().arguments = bundle
                val activity: AppCompatActivity = context as AppCompatActivity
                activity.supportFragmentManager.beginTransaction()
                    .add(R.id.cl_my_profile_edit, QnAMyProfileEditCRUDFragment())
                    .addToBackStack(null).commit()




            }
        }
    }


    fun setData(updateList: List<QnAData>) {
        this.currentUserQnADataMyProfileEdit = updateList
        notifyDataSetChanged()
    }

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




        val viewHolderQuestion =
            itemView.findViewById(R.id.txt_question_qna_my_profile_edit) as TextView?
        val viewHolderAnswer =
            itemView.findViewById(R.id.txt_answer_qna_my_profile_edit) as TextView?

        val viewHolderNewQnA =
            itemView.findViewById(R.id.fab_new_qna_my_profile_edit) as FloatingActionButton?

        val viewHolderCRUDQnAMyProfile =
            itemView.findViewById(R.id.fab_crud_qna_my_profile_edit) as FloatingActionButton?


        //Bind data with inflaters
        fun bindData(currentUserInList: QnAData) {

            viewHolderQuestion?.text = currentUserInList.question
            viewHolderAnswer?.text = currentUserInList.answer
        }

    }
}

QnAData est une simple classe de données avec deux membres question code > et answer sont tous deux du type de chaîne


2 commentaires

Fournissez des parties importantes de votre code pour l'adaptateur RecyclerView.


J'envoie le bundle sur onBindViewHolder dans mon adaptateur


3 Réponses :


1
votes

Essayez de créer un constructeur dans votre fragment, puis beginTransaction

val fragment = QnAMyProfileEditCRUDFragment.newInstance( 
currentUserQnADataMyProfileEdit[position].question, 
currentUserQnADataMyProfileEdit[position].answer)

activity.supportFragmentManager.beginTransaction()
                    .add(R.id.cl_my_profile_edit,fragment)
                    .addToBackStack(null).commit()

Dans le QnAMyProfileEditCRUDFragment, vous pouvez simplement utiliser les valeurs du constructeur


0 commentaires

1
votes

Tout d'abord, il y a quelques refactors à faire sur votre code:

  1. la classe interne peut entraîner une fuite de mémoire, alors changez votre ViewHolder en:
  2. Apportez votre écouteur de clics dans votre ViewHolder

val SomeFragment = SomeFragment().apply {
    SomeFragment.arguments = ... //A bundle for example
}
  1. RecyclerViewAdapter doit définir un protocole pour communiquer avec un fragment:
class Adapter : RecyclerView.Adapter<T>() {

    interface callback {
        fun onSomeEventHappened()
    }


    private var callback: callback? = null

    
    fun setListener(listener: Callback) {
        callback = listener
    }

}
  1. Si les données sont partagées entre deux fragments, utilisez SharedViewModel li>
  2. Si vous avez besoin de transmettre des données, vous pouvez le faire soit avec les arguments AndroidNavigationCompoent, soit en les transmettant lors de la création du fragment:
class MyViewHolder(...) : RecyclerView.ViewHolder(...) {

    fun bind(model: Model) {
        ...

        itemView.setOnClickListener {
            callback?.onSomeEventHappened(model)
            //you can also pass lambda here instead of using an interface for callback
        }

    }

}


2 commentaires

wow merci beaucoup pour la réponse ... quel est le modèle: paramètre de modèle dans la fonction de liaison?


Le modèle @Ian contient les données que vous utilisez pour travailler avec RecyclerView. val model = User (1, "name") Modifiez également votre onCreateHolder () pour utiliser un type de vue plutôt qu'une condition.



1
votes

En supposant que viewModel est partagé par pour votre activité de conteneur Fragment et les fragments enfants, vous pouvez transmettre ce même viewModel à votre adaptateur recyclerview.

class Activity: AppCompatActivity {
   private val viewModel: AnyViewModel by lazy { 
        //Initialization 
   }
   private val adapter = Adapter(viewModel)

   ...
}

class Adapter(viewModel: SomeViewModel) : RecyclerView.Adapter<T>() {

   ...

   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
       holder.onBind(anyList[position])
   }

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


      fun onBind(data: SomeData) {
         btnSomeAction.setOnClickListener {
            //Here change respected data in viewModel or call some method
            //viewModel.anyObject = some_value
            //viewModel.anyMethodCall() 
         }
      }
   }
}

Vous pouvez maintenant accéder à viewModel dans votre adaptateur et utiliser il met à jour les données de l'activité ou du fragment.


0 commentaires