ybybyb
ybybyb

Reputation: 1739

Is it bad code to forcibly observe LiveData by saving itself again?

enter image description here

I am using nested recyclerview.

In the picture, the red box is the Routine Item (Parent Item), and the blue box is the Detail Item (Child Item) in the Routine Item.

You can add a parent item dynamically by clicking the ADD ROUTINE button.

Similarly, child items can be added dynamically by clicking the ADD button of the parent item.

As a result, this function works just fine.

But the problem is in the code I wrote.

I use a ViewModel to observe and update parent item addition/deletion.

However, it does not observe changes in the detail item within the parent item.

I think it's because LiveData only detects additions and deletions to the List.

So I put _items.value = _items.value code to make it observable when child items are added and deleted.

This way, I didn't even have to use update code like notifyDataSetChanged() in the child adapter. In the end it is a success, but I don't know if this is the correct code.

Let me know if you have additional code you want!

In Fragment.kt

class WriteRoutineFragment : Fragment() {
    private var _binding : FragmentWriteRoutineBinding? = null
    private val binding get() = _binding!!
    private lateinit var adapter : RoutineAdapter
    private val vm : WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        _binding = FragmentWriteRoutineBinding.inflate(inflater, container, false)

        adapter = RoutineAdapter(::addDetail, ::deleteDetail)
        binding.rv.adapter = this.adapter
        return binding.root
    }

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

        // RecyclerView Update
        vm.items.observe(viewLifecycleOwner) { updatedItems ->
            adapter.setItems(updatedItems)
        }
    }

    private fun getTabPageResult() {
        val navController = findNavController()
        navController.currentBackStackEntry?.also { stack ->
            stack.savedStateHandle.getLiveData<String>("workout")?.observe(
                viewLifecycleOwner, Observer { result ->
                    vm.addRoutine(result) // ADD ROUTINE
                    stack.savedStateHandle?.remove<String>("workout")
                }
            )
        }
    }

    private fun addDetail(pos: Int) {
        vm.addDetail(pos)
    }

    private fun deleteDetail(pos: Int) {
        vm.deleteDetail(pos)
    }
}

ViewModel

class WriteRoutineViewModel : ViewModel() {
    private var _items: MutableLiveData<ArrayList<RoutineModel>> = MutableLiveData(arrayListOf())

    val items: LiveData<ArrayList<RoutineModel>> = _items

    fun addRoutine(workout: String) {
        val item = RoutineModel(workout, "TEST")
        _items.value?.add(item)
//        _items.value = _items.value
    }

    fun addDetail(pos: Int) {
        val detail = RoutineDetailModel("TEST", "TEST")
        _items.value?.get(pos)?.addSubItem(detail) // Changing the parent item's details cannot be observed by LiveData.
        _items.value = _items.value // is this right way?
    }

    fun deleteDetail(pos: Int) {
        if(_items.value?.get(pos)?.getSubItemSize()!! > 1)
            _items.value?.get(pos)?.deleteSubItem() // is this right way?
        else
            _items.value?.removeAt(pos)
        _items.value = _items.value // is this right way?

    }
}

Upvotes: 0

Views: 189

Answers (1)

Tenfour04
Tenfour04

Reputation: 93609

This is pretty standard practice when using a LiveData with a mutable List type. The code looks like a smell, but it is so common that I think it's acceptable and people who understand LiveData will understand what your code is doing.

However, I much prefer using read-only Lists and immutable model objects if they will be used with RecyclerViews. It's less error prone, and it's necessary if you want to use ListAdapter, which is much better for performance than a regular Adapter. Your current code reloads the entire list into the RecyclerView every time there is any change, which can make your UI feel laggy. ListAdapter analyzes automatically on a background thread your List for which items specifically changed and only rebinds the changed items. But it requires a brand new List instance each time there is a change, so it makes sense to only use read-only Lists if you want to support using it.

Upvotes: 1

Related Questions