1. Before you begin
Introduction
So far, you learned all about building Android apps with Compose. That's a good thing! Compose is a very powerful tool that can simplify the development process. However, Android apps were not always built with declarative UIs. Compose is a very recent tool in the history of Android Apps. Android UIs were originally built with Views. As such, it's highly likely that you will encounter Views as you continue your journey as an Android developer. In this codelab, you learn the basics of how Android apps were built before Compose — with XML, Views, View Bindings, and Fragments.
Prerequisites:
- Complete the Android Basics with Compose coursework through Unit 7.
What you'll need
- A computer with internet access and Android Studio
- A device or emulator
- The starter code for the Juice Tracker app
What you'll build
In this codelab, you complete the Juice Tracker app. This app lets you keep track of notable juices by building a list consisting of detailed items. You add and modify Fragments and XML to complete the UI and the starter code. Specifically, you build the entry form for creating a new juice, including the UI and any associated logic or navigation. The result is an app with an empty list to which you can add your own juices.
 
  
 
2. Get the starter code
- In Android Studio, open the basic-android-kotlin-compose-training-juice-trackerfolder.
- Open the Juice Tracker app code in Android Studio.
3. Create a Layout
When building an app with  Views, you construct the UI inside of a  Layout. Layouts are typically declared using XML. These XML layout files are located in the resources directory under res > layout. Layouts contain the components that make up the UI; these components are known as Views. XML syntax consists of tags, elements, and attributes. For more details on XML syntax, reference the  Create XML layouts for Android codelab.
In this section, you build an XML layout for the "Type of juice" entry dialog pictured.

- Create a new Layout Resource File in the main > res > layout directory called fragment_entry_dialog.


The fragment_entry_dialog.xml layout contains the UI components that the app displays to the user.
Notice that the Root element is a ConstraintLayout. This type of layout is a ViewGroup that lets you position and size Views in a flexible way using the constraints. A ViewGroup is a type of View that contains other Views, called children or child Views. The following steps cover this topic in more detail, but you can learn more about ConstraintLayout in  Build a Responsive UI with ConstraintLayout.
- After you create the file, define the app name space in the ConstraintLayout.
fragment_entry_dialog.xml
<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">
</androidx.constraintlayout.widget.ConstraintLayout>
- Add the following guidelines to the ConstraintLayout.
fragment_entry_dialog.xml
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_left"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_begin="16dp" />
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_middle"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_top"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="horizontal"
   app:layout_constraintGuide_begin="16dp" />
These Guidelines serve as padding for other views. The guidelines constrain the "Type of juice" header text.
- Create a TextViewelement. ThisTextViewrepresents the title of the detail fragment.

- Set the TextViewanidofheader_title.
- Set  layout_widthto0dp. Layout constraints ultimately define thisTextView's width. Therefore, defining a width only adds unnecessary calculations during the drawing of the UI; defining a width of0dpavoids the extra calculations.
- Set the TextView textattribute to@string/juice_type.
- Set the textAppearanceto@style/TextAppearance.MaterialComponents.Headline5.
fragment_entry_dialog.xml
<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" />
Lastly, you need to define the constraints. Unlike the Guidelines, which use dimensions as constraints, the guidelines themselves constrain this TextView. To achieve this outcome, you can reference the id of the Guideline by which you want to constrain the view.
- Constrain the top of the header to the bottom of guideline_top.
fragment_entry_dialog.xml
<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
   app:layout_constraintTop_toBottomOf="@+id/guideline_top" />
- Constrain the end to the start of guideline_middleand the start to the start ofguideline_leftto finish theTextViewplacement. Keep in mind that how you constrain a given view depends entirely on how you want your UI to look.
fragment_entry_dialog.xml
<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
   app:layout_constraintTop_toBottomOf="@+id/guideline_top"
   app:layout_constraintEnd_toStartOf="@+id/guideline_middle"
   app:layout_constraintStart_toStartOf="@+id/guideline_left" />
