Final Results

Your nav_graph navigation resource should resemble:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:id="@+id/nav_graph.xml"
  app:startDestination="@id/rosterListFragment">

  <fragment
    android:id="@+id/rosterListFragment"
    android:name="com.commonsware.todo.RosterListFragment"
    android:label="@string/app_name">
    <action
      android:id="@+id/displayModel"
      app:destination="@id/displayFragment" />
  </fragment>
  <fragment
    android:id="@+id/displayFragment"
    android:name="com.commonsware.todo.DisplayFragment"
    android:label="@string/app_name" >
    <argument
      android:name="modelId"
      app:argType="string" />
    <action
      android:id="@+id/editModel"
      app:destination="@id/editFragment" />
  </fragment>
  <fragment
    android:id="@+id/editFragment"
    android:name="com.commonsware.todo.EditFragment"
    android:label="@string/app_name" >
    <argument
      android:name="modelId"
      app:argType="string" />
  </fragment>
</navigation>

The new actions_display resource should have this XML:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:android="http://schemas.android.com/apk/res/android">

  <item
    android:id="@+id/edit"
    android:icon="@drawable/ic_edit"
    android:title="@string/menu_edit"
    app:showAsAction="ifRoom|withText" />
</menu>

The new todo_edit layout resource should resemble:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <CheckBox
    android:id="@+id/isCompleted"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    app:layout_constraintBottom_toBottomOf="@+id/desc"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@+id/desc" />

  <EditText
    android:id="@+id/desc"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginEnd="8dp"
    android:ems="10"
    android:hint="@string/desc"
    android:inputType="text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@+id/isCompleted"
    app:layout_constraintTop_toTopOf="parent" />

  <EditText
    android:id="@+id/notes"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"
    android:ems="10"
    android:gravity="start|top"
    android:hint="@string/notes"
    android:inputType="textMultiLine"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/desc"
    app:layout_constraintVertical_bias="0.505" />
</androidx.constraintlayout.widget.ConstraintLayout>

DisplayFragment should look like:

package com.commonsware.todo

import android.os.Bundle
import android.text.format.DateUtils
import android.view.*
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.commonsware.todo.databinding.TodoDisplayBinding
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf

class DisplayFragment : Fragment() {
  private val args: DisplayFragmentArgs by navArgs()
  private var binding: TodoDisplayBinding? = null
  private val motor: SingleModelMotor by viewModel { parametersOf(args.modelId) }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setHasOptionsMenu(true)
  }
  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ) = TodoDisplayBinding.inflate(inflater, container, false)
    .apply { binding = this }
    .root

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    motor.getModel()?.let {
      binding?.apply {
        completed.visibility = if (it.isCompleted) View.VISIBLE else View.GONE
        desc.text = it.description
        createdOn.text = DateUtils.getRelativeDateTimeString(
          requireContext(),
          it.createdOn.toEpochMilli(),
          DateUtils.MINUTE_IN_MILLIS,
          DateUtils.WEEK_IN_MILLIS,
          0
        )
        notes.text = it.notes
      }
    }
  }

  override fun onDestroyView() {
    binding = null

    super.onDestroyView()
  }

  override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    inflater.inflate(R.menu.actions_display, menu)

    super.onCreateOptionsMenu(menu, inflater)
  }

  override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
      R.id.edit -> {
        edit()
        return true
      }
    }

    return super.onOptionsItemSelected(item)
  }

  private fun edit() {
    findNavController().navigate(
      DisplayFragmentDirections.editModel(
        args.modelId
      )
    )
  }
}

EditFragment at this point should resemble:

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.navigation.fragment.navArgs
import com.commonsware.todo.databinding.TodoEditBinding
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf

class EditFragment : Fragment() {
  private var binding: TodoEditBinding? = null
  private val args: EditFragmentArgs by navArgs()
  private val motor: SingleModelMotor by viewModel { parametersOf(args.modelId) }

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ) = TodoEditBinding.inflate(inflater, container, false)
    .apply { binding = this }
    .root

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    motor.getModel()?.let {
      binding?.apply {
        isCompleted.isChecked = it.isCompleted
        desc.setText(it.description)
        notes.setText(it.notes)
      }
    }
  }

  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.