Chapters

Hide chapters

Android Apprentice

Fourth Edition · Android 11 · Kotlin 1.4 · Android Studio 4.1

Section II: Building a List App

Section 2: 7 chapters
Show chapters Hide chapters

Section III: Creating Map-Based Apps

Section 3: 7 chapters
Show chapters Hide chapters

9. Communicating Between Activities
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.

So far in this book, you’ve made use of a single Activity for your apps. As your apps get more complicated, however, trying to cram more visual elements into a single Activity can make your app confusing for users. Keeping an Activity dedicated to a single task removes this problem.

At the moment, Listmaker has no way to add items to the lists you create. This is a good task to put in a separate Activity — which is what you’ll do in this chapter — and when you’re done, you’ll have learned how to:

  1. Create another Activity.
  2. Communicate between Activities using an Intent.
  3. Pass data between Activities.

Getting started

If you’re following along with your own project, 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.

Note: If you added lists in the previous chapter, you’ll continue to see them inside your app. If you want to start fresh, delete the app from your device, then keep going with this chapter. All of the previous list data gets deleted when you delete the app.

With the Listmaker project open, run the app. When it appears, tap the Floating Action Button in the bottom-right and enter the list title as My List.

Tap Create, and the new list name shows up in the RecyclerView.

That works, but it isn’t too useful. If you tap the title of the list in the RecyclerView, nothing happens. Wouldn’t it be great if something were to happen? Absolutely!

You’ll fix this by creating another Activity. As a general rule, Activities should focus on a single task, so the logic within an Activity stays clean and simple as you build it. Single task Activities also benefit your users because navigation between screens becomes more intuitive.

Note: This rule of thumb also applies to Fragments. Every Fragment should be responsible for one job. You’ll learn more about Fragments in Chapter 11.

In the Project navigator, right-click com.raywenderlich.listmaker.ui.main.

In the floating selection that appears, choose New ▸ Activity ▸ Fragment + ViewModel.

Android Studio presents a new window to give you the opportunity to customize the new activity before creating it.

Creating a new Activity

The Configure Activity wizard provides some fields to customize the Activity:

The app manifest

Every Android app has an app manifest. It’s important because it tells an Android device everything it needs to know about your app.

viewModel = ViewModelProvider(this).get(ListDetailViewModel::class.java)
viewModel = ViewModelProvider(requireActivity()).get(ListDetailViewModel::class.java)

Intents

Now that you have the two Activities, it’s time to give your app the ability to navigate between them. In the MainActivity, you have two main points of entry for the new Activity:

An Intent is created to show another Activity on screen
Oy Azcaxn er zzeotob le ctum atemrah Omrulejn ir fccain

private fun showListDetail(list: TaskList) {
  // 1
  val listDetailIntent = Intent(this, ListDetailActivity::class.java)
  // 2
  listDetailIntent.putExtra(INTENT_LIST_KEY, list)
  // 3
  startActivity(listDetailIntent)
}

Intents and Parcels

Open TaskList.kt and change the class declaration so it implements the Parcelable interface:

class TaskList(val name: String, val tasks: ArrayList<String> = ArrayList()) : Parcelable {
//1
constructor(source: Parcel) : this(
  source.readString()!!,
  source.createStringArrayList()!!
)

override fun describeContents() = 0

//2
override fun writeToParcel(dest: Parcel, flags: Int) {
  dest.writeString(name)
  dest.writeStringList(tasks)
}

// 3
companion object CREATOR: Parcelable.Creator<TaskList> {
    // 4
    override fun createFromParcel(source: Parcel): TaskList = TaskList(source)
    override fun newArray(size: Int): Array<TaskList?> = arrayOfNulls(size)
}

Bringing everything together

Now that the TaskList can be passed around on Android, it’s time to tie everything together so you can pass TaskLists through to the next screen. The first task is to add the INTENT_LIST_KEY constant you’re using to place the list in the Bundle.

companion object {
  const val INTENT_LIST_KEY = "list"
}
builder.setPositiveButton(positiveButtonTitle) { dialog, _ ->
    dialog.dismiss()
    
    val taskList = TaskList(listTitleEditText.text.toString())
    viewModel.saveList(taskList)
    showListDetail(taskList)
}
interface ListSelectionRecyclerViewClickListener {
  fun listItemClicked(list: TaskList)
}
class ListSelectionRecyclerViewAdapter(val lists: MutableList<TaskList>, val clickListener: ListSelectionRecyclerViewClickListener) : RecyclerView.Adapter<ListSelectionViewHolder>() {
  override fun onBindViewHolder(holder: ListSelectionViewHolder, position: Int) {
    holder.binding.itemNumber.text = (position + 1).toString()
    holder.binding.itemString.text = lists[position].name
    holder.itemView.setOnClickListener {
      clickListener.listItemClicked(lists[position]) 
    }
  }
class MainFragment : Fragment(), ListSelectionRecyclerViewAdapter.ListSelectionRecyclerViewClickListener {
override fun listItemClicked(list: TaskList) {
  clickListener.listItemTapped(list)
}
val recyclerViewAdapter = ListSelectionRecyclerViewAdapter(viewModel.lists, this)
interface MainFragmentInteractionListener {
  fun listItemTapped(list: TaskList)
}
class MainFragment(val clickListener: MainFragmentInteractionListener) : Fragment(), ListSelectionRecyclerViewAdapter.ListSelectionRecyclerViewClickListener {
fun newInstance(clickListener: MainFragmentInteractionListener) = MainFragment(clickListener)
class MainActivity : AppCompatActivity(), MainFragment.MainFragmentInteractionListener {
if (savedInstanceState == null) {
  val mainFragment = MainFragment.newInstance(this)
  supportFragmentManager.beginTransaction()
          .replace(R.id.container, mainFragment)
          .commitNow()
}
override fun listItemTapped(list: TaskList) {
  showListDetail(list)
}
lateinit var list: TaskList
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.list_detail_activity)
  // 1
  list = intent.getParcelableExtra(MainActivity.INTENT_LIST_KEY)!!
  // 2
  title = list.name

  if (savedInstanceState == null) {
    supportFragmentManager.beginTransaction()
      .replace(R.id.container, ListDetailFragment.newInstance())
      .commitNow()
  }
}

Key Points

Listmaker is beginning to take shape. You now have two Activities, each dedicated to a particular task. You also know how to pass data between Activities. You’ve learned:

Where to go from here?

Intents are another common pattern you’ll see in all Android apps. They’re used for all kinds of purposes beyond starting Activities. Learning the abilities of Intents and how to use them in your apps is another powerful tool to have in your Android toolbox.

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.
© 2024 Kodeco Inc.

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 Kodeco Personal Plan.

Unlock now