Try to build the rest of the UI based on the screenshots. You can find the completed  fragment_entry_dialog.xml file in the  solution.
4. Create a Fragment with Views
In Compose, you build layouts declaratively using Kotlin or Java. You can access different "screens" by navigating to different Composables, typically within the same activity. When building an app with Views, a Fragment that hosts the XML layout replaces the concept of a Composable "screen."
In this section, you create a Fragment to host the fragment_entry_dialog layout and provide data to the UI.
- In the juicetrackerpackage, create a new class calledEntryDialogFragment.
- Make the EntryDialogFragmentextend theBottomSheetDialogFragment.
EntryDialogFragment.kt
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class EntryDialogFragment : BottomSheetDialogFragment() {
}
The DialogFragment is a Fragment that displays a floating dialog. BottomSheetDialogFragment inherits from the DialogFragment class, but displays a sheet the width of the screen pinned to the bottom of the screen. This approach matches the design pictured previously.
- Rebuild the project, which causes View Binding files based on the fragment_entry_dialoglayout to autogenerate. The View Bindings let you access and interact with XML declaredViews, you can read more about them in the View Binding documentation.
- In the EntryDialogFragmentclass, implement theonCreateView()function. As the name suggests, this function creates theViewfor thisFragment.
EntryDialogFragment.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
override fun onCreateView(
   inflater: LayoutInflater,
   container: ViewGroup?,
   savedInstanceState: Bundle?
): View? {
   return super.onCreateView(inflater, container, savedInstanceState)
}
The onCreateView() function returns a View, but right now, it does not return a useful View.
- Return the Viewgenerated by inflating theFragmentEntryDialogViewBindinginstead of returningsuper.onCreateView().
EntryDialogFragment.kt
import com.example.juicetracker.databinding.FragmentEntryDialogBinding
override fun onCreateView(
   inflater: LayoutInflater,
   container: ViewGroup?,
   savedInstanceState: Bundle?
): View? {
   return FragmentEntryDialogBinding.inflate(inflater, container, false).root
}
- Outside of the onCreateView()function, but inside theEntryDialogFragmentclass, create an instance of theEntryViewModel.
- Implement the onViewCreated()function.
After you inflate the View binding, you can access and modify the Views in the layout. The onViewCreated() method is called after onCreateView() in the lifecycle. The onViewCreated() method is the recommended place to access and modify the Views within the layout.
- Create an instance of the view binding by calling the bind()method onFragmentEntryDialogBinding.
At this point, your code should look like the following example:
EntryDialogFragment.kt
import androidx.fragment.app.viewModels
import com.example.juicetracker.ui.AppViewModelProvider
import com.example.juicetracker.ui.EntryViewModel
class EntryDialogFragment : BottomSheetDialogFragment() {
   private val entryViewModel by viewModels<EntryViewModel> { AppViewModelProvider.Factory }
   override fun onCreateView(
       inflater: LayoutInflater,
       container: ViewGroup?,
       savedInstanceState: Bundle?
   ): View {
       return FragmentEntryDialogBinding.inflate(inflater, container, false).root
   }
   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
    }
}
You can access and set Views through the binding. For example, you can set a TextView through the setText() method.
binding.name.setText("Apple juice")
The entry dialog UI serves as a place for a user to create a new item, but you can also use it to modify an existing item. Therefore, the Fragment needs to retrieve a clicked item. The Navigation Component facilitates navigating to the EntryDialogFragment and retrieving a clicked item.
The EntryDialogFragment is not yet complete, but don't worry! For now, move on to the next section to learn more about using the Navigation Component in an app with Views.
5. Modify the Navigation Component
In this section, you use the navigation component to launch the entry dialog and to retrieve an item, if applicable.
Compose affords the opportunity to render different composables simply by calling them. However, Fragments work differently. The Navigation Component coordinates Fragment "destinations," providing an easy way to move between different Fragments and the Views they contain.
Use the Navigation Component to coordinate navigation to your EntryDialogFragment.
- Open the nav_graph.xmlfile and make sure the Design tab is selected. 
- Click the   icon to add a new destination. icon to add a new destination.

