首页 > 解决方案 > 一个复选框触发不同布局中的另一个复选框

问题描述

我目前正在编写一个显示电影列表的应用程序。我有许多显示包含电影的卡片视图的片段,每个卡片视图都有一个复选框。用户可以按下卡片视图以转到存在另一个复选框的电影的详细信息页面。

这两个复选框的目标是将电影添加到收藏夹选项卡。

我的问题是,当用户检查卡片视图中的复选框时,如何检查详细信息页面内的复选框?

下面是相关代码。

感谢我能得到的所有帮助。

MoviesListFragment.kt


package com.example.moviesapp.ui.Fragments

import android.os.Bundle
import android.view.*
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.moviesapp.R
import com.example.moviesapp.databinding.FragmentMoviesListBinding
import com.example.moviesapp.network.MoviesFavorites
import com.example.moviesapp.network.MoviesResults
import com.example.moviesapp.ui.DaoViewModel
import com.example.moviesapp.ui.MovieApiStatus
import com.example.moviesapp.ui.MoviesListAdapter
import com.example.moviesapp.ui.MoviesListViewModel
import dagger.hilt.android.AndroidEntryPoint


@AndroidEntryPoint
class MoviesListFragment : Fragment(R.layout.fragment_movies_list), MoviesListAdapter.OnItemClickListener {


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

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



    private val daoViewModel by viewModels<DaoViewModel>()
    private val viewModel by viewModels<MoviesListViewModel>()
    private var _binding: FragmentMoviesListBinding? = null
    private val binding get() = _binding!!




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

        //View is inflated layout

        _binding = FragmentMoviesListBinding.bind(view)

        val adapter = MoviesListAdapter(this)


        binding.apply {
            recyclerView.layoutManager = LinearLayoutManager(requireContext())
            //Disable animations
            recyclerView.setHasFixedSize(true)
            recyclerView.adapter = adapter


        }

        //Observe the movies livedata
        //Use viewLifecycleOwner instead of this because the UI should stop being updated when the fragment view is destroyed
        viewModel.getTrending()

       viewModel.moviesTrending.observe(viewLifecycleOwner) {
           adapter.submitList(it)

       }

        viewModel.networkState.observe(viewLifecycleOwner, {
            binding.progressBar.isVisible = if (it==MovieApiStatus.LOADING) true else view.isGone
            binding.buttonRetry.isVisible = if(it==MovieApiStatus.ERROR) true else view.isGone
            binding.errorTextView.isVisible = if(it==MovieApiStatus.ERROR) true else view.isGone
            binding.recyclerView.isVisible =  if(it==MovieApiStatus.DONE) true else view.isGone
            binding.noResultsText.isVisible = false


        })









        //Display trending movies

        //loadstate is of type combined loadstates, which combines the loadstate of different scenarios(when we refresh dataset or when we append new data to it) into this one object
        //We can use it to check for these scenarios and make our views visible or unvisible according to it

       setHasOptionsMenu(true)
    }

    override fun  onItemClick(movie: MoviesResults.Movies) {
        val action = MoviesListFragmentDirections.actionMoviesListFragmentToMoviesDetailsFragment(movie)
        findNavController().navigate(action)
    }

    override fun onFavoriteClick(favorites: MoviesFavorites) {
        daoViewModel.addMovieToFavs(favorites)
    }

    override fun onDeleteClick(favorites: MoviesFavorites) {
        daoViewModel.deleteMovieFromFavs(favorites)
    }






    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)

        // Inflate the gallery menu
        inflater.inflate(R.menu.menu_gallery, menu)




    }





    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

}

MoviesListAdapter.kt


package com.example.moviesapp.ui

import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.moviesapp.R
import com.example.moviesapp.databinding.MovieLayoutBinding
import com.example.moviesapp.network.MoviesFavorites
import com.example.moviesapp.network.MoviesResults

val IMAGE_BASE_URL = "https://image.tmdb.org/t/p/w500"

