在我的 MenuFragment 中,我显示可以添加到购物车的产品,当我单击日志中的任何产品时,我看到该产品已添加到列表中并且列表已更新,但是当我转到 CartFragment 时,没有显示任何内容,在日志中我看到观察者观察到一个空列表并将其传递给适配器。
package com.example.restauratio.cart
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.restauratio.R
import com.example.restauratio.databinding.FragmentCartBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class CartFragment : Fragment() {
private val cartViewModel: CartViewModel by viewModels()
private var _binding: FragmentCartBinding? = null
private val binding get() = _binding!!
private lateinit var cartAdapter: CartAdapter
private val actionCartToPop = R.id.action_cartFragment_pop
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentCartBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
cartAdapter = CartAdapter(cartViewModel)
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = cartAdapter
cartViewModel.cartItems.observe(viewLifecycleOwner) { cartItems ->
Log.d("CartFragment", "Observed cart items: $cartItems")
cartAdapter.setCartItems(cartItems)
}
binding.imageView4.setOnClickListener {
findNavController().navigate(actionCartToPop)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
package com.example.restauratio.cart
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.restauratio.menu.DishModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class CartViewModel @Inject constructor() : ViewModel() {
private val _cartItems = MutableLiveData<List<DishModel>>()
val cartItems: LiveData<List<DishModel>> get() = _cartItems
fun addToCart(dish: DishModel) {
Log.d("CartViewModel", "Adding to cart: $dish")
val currentItems = _cartItems.value.orEmpty().toMutableList()
val existingDish = currentItems.find { it.id == dish.id }
if (existingDish != null) {
existingDish.quantity += 1
} else {
currentItems.add(dish.copy(quantity = 1))
}
_cartItems.value = currentItems
Log.d("CartViewModel", "Updated cart items: ${_cartItems.value}")
}
fun removeFromCart(cartItem: DishModel) {
Log.d("CartViewModel", "Removing from cart: $cartItem")
val currentCartItems = _cartItems.value.orEmpty().toMutableList()
currentCartItems.remove(cartItem)
_cartItems.value = currentCartItems
}
fun clearCart() {
Log.d("CartViewModel", "Clearing cart")
_cartItems.value = emptyList()
}
}
package com.example.restauratio.cart
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.example.restauratio.databinding.CartItemBinding
import com.example.restauratio.menu.DishModel
import de.hdodenhof.circleimageview.CircleImageView
class CartAdapter(
private val cartViewModel: CartViewModel
) : RecyclerView.Adapter<CartAdapter.CartViewHolder>() {
private var cartItems: List<DishModel> = emptyList()
inner class CartViewHolder(binding: CartItemBinding) : RecyclerView.ViewHolder(binding.root) {
val dishImage: CircleImageView = binding.imageView2
val dishName: TextView = binding.textView2
val dishPrice: TextView = binding.textView
val quantityTextView: TextView = binding.textView55
val removeFromCartButton: ImageView = binding.imageView8
init {
removeFromCartButton.setOnClickListener {
val removedDish = cartItems[adapterPosition]
cartViewModel.removeFromCart(removedDish)
Toast.makeText(itemView.context, "Danie usunięte z koszyka", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder {
val inflater = LayoutInflater.from(parent.context)
val cartItemBinding = CartItemBinding.inflate(inflater, parent, false)
return CartViewHolder(cartItemBinding)
}
override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
val cartItem = cartItems[position]
holder.dishName.text = cartItem.name
holder.dishPrice.text = String.format("%.2f zł", cartItem.price)
holder.quantityTextView.text = cartItem.quantity.toString()
}
override fun getItemCount(): Int {
return cartItems.size
}
fun setCartItems(newCartItems: List<DishModel>) {
Log.d("CartAdapter", "Setting cart items: $cartItems")
cartItems = newCartItems
notifyDataSetChanged()
}
}
package com.example.restauratio.menu
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.core.view.GravityCompat
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.restauratio.R
import com.example.restauratio.cart.CartFragment
import com.example.restauratio.cart.CartViewModel
import com.example.restauratio.databinding.FragmentMenuBinding
import com.example.restauratio.loginSession.SessionManager
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class MenuFragment : Fragment() {
@Inject
lateinit var sessionManager: SessionManager
private val menuViewModel: MenuViewModel by viewModels()
private val cartViewModel: CartViewModel by viewModels()
private var _binding: FragmentMenuBinding? = null
private val binding get() = _binding!!
private lateinit var dishAdapter: DishAdapter
private val actionMenuToAboutUs = R.id.action_menu_to_aboutUs
private val actionMenuToReservation = R.id.action_menu_to_reservationView
private val actionMenuToRules = R.id.action_menu_to_rulesView
private val actionMenuToPrivacyPolicy = R.id.action_menu_to_privacyPolicy
private val actionLogout = R.id.action_menu_pop
private val actionMenuToAlerts = R.id.action_menu_to_alerts
private val actionMenuToProfile = R.id.action_menu_to_profile
private val actionMenuToOrders = R.id.action_menu_to_orders
private val actionMenuToCart = R.id.action_menu_to_cartFragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMenuBinding.inflate(inflater, container, false)
val drawerLayout = binding.drawerLayout
val mainView: View = binding.root
mainView.setOnClickListener {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START)
}
}
binding.hamburgerButton.setOnClickListener {
onHamburgerButtonClick()
}
binding.imageView6.setOnClickListener{
findNavController().navigate(actionMenuToAlerts)
}
binding.imageView2.setOnClickListener{
findNavController().navigate(actionMenuToProfile)
}
binding.imageView7.setOnClickListener{
findNavController().navigate(actionMenuToOrders)
}
binding.imageView.setOnClickListener {
findNavController().navigate(actionMenuToCart)
}
binding.navigationView.setNavigationItemSelectedListener { menuItem ->
when (menuItem.itemId) {
R.id.about_us -> {
findNavController().navigate(actionMenuToAboutUs)
}
R.id.reservation -> {
findNavController().navigate(actionMenuToReservation)
}
R.id.rules -> {
findNavController().navigate(actionMenuToRules)
}
R.id.privacy_policy -> {
findNavController().navigate(actionMenuToPrivacyPolicy)
}
R.id.logout -> {
sessionManager.logout()
findNavController().navigate(actionLogout)
}
}
binding.drawerLayout.closeDrawer(GravityCompat.START)
true
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dishAdapter = DishAdapter(cartViewModel)
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = dishAdapter
menuViewModel.dishes.observe(viewLifecycleOwner) { dishes ->
dishAdapter.setDishes(dishes)
}
menuViewModel.loadDishes(categoryId = null, name = null)
binding.textInputLayout3.editText?.addTextChangedListener { text ->
val searchQuery = text.toString().trim()
menuViewModel.loadDishes(categoryId = null, name = null, searchQuery = searchQuery)
}
}
private fun onHamburgerButtonClick() {
val drawerLayout = binding.drawerLayout
if (!drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.openDrawer(GravityCompat.START)
} else {
drawerLayout.closeDrawer(GravityCompat.START)
}
}
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (sessionManager.isLoggedIn()) {
requireActivity().finish()
} else {
findNavController().popBackStack()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
package com.example.restauratio.menu
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.restauratio.request.AuthService
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class MenuViewModel @Inject constructor(
private val authService: AuthService
) : ViewModel() {
private val _dishes = MutableLiveData<List<DishModel>>()
val dishes: LiveData<List<DishModel>> get() = _dishes
fun loadDishes(
categoryId: Int?,
name: String?,
searchQuery: String? = null
) {
viewModelScope.launch {
try {
val dishesResponse = authService.getDishes(DishRequest(categoryId, name))
if (dishesResponse.isSuccessful) {
val allDishes = dishesResponse.body()?.dishes ?: emptyList()
val filteredDishes = if (!searchQuery.isNullOrBlank()) {
allDishes.filter { dish -> dish.name.contains(searchQuery, ignoreCase = true) }
} else {
allDishes
}
_dishes.value = filteredDishes
} else {
Log.e("MenuViewModel", "Failed to fetch dishes. Error code: ${dishesResponse.code()}")
}
} catch (e: Exception) {
Log.e("MenuViewModel", "An error occurred while fetching dishes", e)
}
}
}
}
package com.example.restauratio.menu
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.example.restauratio.cart.CartViewModel
import com.example.restauratio.databinding.MenuItemBinding
import de.hdodenhof.circleimageview.CircleImageView
class DishAdapter(
private val cartViewModel: CartViewModel
) : RecyclerView.Adapter<DishAdapter.DishViewHolder>() {
private var dishes: List<DishModel> = emptyList()
inner class DishViewHolder(binding: MenuItemBinding) : RecyclerView.ViewHolder(binding.root) {
val dishImage: CircleImageView = binding.imageView2
val dishName: TextView = binding.textView2
val dishPrice: TextView = binding.textView
val addToCartButton: ImageView = binding.imageView8
init {
addToCartButton.setOnClickListener {
val clickedDish = dishes[adapterPosition]
cartViewModel.addToCart(clickedDish)
Toast.makeText(itemView.context, "Danie dodane do koszyka", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DishViewHolder {
val inflater = LayoutInflater.from(parent.context)
val menuItemBinding = MenuItemBinding.inflate(inflater, parent, false)
return DishViewHolder(menuItemBinding)
}
override fun onBindViewHolder(holder: DishViewHolder, position: Int) {
val dish = dishes[position]
holder.dishName.text = dish.name
holder.dishPrice.text = String.format("%.2f zł", dish.price)
}
override fun getItemCount(): Int {
return dishes.size
}
fun setDishes(newDishes: List<DishModel>) {
dishes = newDishes
notifyDataSetChanged()
}
}
package com.example.restauratio.menu
data class DishModel(
val id: Int,
val name: String,
val description: String,
val price: Double,
val dishCategoryIds: List<Int>,
var quantity: Int = 0
)
data class DishRequest(
val categoryId: Int?,
val name: String?
)
data class DishResponse(
val dishes: List<DishModel>,
val total: Int
)
不知道为什么会这样
当您切换到 CartFragment 时,会创建一个新的fragment 实例,同时也会创建一个新的 viewmodel 实例。为了解决这个问题,引入了共享视图模型的概念,使用
activityViewModels
而不是 viewModels
。
activityViewModels
与托管片段的活动相关联,而 viewModels
与每个片段相关联。以下是代码外观的示例:
CartFragment
@AndroidEntryPoint
class CartFragment : Fragment() {
private val cartViewModel: CartViewModel by activityViewModels()
....
}
MenuFragment
@AndroidEntryPoint
class MenuFragment : Fragment() {
...
private val cartViewModel: CartViewModel by activityViewModels()
...
要了解更多信息,请查看此 Codelab - 跨片段共享视图模型
如果您将数据保存到 db 或 api,则另一种选择是添加一个新函数来获取数据并更新 observable,然后在 CartFragment onviewCreated 上调用该函数。