- Select EntryDialogFragmentdestination. This action declares theentryDialogFragmentin the nav graph, making it accessible for navigation actions.

You need to launch the EntryDialogFragment from the TrackerFragment. Therefore, a navigation action needs to accomplish this task.
- Drag your cursor over the trackerFragment. Selecting the gray dot and drag the line to theentryDialogFragment. 
- The nav_graph design view lets you declare arguments for a destination by selecting the destination and clicking the  icon next to the Arguments dropdown. Use this feature to add an icon next to the Arguments dropdown. Use this feature to add anitemIdargument of typeLongto theentryDialogFragment; the default value should be0L.


Note that the TrackerFragment holds a list of Juice items — if you click one of these items, the EntryDialogFragment launches.
- Rebuild the project. The itemIdargument is now accessible in theEntryDialogFragment.
6. Complete the Fragment
With the data from the navigation arguments, complete the entry dialog.
- Retrieve the navArgs()in theonViewCreated()method of theEntryDialogFragment.
- Retrieve the itemIdfrom thenavArgs().
- Implement the saveButtonto save the new/modified juice using theViewModel.
Recall from the entry dialog UI that the default color value is red. For now, pass this as a place holder.
Pass the item id from the args when calling saveJuice().
EntryDialogFragment.kt
import androidx.navigation.fragment.navArgs
import com.example.juicetracker.data.JuiceColor
class EntryDialogFragment : BottomSheetDialogFragment() {
   //...
   var selectedColor: JuiceColor = JuiceColor.Red
   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
        val args: EntryDialogFragmentArgs by navArgs()
        val juiceId = args.itemId
        binding.saveButton.setOnClickListener {
           entryViewModel.saveJuice(
               juiceId,
               binding.name.text.toString(),
               binding.description.text.toString(),
               selectedColor.name,
               binding.ratingBar.rating.toInt()
           )
        }
    }
}
- After the data is saved, dismiss the dialog with the dismiss()method.
EntryDialogFragment.kt
class EntryDialogFragment : BottomSheetDialogFragment() {
    //...
    var selectedColor: JuiceColor = JuiceColor.Red
    //...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
        val args: EntryDialogFragmentArgs by navArgs()
        binding.saveButton.setOnClickListener {
           entryViewModel.saveJuice(
               juiceId,
               binding.name.text.toString(),
               binding.description.text.toString(),
               selectedColor.name,
               binding.ratingBar.rating.toInt()
           )
           dismiss()
        }
    }
}
Keep in mind that the above code does not complete the EntryDialogFragment. You still need to implement a number of things, such as the population of the fields with existing Juice data (if applicable), the selection of a color from the colorSpinner, the implementation of the cancelButton, and more. However, this code is not unique to Fragments, and you are able to implement this code on your own. Try to implement the rest of the functionality. As a last resort, you can refer to the solution code for this codelab.
7. Launch the entry dialog
The last task is to launch the entry dialog using the Navigation Component. The entry dialog needs to launch when the user clicks the floating action button (FAB). It needs to also launch and pass the corresponding id when the user clicks an item.
- In the onClickListener()for the FAB, callnavigate()on the nav controller.
TrackerFragment.kt
import androidx.navigation.findNavController
//...
binding.fab.setOnClickListener { fabView ->
   fabView.findNavController().navigate(
   )
}
//...
- In the navigate function, pass the action to navigate from the tracker to the entry dialog.
TrackerFragment.kt
//...
binding.fab.setOnClickListener { fabView ->
   fabView.findNavController().navigate(
TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment()
   )
}
//...
- Repeat this action in the lambda body for the onEdit()method in theJuiceListAdapter, but this time, pass theidof theJuice.
TrackerFragment.kt
//...
onEdit = { drink ->
   findNavController().navigate(
       TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment(drink.id)
   )
},
//...
8. Get the solution code
To download the code for the finished codelab, you can use these git commands:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views
Alternatively, you can download the repository as a zip file, unzip it, and open it in Android Studio.
If you want to see the solution code, view it on GitHub.