class MoviesListAdapter constructor(private val listener: OnItemClickListener) :
     ListAdapter<MoviesResults.Movies, MoviesListAdapter.MoviesListViewHolder>(DiffCallback) {

    private lateinit var fav: MoviesFavorites





    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoviesListViewHolder {
        val binding = MovieLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)


        return MoviesListViewHolder(binding)
    }


    override fun onBindViewHolder(holder: MoviesListViewHolder, position: Int) {
        val currentItem = getItem(position)

        holder.binding.favoritesCheckbox.isChecked = currentItem.isFavorite
        holder.binding.favoritesCheckbox.setOnCheckedChangeListener { _, isChecked ->
            currentItem.isFavorite

        }

        if(holder.binding.favoritesCheckbox.isChecked ) {
            currentItem.isFavorite = true

        }


        if (currentItem != null) {
            holder.bind(currentItem)

        }







    }

    inner class MoviesListViewHolder(val binding: MovieLayoutBinding) :
        RecyclerView.ViewHolder(binding.root) {




        init {
            binding.root.setOnClickListener {
                val position = absoluteAdapterPosition
                if (position != RecyclerView.NO_POSITION) {
                    val item = getItem(position)
                    listener.onItemClick(item)
                }
            }
        }

        init {

            binding.favoritesCheckbox.setOnClickListener{

                if(binding.favoritesCheckbox.isChecked) {

                    val position = absoluteAdapterPosition
                    if (position != RecyclerView.NO_POSITION) {
                        val item = getItem(position)
                        item.isFavorite = true
                        fav = MoviesFavorites(item.title, item.id, item.release_date, item.overview, item.vote_average, item.poster_path, item.original_language, item.isFavorite)
                        listener.onFavoriteClick(fav)
                        listener.onCheckboxClick(binding.favoritesCheckbox.isChecked)

                    }

                    showToast("${fav.title} is added to your favorites")





                }
                else {
                    val position = absoluteAdapterPosition
                    if (position != RecyclerView.NO_POSITION) {
                        val item = getItem(position)
                        item.isFavorite = false
                         fav = MoviesFavorites(item.title, item.id, item.release_date, item.overview, item.vote_average, item.poster_path, item.original_language, item.isFavorite)
                        listener.onDeleteClick(fav)
                        listener.onCheckboxClick(binding.favoritesCheckbox.isChecked)
                    }


                    showToast("${fav.title} is removed from your favorites")
                }

            }



        }



        fun bind(movie: MoviesResults.Movies) {
            binding.apply {
                movieTitle.text = movie.title
                movieRating.text = movie.vote_average
                movieYear.text = movie.release_date
                Glide.with(itemView)
                    .load(IMAGE_BASE_URL + movie.poster_path)
                    .centerCrop()
                    .error(R.drawable.ic_baseline_error_outline_24)
                    .into(movieImage)
               val item = getItem(absoluteAdapterPosition)
                favoritesCheckbox.isChecked = item.isFavorite






            }

        }


        private fun showToast(string: String) {
            Toast.makeText(itemView.context, string, Toast.LENGTH_SHORT).show()

        }


    }


    interface OnItemClickListener {
        fun onItemClick(movie: MoviesResults.Movies)
        fun onFavoriteClick(favorites: MoviesFavorites)
        fun onDeleteClick(favorites: MoviesFavorites)
        fun onCheckboxClick(fav: Boolean)
    }







    companion object DiffCallback : DiffUtil.ItemCallback<MoviesResults.Movies>() {
        override fun areItemsTheSame(
            oldItem: MoviesResults.Movies,
            newItem: MoviesResults.Movies
        ): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(
            oldItem: MoviesResults.Movies,
            newItem: MoviesResults.Movies
        ): Boolean {
            return oldItem == newItem
        }
    }


}







MoviesDetailsFragment.kt


