Final Results

RosterRowHolder should resemble:

package com.commonsware.todo

import androidx.recyclerview.widget.RecyclerView
import com.commonsware.todo.databinding.TodoRowBinding

class RosterRowHolder(
  private val binding: TodoRowBinding,
  val onCheckboxToggle: (ToDoModel) -> Unit
) :
  RecyclerView.ViewHolder(binding.root) {

  fun bind(model: ToDoModel) {
    binding.apply {
      isCompleted.isChecked = model.isCompleted
      isCompleted.setOnCheckedChangeListener { _, _ -> onCheckboxToggle(model) }
      desc.text = model.description
    }
  }
}

RosterAdapter should look like:

package com.commonsware.todo

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.commonsware.todo.databinding.TodoRowBinding

class RosterAdapter(
  private val inflater: LayoutInflater,
  private val onCheckboxToggle: (ToDoModel) -> Unit
) :
  ListAdapter<ToDoModel, RosterRowHolder>(DiffCallback) {
  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
    RosterRowHolder(
      TodoRowBinding.inflate(inflater, parent, false),
      onCheckboxToggle
    )

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

private object DiffCallback : DiffUtil.ItemCallback<ToDoModel>() {
  override fun areItemsTheSame(oldItem: ToDoModel, newItem: ToDoModel) =
    oldItem.id == newItem.id

  override fun areContentsTheSame(oldItem: ToDoModel, newItem: ToDoModel) =
    oldItem.isCompleted == newItem.isCompleted &&
        oldItem.description == newItem.description
}

ToDoRepository should resemble:

package com.commonsware.todo

class ToDoRepository {
  var items = listOf(
    ToDoModel(
      description = "Buy a copy of _Exploring Android_",
      isCompleted = true,
      notes = "See https://wares.commonsware.com"
    ),
    ToDoModel(
      description = "Complete all of the tutorials"
    ),
    ToDoModel(
      description = "Write an app for somebody in my community",
      notes = "Talk to some people at non-profit organizations to see what they need!"
    )
  )

  fun save(model: ToDoModel) {
    items = if (items.any { it.id == model.id }) {
      items.map { if (it.id == model.id) model else it }
    } else {
      items + model
    }
  }
}

RosterMotor should have:

package com.commonsware.todo

import androidx.lifecycle.ViewModel

class RosterMotor(private val repo: ToDoRepository) : ViewModel() {
  val items = repo.items

  fun save(model: ToDoModel) {
    repo.save(model)
  }
}

And RosterListFragment should contain:

package com.commonsware.todo

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.commonsware.todo.databinding.TodoRosterBinding
import org.koin.androidx.viewmodel.ext.android.viewModel

class RosterListFragment : Fragment() {
  private val motor: RosterMotor by viewModel()
  private var binding: TodoRosterBinding? = null

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View = TodoRosterBinding.inflate(inflater, container, false)
    .also { binding = it }
    .root

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

    val adapter = RosterAdapter(layoutInflater) {
      motor.save(it.copy(isCompleted = !it.isCompleted))
    }

    binding?.items?.apply {
      setAdapter(adapter)
      layoutManager = LinearLayoutManager(context)

      addItemDecoration(
        DividerItemDecoration(
          activity,
          DividerItemDecoration.VERTICAL
        )
      )
    }

    adapter.submitList(motor.items)
    binding?.empty?.visibility = View.GONE
  }

  override fun onDestroyView() {
    binding = null

    super.onDestroyView()
  }
}

Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.