How To Observe LiveData In RecyclerView Adapter In MVVM Architecture?
I have a RecyclerView
adapter and a button in its items.
When I click on the button, I want to remove its item from server and after that from RecyclerView
.
I want to do that by observing LiveData
(when it removed from server I have to remove it from recycler view thus I need the result of server)
What is the best practice way to do that - I must observe in the fragment and pass a listener to the adapter and implement that in the fragment and when user clicked on the button call a method in fragment or there is a better way to do that?
Answer
After a full searching in several posts, finally, I found the recommended solution. Step 1: declare an interface in your adapter as below:
class AddExpenseLabelAdapter(
val items: List<LabelResponse>,
val context: Context,
val listener: OnLabelClickListener
) : RecyclerView.Adapter<AddExpenseLabelAdapter.ViewHolder>() {
interface OnLabelClickListener {
fun onLabelDeleteButtonClicked(request : SubCategoryLabelRequest)
}
lateinit var binding: ItemListExpenseAddLabelBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(context)
val binding = ItemListExpenseAddLabelBinding.inflate(inflater)
this.binding = binding
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount(): Int = items.size
inner class ViewHolder(val binding: ItemListExpenseAddLabelBinding) : RecyclerView.ViewHolder(binding.root), OnClickListener {
lateinit var item: LabelResponse
fun bind(item: LabelResponse) {
this.item = item
binding.itemListLabelLayout.setBackgroundColor(Color.parseColor("#" + item.color))
binding.labelResponse = item
binding.onClickListener = this
binding.executePendingBindings()
}
override fun onClick(view: View) {
if (view.id == binding.itemListLabelLayout.id) {
val subCategoryLabelRequest = SubCategoryLabelRequest(item.id)
listener.onLabelDeleteButtonClicked(subCategoryLabelRequest)
}
}
}
}
step 2: implement the interface in your view and pass it to your adapter like this:
class AddExpenseLabelDialog : DialogFragment(), AddExpenseLabelAdapter.OnLabelClickListener {
lateinit var binding: DialogAddExpenseLabelBinding
lateinit var view: Any
var expenseId: Int = 0
var categoryId: Int = 0
lateinit var application: MyApplication
lateinit var addExpenseLabelViewModel: AddExpenseLabelViewModel
fun newInstance(expenseId: Int, categoryId: Int): AddExpenseLabelDialog =
AddExpenseLabelDialog().also { fragment ->
arguments = Bundle().also { bundle ->
bundle.putInt("expenseId", expenseId)
bundle.putInt("categoryId", categoryId)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(layoutInflater, R.layout.dialog_add_expense_label, container, false)
addExpenseLabelViewModel = ViewModelProviders.of(this).get(AddExpenseLabelViewModel::class.java)
expenseId = arguments!!.getInt("expenseId")
categoryId = arguments!!.getInt("categoryId")
initialize()
view = binding.root
return view as View
}
fun initialize() {
binding.labelRec.layoutManager = LinearLayoutManager(context)
addExpenseLabelViewModel.liveData.observe(this, Observer { response ->
binding.labelRec.adapter = AddExpenseLabelAdapter(response as ArrayList<LabelResponse>, context!!, this)
})
}
override fun onLabelDeleteButtonClicked(request : SubCategoryLabelRequest) {
addExpenseLabelViewModel.createExpenseLabel(categoryId, expenseId, request).observe(this, Observer { response ->
when (response?.status) {
Status.LOADING -> Toast.makeText(activity, "LOADING", Toast.LENGTH_SHORT).show()
Status.SUCCESS -> {
dismiss()
Toast.makeText(activity, "SUCCESS", Toast.LENGTH_SHORT).show()
}
else -> Toast.makeText(activity, InjectorUtil.convertCodeToMessage(response?.error?.code!!), Toast.LENGTH_SHORT).show()
}
})
}
}
Related Questions
- → should I choose reactjs+f7 or f7+vue.js?
- → Phonegap Android write to sd card
- → Local reference jquery script in nanohttpd (Android)
- → Click to navigate on mobile devices
- → How to allow api access to android or ios app only(laravel)?
- → Access the Camera and CameraRoll on Android using React Native?
- → React native change listening port
- → What is the default unit of style in React Native?
- → Google play market autocomplete icon
- → Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `ListView`
- → Using Laravel with Genymotion
- → react native using like web-based ajax function
- → react native pdf View