Home Android & Kotlin Books Android Apprentice

11
Using Fragments Written by Darryl Bayliss

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Thanks to the standard set of hardware and software features Android includes across devices, adding new features to your app is easy. When it comes to designing an appealing user interface adapting across devices with varying screen sizes, things can get tricky!

In this chapter, you’ll adapt Listmaker to make full use of the additional screen space a tablet provides. Along the way, you’ll also learn:

  • What Fragments are and how they work with Activities.
  • How to split Activities into Fragments.
  • How to provide different Layout files for your app depending on the device’s screen size.

Getting started

If you’re following along with your own app, open it and keep using it with this chapter. If not, don’t worry. Locate the projects folder for this chapter and open the Listmaker app inside the starter folder.

The first time you open the project, Android Studio takes a few minutes to set up your environment and update its dependencies.

You’ll start off by creating a virtual device to emulates a tablet. If you have a physical tablet available, you can use that if you prefer.

With the Listmaker project open, click Android Virtual Device Manager along the top of Android Studio.

The AVD window pops up, showing you the emulators already available on your machine.

Click Create Virtual Device at the bottom left of the window.

A new window pops up asking what hardware you want the virtual device to emulate.

Select the Tablet category on the left. Notice the table in the middle of the window changes to offer a selection of tablets.

Select Pixel C. Then, in the bottom-right, click Next to show the next screen.

This screen asks what version of Android you want the device to run. Select the release name titled R and click Next.

Note: You may need to download the Android image before selecting the Android version.

If so, don’t worry. Just click the download button next to the release name, Android Studio will display a new window to show the download progress.

Once the download is complete. Press the Finish button to return back to the previous screen. Then, select Android Q and click Next.

The final screen displays the configuration for the device, allowing you to tweak properties like the device name, the orientation of the device on startup, and a range of advanced settings. Don’t worry about changing anything. Click Finish to complete setting up the emulator.

Run your app using the new emulator. Close the AVD window, then next to the run app button at the top of Android Studio, select the new device using the dropdown.

Next, click the run button. The tablet emulator will begin to load. Once the tablet has started and the app loads, you’ll see that it looks exactly as it did on the phone.

Note: For Android Pie devices and above, you may need to enable auto-rotate on your device or emulator if the screen doesn’t rotate automatically.

To do this, swipe the notification drawer down to reveal the quick settings and ensure the auto-rotate button is not grayed out. Tap on it to enable auto-rotation if it is.

Try out using Listmaker on a bigger device. Create some lists and add tasks to each one, taking note of the extra real estate in the app.

Although the app works on a tablet, its design isn’t optimized for the extra space available on the screen. That’s your main task for this chapter, you need to consider how to make your app adapt to the size of a device’s screen.

The good news is you’ve already done most of the hard work in the last few chapters by using Fragments. It’s finally time to talk about them and learn what they are.

What is a Fragment?

In Section 1, you built the UI for Timefighter using a single Activity. If you continued to build the app, you may have gotten into a situation where you wanted to show the same UI across different Activities. You could have copied the UI across different Activities, but that defeats one of the core rules of programming. Don’t repeat yourself, or DRY for short. That’s where Fragments help.

Creating a Layout for Tablets

In summary. You want to make sure MainActivity.kt is able to:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/detail_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.raywenderlich.listmaker.MainActivity"
    tools:showIn="@layout/main_activity">

    <!-- 1 -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/main_fragment_container"
        android:name="com.raywenderlich.listmaker.ui.main.MainFragment"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        android:layout_marginBottom="0dp"
        android:layout_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/list_detail_fragment_container"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!-- 2 -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/list_detail_fragment_container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_weight="2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toEndOf="@+id/main_fragment_container"
        app:layout_constraintTop_toTopOf="parent" />
    
    <!-- 3 -->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fabButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:clickable="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:srcCompat="@android:drawable/ic_menu_add" />