package com.example.moviesapp.ui.Fragments

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.navArgs
import com.bumptech.glide.Glide
import com.example.moviesapp.R
import com.example.moviesapp.databinding.FragmentMoviesDetailsBinding
import com.example.moviesapp.network.MoviesFavorites
import com.example.moviesapp.network.MoviesResults
import com.example.moviesapp.ui.DaoViewModel
import com.example.moviesapp.ui.IMAGE_BASE_URL
import com.example.moviesapp.ui.SharedViewModel
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MoviesDetailsFragment() : Fragment(R.layout.fragment_movies_details) {


    //We can get the movies from the args property
    private val args by navArgs<MoviesDetailsFragmentArgs>()
    private val daoViewModel by viewModels<DaoViewModel>()
    private val sharedViewModel by viewModels<SharedViewModel>()
    private fun showToast(string: String) {
        Toast.makeText(view?.context, string, Toast.LENGTH_SHORT).show()

    }

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

        val binding = FragmentMoviesDetailsBinding.bind(view)


        sharedViewModel.checkBox.observe(viewLifecycleOwner) {
            binding.favCheckbox.isChecked = it

        }



        binding.apply {
            val movie: MoviesResults.Movies = args.movie
            val fav = MoviesFavorites(
                movie.title,
                movie.id,
                movie.release_date,
                movie.overview,
                movie.vote_average,
                movie.poster_path,
                movie.original_language,
                movie.isFavorite,
            )
            //When you are in fragment/activity, pass it to a glide.with because view is less efficient
            Glide.with(this@MoviesDetailsFragment)
                .load(IMAGE_BASE_URL + movie.poster_path)
                //Have the textview visible only when image is visible
                .error(R.drawable.ic_baseline_error_outline_24)
                .fitCenter()
                .into(coverPhoto)

            title.text = movie.title
            releaseDate.text = movie.release_date
            language.text = movie.original_language
            rating.text = movie.vote_average
            plot.text = movie.overview





            favCheckbox.setOnClickListener {


                if (favCheckbox.isChecked) {


                    fav.isFavorite = true
                    daoViewModel.addMovieToFavs(fav)
                    showToast("${fav.title} is added to your favorites")

                } else {


                    fav.isFavorite = false
                    daoViewModel.deleteMovieFromFavs(fav)
                    showToast("${fav.title} is removed from your favorites")


                }


            }
        }


    }


}


SharedViewModel.kt

package com.example.moviesapp.ui

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider



class SharedViewModel: ViewModel() {


    val checkBox = MutableLiveData<Boolean>()


    fun sendValue(favorite: Boolean) {
        checkBox.value = favorite

    }

    class SharedViewModelFactor(

    ) : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(SharedViewModel::class.java)) {
                @Suppress("UNCHECKED_CAST")
                return SharedViewModel() as T
            }
            throw IllegalArgumentException("Unknown ViewModel class")

        }


    }








}


标签: androidkotlinandroid-fragmentsandroid-recyclerview

解决方案


您可以使用 2 种方法来执行此操作:
1)您可以使用LocalBroadcast通知一个片段/活动在另一个片段/活动中发生变化。
注意LocalBroadcast现在已弃用。或者,您可以使用事件总线在片段之间进行通信

  1. 创建本地广播

     private BroadcastReceiver onNotice= new BroadcastReceiver() {
    
       @Override
       public void onReceive(Context context, Intent intent) {
         // intent can contain anydata
         Log.d(TAG,"onReceive called");
       }
     };
    
  2. 在片段的 onResume 中注册您的接收器,例如:

    public void onResume() {
         super.onResume();
         IntentFilter iff= new IntentFilter(MyIntentService.ACTION);
         LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, iff);
     }
    
  3. 在 onPause 中取消注册接收器:

     public void onPause() {    
       super.onPause();    
       LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice);    
     }
    

更多信息可以参考:
https ://blog.mindorks.com/using-localbroadcastmanager-in-android

2)可以LiveData用来观察一个fragment在另一个fragment中的数据变化

  1. 创建共享 ViewModel

    public class SharedViewModel extends ViewModel {
    
    private MutableLiveData<String> name;
    
    public void setNameData(String nameData) {
        name.setValue(nameData);
    }
    
    public MutableLiveData<String> getNameData() {
        if (name == null) {
            name = new MutableLiveData<>();
        }
    
        return name;
    }
    }
    
  2. 片段一

     private SharedViewModel sharedViewModel;
    
     public FragmentOne() {
    
     }
    
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
    
         sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
         submitButton.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View view) {
    
                sharedViewModel.setNameData(submitText.getText().toString());
               }
          });
    
     }
    
  3. 片段二

     private SharedViewModel sharedViewModel;
    
     public FragmentTwo() {
    
     }
    
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
    
         sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
    
         sharedViewModel.getNameData().observe(this, nameObserver);
     }
    
     Observer<String> nameObserver = new Observer<String>() {
        @Override
        public void onChanged(String name) {
           receivedText.setText(name);
        }
     };
    

有关 viewmodel 的更多详细信息,您可以参考:

https://nabeelj.medium.com/android-how-to-share-data-between-fragments-using-viewmodel-and-livedata-android-mvvm-9fc463af5152

https://developer.android.com/guide/fragments/communicate#fragments


推荐阅读