android - 一个复选框触发不同布局中的另一个复选框
问题描述
我目前正在编写一个显示电影列表的应用程序。我有许多显示包含电影的卡片视图的片段,每个卡片视图都有一个复选框。用户可以按下卡片视图以转到存在另一个复选框的电影的详细信息页面。
这两个复选框的目标是将电影添加到收藏夹选项卡。
我的问题是,当用户检查卡片视图中的复选框时,如何检查详细信息页面内的复选框?
下面是相关代码。
感谢我能得到的所有帮助。
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")
}
}
}
解决方案
您可以使用 2 种方法来执行此操作:
1)您可以使用LocalBroadcast
通知一个片段/活动在另一个片段/活动中发生变化。
注意:LocalBroadcast
现在已弃用。或者,您可以使用事件总线在片段之间进行通信
创建本地广播
private BroadcastReceiver onNotice= new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // intent can contain anydata Log.d(TAG,"onReceive called"); } };
在片段的 onResume 中注册您的接收器,例如:
public void onResume() { super.onResume(); IntentFilter iff= new IntentFilter(MyIntentService.ACTION); LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, iff); }
在 onPause 中取消注册接收器:
public void onPause() { super.onPause(); LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice); }
更多信息可以参考:
https ://blog.mindorks.com/using-localbroadcastmanager-in-android
2)可以LiveData
用来观察一个fragment在另一个fragment中的数据变化
创建共享 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; } }
片段一
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()); } }); }
片段二
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://developer.android.com/guide/fragments/communicate#fragments
推荐阅读
- javascript - NUXT 应用程序设置中的 Google 登录不起作用
- flutter - 如何在颤动的 Web http 请求中添加标头?
- r - 在受控的中断时间序列中平衡 1 个处理组和 2 个对照组之间的协变量以进行下游分析
- python - 分组附近的轮廓/边界矩形
- javascript - 当表达式作为参数 Javascript 发送时,运算符会丢失
- python - 我如何用python制作一个二维码(包含日历数据类型)
- vbscript - 在 VBScript 中使用 NTLM 身份验证发送请求
- c# - 如何在不读取所有数据的情况下找到最大行和列?
- mysql - 使用唯一信息创建表
- scala - 如何实现自定义序列化程序