</androidx.constraintlayout.widget.ConstraintLayout>
if (savedInstanceState == null) {

  // 1
  val mainFragment = MainFragment.newInstance()
  mainFragment.clickListener = this

  // 2
  val fragmentContainerViewId: Int = if (binding.mainFragmentContainer == null) {
    R.id.detail_container
  } else {
    R.id.main_fragment_container
  }
  
  // 3
  supportFragmentManager.commit {
    setReorderingAllowed(true)
    add(fragmentContainerViewId, mainFragment)
  }
}
class MainFragment : Fragment(), ListSelectionRecyclerViewAdapter.ListSelectionRecyclerViewClickListener {
lateinit var clickListener: MainFragmentInteractionListener
  companion object {
    fun newInstance() = MainFragment()
  }

Adding the ListSelectionFragment to MainActivity

In MainActivity, update showListDetail(list: TaskList):

private fun showListDetail(list: TaskList) {

  if (binding.mainFragmentContainer == null) {
    val listDetailIntent = Intent(this, ListDetailActivity::class.java)
    listDetailIntent.putExtra(INTENT_LIST_KEY, list)
    startActivityForResult(listDetailIntent, LIST_DETAIL_REQUEST_CODE)
  } else {
    val bundle = bundleOf(INTENT_LIST_KEY to list)
    supportFragmentManager.commit {
      setReorderingAllowed(true)
      replace(R.id.list_detail_fragment_container, ListDetailFragment::class.java, bundle, null)
    }
  }
}
val list: TaskList? = arguments?.getParcelable(MainActivity.INTENT_LIST_KEY)
if (list != null) {
  viewModel.list = list
  requireActivity().title = list.name
}

Wiring Up the FloatingActionButton

In MainActivity.kt. You must change the behavior of the FloatingActionButton when adding tasks to a list.

private fun showCreateTaskDialog() {
  val taskEditText = EditText(this)
  taskEditText.inputType = InputType.TYPE_CLASS_TEXT

  AlertDialog.Builder(this)
          .setTitle(R.string.task_to_add)
          .setView(taskEditText)
          .setPositiveButton(R.string.add_task) { dialog, _ ->
            val task = taskEditText.text.toString()
            viewModel.addTask(task)
            dialog.dismiss()
          }
          .create()
          .show()
}
lateinit var list: TaskList

lateinit var onTaskAdded: (() -> Unit)
  
fun addTask(task: String) {
  list.tasks.add(task)
  onTaskAdded.invoke()
}
lateinit var viewModel: MainViewModel
viewModel = ViewModelProvider(
  this,
  MainViewModelFactory(PreferenceManager.getDefaultSharedPreferences(this))
).get(MainViewModel::class.java)
viewModel = ViewModelProvider(
        requireActivity(),        MainViewModelFactory(PreferenceManager.getDefaultSharedPreferences(requireActivity()))
)
.get(MainViewModel::class.java)
binding.fabButton.setOnClickListener {
  showCreateTaskDialog()
}
override fun onBackPressed() {

  // 1
  val listDetailFragment =
    supportFragmentManager.findFragmentById(R.id.list_detail_fragment_container)

  // 2
  if (listDetailFragment == null) {
    super.onBackPressed()
  } else {
    // 3
    title = resources.getString(R.string.app_name)

    // 4
    supportFragmentManager.commit {
      setReorderingAllowed(true)
      remove(listDetailFragment)
    }
    
    // 5
    binding.fabButton.setOnClickListener {
      showCreateListDialog()
    }
  }
}

Key Points

Fragments are powerful components in Android. They have become a major part of Android over the years and you would do well to learn all you can about them. In this chapter, you’ve learned:

Where to go from here?

What you’ve encountered here is a brief dip into the benefits they can provide. Any app wanting to succeed across multiple-sized devices needs Fragments to ensure it provides the best experience for its users. If you’d like to know more about the Fragments, the Android developer pages have comprehensive information on Fragments over at https://developer.android.com/guide/fragments.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.