WBLord
WBLord

Reputation: 1025

How to get element from MutableLiveData

First I'll give you an example of my code and describe the problem below.

MainViewModel

class MainViewModel @ViewModelInject constructor(private val ordersRepository: OrdersRepository) :
        ViewModel() {

    private val _orderList = MutableLiveData<State<List<Order>>>()
    val orderList: LiveData<State<List<Order>>>
        get() = _orderList

    fun getOrders() {
        viewModelScope.launch {
            ordersRepository.getAllOrders().collect {
                Log.d("test2",it.toString())
                _orderList.value = it
            }
        }
    }
}

HomeItemAdapter

class HomeItemAdapter(private val viewModel: MainViewModel) : ListAdapter < Order,
HomeItemAdapter.ViewHolder > (HomeItemDiffCallback()) {

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : ViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return ViewHolder(inflater.inflate(R.layout.item_main, parent, false))
  }

  override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.bind(position)
  }

  inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
    private val title: TextView = view.findViewById(R.id.item_title)

    fun bind(pos: Int) {
      var value = viewModel.orderList.value
      Log.d("test", value.toString())
      title.text = viewModel.orderList.value.data toString()

      view.setOnClickListener {
        view.findNavController().navigate(R.id.action_homeFragment_to_orderFragment, setupInputData2(adapterPosition).arguments)
      }
    }
  }
  private fun setupInputData2(adapterPosition: Int) : NavDirections {
    return HomeFragmentDirections.actionHomeFragmentToOrderFragment(adapterPosition)
  }

  class HomeItemDiffCallback: DiffUtil.ItemCallback < Order > () {

    override fun areItemsTheSame(oldItem: Order, newItem: Order) : Boolean = oldItem == newItem

    override fun areContentsTheSame(oldItem: Order, newItem: Order) : Boolean = oldItem == newItem
  }
}

HomeFragment

@ExperimentalCoroutinesApi
@AndroidEntryPoint
class HomeFragment : BaseFragment<MainViewModel, FrgMainBinding>() {
    override val mViewModel: MainViewModel by viewModels()

    private lateinit var recycler: RecyclerView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.frg_main, container, false)
    }

    private fun getOrders() {
        mViewModel.getOrders()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.let {
             setRecyclerItemsObserver()
        }
    }


    private fun initOrders() {
        mViewModel.orderList.observe(this) { state ->
            when (state) {
                is State.Loading -> showLoading(true)
                is State.Success -> {
                    if (state.data.isNotEmpty()) {
                        (recycler.adapter as? HomeItemAdapter)?.submitList(state.data.toMutableList())
                        showLoading(false)
                    }
                }
                is State.Error -> {
                    showToast(state.message)
                    showLoading(false)
                }
            }
        }


        //TODO
        /*swipeRefreshLayout.setOnRefreshListener {
            getOrders()
        }*/

        // If State isn't `Success` then reload posts.
        if (mViewModel.orderList.value !is State.Success) {
            getOrders()
        }
    }

    private fun showLoading(isLoading: Boolean) {
        // swipeRefreshLayout.isRefreshing = isLoading
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // set fab listener to start an action
        view.addNewButton.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_create, null))

        recycler = view.recycler
        setupRecycler()
    }

    private fun setRecyclerItemsObserver() {
        initOrders()
    }

    private fun setupRecycler() {
        recycler.layoutManager = LinearLayoutManager(context)
        val itemDecor = DividerItemDecoration(context, RecyclerView.VERTICAL)
        recycler.addItemDecoration(itemDecor)
        recycler.adapter = HomeItemAdapter(mViewModel)
    }

    override fun getViewBinding(): FrgMainBinding {
        return FrgMainBinding.inflate(layoutInflater)
    }
}

My question

My problem occurs when creating a Holder by Recycle View.

  1. How can I fill the RecycleView from MutableLiveData<State<List>>.
  2. How to get an element from MutableLiveData to get its fields .

I will be glad to see an example of the code on kotlin to solve my problem Thanks!

Updated:

My problem is that I can't bind the object. Because I used to get it from the list and could do


private var title: TextView = view. findViewById(R. id.item_title)
title = list Product[position]. title 

and now I can't get its object!

Update v2 with error enter image description here enter image description here

Upvotes: 1

Views: 1272

Answers (2)

user14653807
user14653807

Reputation:

Use this fragment

inner class YouViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
    private val title: TextView = view.findViewById(R.id.product_name)
    private val count: TextView = view.findViewById(R.id.product_count)
    private val price: TextView = view.findViewById(R.id.product_price)

    fun bind(pos: Int) {
        title.text = mOrders?.get(pos)?.name
        price.text = mOrders?.get(pos)?.price
        count.text = pos.toString()

    }
}

Upvotes: 0

Sarah Khan
Sarah Khan

Reputation: 866

Add a parameter to your adapter's constructor of the type List which is the initial list that you want to populate your RecyclerView with. submitList is a method that is called when in the course of lifecycle of the adpater, you want to send updated list to the adapter. (Say when your list changes)

class HomeItemAdapter() : ListAdapter<Order,
HomeItemAdapter.ViewHolder> (HomeItemDiffCallback()) {
    private var mOrders: MutableList<Order?>? = null
    private val mViewModel : MainViewModel
    constructor(private val viewModel: MainViewModel, listOrders : MutableList<Order?>?) : this() {
        mOrders = listOrders
        mViewModel = viewModel
    }
    
    fun submitList(newOrders: List<Order>) {
        val diffCallBack = HomeItemDiffCallback(this.mOrders, new Orders)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        this.mOrders.clear()
        this.mOrders.addAll(newOrders)
        diffResult.dispatchUpdatesTo(this)
    }

Your callback

class HomeItemDiffCallback(
    private val oldList: List<Order>,
    private val newList: List<Order>
) : DiffUtil.ItemCallback() {

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].id == newList[newItemPosition].id
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
    }

}

In my view in your HomeFragment, you should first call setupRecycler() and then setRecyclerItemsObserver()

While setting up a recycler if you don't have a list you want to pass, you can simple send null here,

recycler.adapter = HomeItemAdapter(mViewModel, null)

When you receive the list in initOrders(), do what you are currently doing i.e

(recycler.adapter as? HomeItemAdapter)?.submitList(state.data.toMutableList())

Upvotes: 1

Related Questions