現在、recyclerview 内の各項目にチェックボックスがあるアプリを作成しています。ユーザーはチェックボックスをクリックして、特定のムービーを保存できます。
Jetpack Datastore を使用して、チェックボックスの状態を保存したいと考えています。設定マネージャーをセットアップしましたが、フラグメントはチェックボックスへの参照を保持していないため、フラグメントで苦労しています (チェックボックスは項目レイアウトにあります)。
以下に、設定マネージャー クラス、daoviewmodel (API からデータを取得するために別のビューモデルを使用しています)、および recyclerview を表示するフラグメントの 1 つがあります。
どんな助けでも大歓迎です。ありがとうございました!
PreferencesManager.kt
package com.example.moviesapp.network
import android.content.Context
import android.util.Log
import androidx.datastore.preferences.createDataStore
import androidx.datastore.preferences.edit
import androidx.datastore.preferences.emptyPreferences
import androidx.datastore.preferences.preferencesKey
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton
private const val TAG = "PreferencesManager"
data class FilterPreferences(val favorite: Boolean)
@Singleton
class PreferencesManager @Inject constructor(@ApplicationContext context: Context) {
private val dataStore = context.createDataStore("user_preferences")
val preferencesFlow = dataStore.data
.catch { exception ->
if (exception is IOException) {
Log.e(TAG, "Error reading preferences", exception)
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
val favorite = preferences[PreferencesKeys.FAVORITES] ?: false
FilterPreferences(favorite)
}
suspend fun updatefavorite(favorite: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.FAVORITES] = favorite
}
}
private object PreferencesKeys {
val FAVORITES = preferencesKey<Boolean>("favorites")
}
}
DaoViewModel.kt
package com.example.moviesapp.ui
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.moviesapp.network.MoviesFavoritesRepository
import com.example.moviesapp.network.MoviesResults
import com.example.moviesapp.network.PreferencesManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class DaoViewModel @Inject constructor(private val repository: MoviesFavoritesRepository,
private val preferencesManager: PreferencesManager) :
ViewModel() {
val preferencesFlow = preferencesManager.preferencesFlow
fun onFavorite(favorite: Boolean) {
viewModelScope.launch {
preferencesManager.updatefavorite(favorite)
}
}
fun addMovieToFavs(movie: MoviesResults.Movies) {
viewModelScope.launch {
repository.insertFavorite(movie)
}
}
fun getFavorites(): LiveData<List<MoviesResults.Movies>> {
return repository.getFavorites()
}
}
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.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)
var status: MovieApiStatus? = MovieApiStatus.LOADING
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(movie: MoviesResults.Movies, isChecked: Boolean) {
daoViewModel.addMovieToFavs(movie)
}
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
}
}