Chapter 14 uses Kotlin for Android development

According to Realm Report (2017-Q4, realm.io/realm-repor…) , development on Android over the past year: Java went down from 95% to 85%, and Kotlin went up from 5% to 15%, as shown below

With this trend, and the latest release of Android Studio 3.0 (with built-in Kotlin support for Developing Android projects), Kotlin will soon overturn Java’s position in the Android space.

This chapter will give you a quick start on developing Android applications using Kotlin.

14.1 Quickly Start Hello World

Let’s start with a simple Kotlin version of the Hello World Android application.

14.1.1 Preparations

Get your development tools ready first. Android development is better off using Android Studio, Google’s officially supported IDE.

Introduction to Android Studio 3.0

Google announced the release of the Android 8.1 Oreo developer Preview on October 26, 2017, along with the official release of Android Studio 3.0, which introduces a number of new features to its IDE.

Android Studio 3.0 focuses on accelerating Android application development and includes a number of updates. One of the main features is support for Kotlin. As Google announced in Google I/O 2017.5, Kotlin has been officially supported for Android development. Android Studio 3.0 is the first milestone release to support the Kotlin language (prior to that, it was possible to use the Kotlin plug-in for Android Studio).

There are many handy features such as code completion and syntax highlighting, and Android Studio’s built-in conversion tools make it easy to convert Java code into Kotlin code, as shown in the figure below

Install Android Studio 3.0

Android Studio is the official IDE for Android. One of the highlights of Android Studio 3.0 is built-in support for Kotlin (Developer.Android.goo…). . As announced in Google I/O 2017, Kotlin has become the official Android development language.

Using Android Studio 3.0, we can easily convert Java source code into Kotlin code automatically, You can also create a Kotlin Android project by checking Include Kotlin Support when creating a project.

First go to the official website to download and install: developer.android.goo… . The current installation package is Android-Studio-IDE-171.4408382-mac.dmg. Click on the DMG file

Copy it to the application.

14.1.2 Creating an Android project based on Kotlin

First, create a new project. If you do not have the project open, click Start a new Android Studio Project in the Welcome to Android Studio window

If you have opened the Project, please click File > New > New Project, as shown below

The Create Android Project dialog box is displayed. In the Create Android Project dialog box, configure the basic application information, check Kotlin support, and click Next. As shown in the figure below

Access Target Android Devices to configure the application running SDK and environment information

Select Phone and Tablet, API 15: Android 4.0.3, and click Next to enter the Add Activity screen

We select Empty Activity and click Next to enter the configure Activity interface

After configuring the Activity Name and Layout Name, click Finish. We’ll get a Kotlin version of the Hello World Android app. The project catalog is as follows

14.1.3 Project Directory File Description

Gradle builds. Gradle with a dependency on the kotlin-gradle-plugin plugin

Buildscript {ext.kotlin_version = '1.1.51'... Dependencies {classpath 'com. Android. View the build: gradle: 3.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } ...Copy the code

The build.gradle configuration file in the app directory contains the following contents

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

dependencies {
    ...
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    ...
}Copy the code

The apply plugin: ‘kotlin-Android-Extensions’ means to use the Kotlin Android Extensions plug-in. This plugin is Kotlin’s Android extension that implements functionality with data-binding, Dagger, and other frameworks.

The layout file activity_main.xml reads as follows

<? The XML version = "1.0" encoding = "utf-8"? > <android.support.constraint.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:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.easy.kotlin.myapplication.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>Copy the code

The mainactivity. kt code is as follows

package com.easy.kotlin.myapplication

import android.support.v7.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
Copy the code

The androidmanifest.xml file reads as follows

<? The XML version = "1.0" encoding = "utf-8"? > <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.easy.kotlin.myapplication"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>Copy the code

14.1.4 Installation and Operation

Click the Run button in the function menu bar

We are prompted to select the target device to run the application deployment

Note that the phone should be in USB debug mode. Click OK, and Android Studio will do the packing, installation, and more for us. The final result is as follows

14.2 Integrated project: Develop a movie guide application

In this section we will develop an Android application that lists popular/highest rated movies, shows trailers and reviews.

14.2.1 Create the Kotlin Android project

Running effect

14.2.2 Project Description

AndroidManifest.xml

<? The XML version = "1.0" encoding = "utf-8"? > <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.easy.kotlin"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".ItemListActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ItemDetailActivity" android:label="@string/title_item_detail" android:parentActivityName=".ItemListActivity" android:theme="@style/AppTheme.NoActionBar"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.easy.kotlin.ItemListActivity" /> </activity> </application> </manifest>Copy the code

The android. Intent. Action. The MAIN place of configuration specifies the application startup Activity for. ItemListActivity, the dot “.” Indicates that the class is in package=”com.easy. Kotlin “.

<activity android:name=".ItemListActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar">  <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>Copy the code

Let’s look at the main startup class for our application, ItemListActivity.

ItemListActivity

The Kotlin code is as follows

package com.easy.kotlin import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.support.v7.widget.RecyclerView import android.support.design.widget.Snackbar import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import com.easy.kotlin.dummy.DummyContent import kotlinx.android.synthetic.main.activity_item_list.* import kotlinx.android.synthetic.main.item_list_content.view.* import kotlinx.android.synthetic.main.item_list.* /** * An activity representing a list of Pings. This activity * has different presentations for handset and tablet-size devices. On * handsets, the activity presents a list of items, which when touched, * lead to a [ItemDetailActivity] representing * item details. On tablets, the activity presents the list of items and * item details side-by-side using two vertical panes. */ class ItemListActivity : AppCompatActivity() { /** * Whether or not the activity is in two-pane mode, i.e. running on a tablet * device. */ private var mTwoPane: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_item_list) setSupportActionBar(toolbar) toolbar.title = title fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } if (item_detail_container ! = null) { // The detail container view will be present only in the // large-screen layouts (res/values-w900dp). // If this view is present, then the // activity should be in two-pane mode. mTwoPane = true } setupRecyclerView(item_list) } private fun setupRecyclerView(recyclerView: RecyclerView) { recyclerView.adapter = SimpleItemRecyclerViewAdapter(this, DummyContent.ITEMS, mTwoPane) } class SimpleItemRecyclerViewAdapter(private val mParentActivity: ItemListActivity, private val mValues: List<DummyContent.DummyItem>, private val mTwoPane: Boolean) : RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder>() { private val mOnClickListener: View.OnClickListener init { mOnClickListener = View.OnClickListener { v -> val item = v.tag as DummyContent.DummyItem if  (mTwoPane) { val fragment = ItemDetailFragment().apply { arguments = Bundle() arguments.putString(ItemDetailFragment.ARG_ITEM_ID, item.id) } mParentActivity.supportFragmentManager .beginTransaction() .replace(R.id.item_detail_container, fragment) .commit() } else { val intent = Intent(v.context, ItemDetailActivity::class.java).apply { putExtra(ItemDetailFragment.ARG_ITEM_ID, item.id) } v.context.startActivity(intent) } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_list_content, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = mValues[position] holder.mIdView.text = item.id holder.mContentView.text = item.content with(holder.itemView) { tag = item setOnClickListener(mOnClickListener) } } override fun getItemCount(): Int { return mValues.size } inner class ViewHolder(mView: View) : RecyclerView.ViewHolder(mView) { val mIdView: TextView = mView.id_text val mContentView: TextView = mView.content } } }Copy the code

The layout file XML code is activity_item_list.xml

<? The XML version = "1.0" encoding = "utf-8"? > <android.support.design.widget.CoordinatorLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="com.easy.kotlin.ItemListActivity"> <android.support.design.widget.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <FrameLayout android:id="@+id/frameLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <include layout="@layout/item_list" /> </FrameLayout> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" app:srcCompat="@android:drawable/ic_dialog_email" /> </android.support.design.widget.CoordinatorLayout>Copy the code

The corresponding UI design effect is as follows

AppCompatActivity

When developing Android apps using Android Studio, you automatically inherit AppCompatActivity when creating projects. So that we can add in the custom Activity class android. Support. V7. App. The ActionBar API level 7 (+). For example, in the activity_item_list.xml layout

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>Copy the code

The code to add toolbars to the Activity is

class ItemListActivity : AppCompatActivity() { /** * Whether or not the activity is in two-pane mode, i.e. running on a tablet * device. */ private var mTwoPane: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_item_list) setSupportActionBar(toolbar) toolbar.title = title fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } if (item_detail_container ! = null) { // The detail container view will be present only in the // large-screen layouts (res/values-w900dp). // If this view is present, then the // activity should be in two-pane mode. mTwoPane = true } setupRecyclerView(item_list) } }Copy the code

AppCompatActivity is an inherited Activity behind it. With Android 5.0, there are a lot of new features available, so support V7 has been updated to show AppCompatActivity. AppCompatActivity is used to replace ActionBarActivity. The class diagram of AppCompatActivity has the following inheritance hierarchy

Activity Lifecycle

The Activity lifecycle is shown below (from the official website)

The following is a brief description of the Activity lifecycle

1. The system calls the onCreate method, onStart method, and onResume method to start the Activity.

2. The current Activity is overwritten by another Activity or the screen is locked: The system calls the onPause method to pause the Activity.

3. The current Activity returns to the foreground or unlock screen after being overwritten: the system calls onResume to start the Activity again.

4. The current Activity moves to a new Activity screen or press the Home button to return to the Home screen: The system calls onPause and then onStop to stop the Activity.

5. The user steps back to the Activity: the system calls the onRestart method, then the onStart method, and finally the onResume method to start the Activity again.

6. The current Activity is overwritten or not visible in the background, that is, in step 2 and step 4, the system is out of memory, kill the current Activity, and then the user returns to the current Activity: call onCreate, onStart, and onResume methods again to enter the running state.

7. The user exits the current Activity: the system calls onPause, onStop, and onDestory to end the current Activity.

This process can be briefly illustrated by the following state diagram

The Kotlin Android Extensions plug-in

In the above ItemListActivity. OnCreate function, the lines of code

setSupportActionBar(toolbar) Copy the code

Is to set the supported ActionBar methods. However, the findViewById() method is not used to retrieve the Android :id=”@+id/toolbar” toolbar View object, as we might have done earlier

Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);Copy the code

This uses the toolbar object variable directly. How does this work? This is actually done through the Kotlin Android Extensions plug-in. We added this configuration in the Gradle configuration file build. Gradle in the app directory

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'Copy the code

With this plugin we can kiss findViewById goodbye forever. The Kotlin Android Extensions plugin is a general purpose extension that Kotlin developed for Android. It provides a minimalist and seamless way to implement activities, Create and get View views in Fragment and View layout components. Using Kotlin to develop Android has greatly reduced our boilerplate code.

As in the example above, we simply use the id name of the layout component as the variable name in the code, and the Kotlin plug-in will take care of it for us. The Kotlin Android Extensions plug-in will generate some extra code for us to get its View object directly from the ID in the layout XML. In addition, it generates a local view cache that performs a regular findViewById when the property is first used. But the next time the property is used, the view is recovered from the cache, so access is faster.

As long as the layout adds a View, the Activity, View, Fragment can directly use the ID to reference the View, Kotlin Android programming minimalist style play to the fullest.

We can look at Kotlin’s bytecode for a more fundamental insight into what Kotlin is doing. Kotlin’s toolkit is available in Android Studio as well as IDEA. On the menu bar, choose Code > Kotlin > Show Kotlin Bytecode, as shown in the following figure

After clicking Show Kotlin Bytecode, you will see the Kotlin Bytecode interface as shown below

These two lines of code

setSupportActionBar(toolbar)
toolbar.title = titleCopy the code

The corresponding bytecode is

LINENUMBER 39 L2 ALOAD 0 ALOAD 0 GETSTATIC com/easy/kotlin/R$id.toolbar : I INVOKEVIRTUAL com/easy/kotlin/ItemListActivity._$_findCachedViewById (I)Landroid/view/View; CHECKCAST android/support/v7/widget/Toolbar INVOKEVIRTUAL com/easy/kotlin/ItemListActivity.setSupportActionBar (Landroid/support/v7/widget/Toolbar;) V L3 LINENUMBER 40 L3 ALOAD 0 GETSTATIC com/easy/kotlin/R$id.toolbar : I INVOKEVIRTUAL com/easy/kotlin/ItemListActivity._$_findCachedViewById (I)Landroid/view/View; CHECKCAST android/support/v7/widget/Toolbar ALOAD 0 INVOKEVIRTUAL com/easy/kotlin/ItemListActivity.getTitle ()Ljava/lang/CharSequence; INVOKEVIRTUAL android/support/v7/widget/Toolbar.setTitle (Ljava/lang/CharSequence;) V L4Copy the code

Actually from bytecode

GETSTATIC com/easy/kotlin/R$id.toolbar : I
    INVOKEVIRTUAL com/easy/kotlin/ItemListActivity._$_findCachedViewById Copy the code

We’ve seen what Kotlin has done for us. Decompiling into Java code might make it a little clearer

public final class ItemListActivity extends AppCompatActivity { private boolean mTwoPane; private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131361820); this.setSupportActionBar((Toolbar)this._$_findCachedViewById(id.toolbar)); ((Toolbar)this._$_findCachedViewById(id.toolbar)).setTitle(this.getTitle()); . } public View _$_findCachedViewById(int var1) { if(this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1)); if(var2 == null) { var2 = this.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; }... }Copy the code

The private member variable _$_findViewCache of type HashMap in the ItemListActivity class is the local cache. This reflects the core idea of Kotlin’s language design: the higher level of Java encapsulation not only greatly simplifies the programmer’s boilerplate code, but also provides better performance for specific problem scenarios that can be optimized.

Also, the fab variable in the code above

fab.setOnClickListener { view ->
    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
            .setAction("Action", null).show()
}Copy the code

Android :id=”@+id/fab”

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_email" />Copy the code

Item_detail_container and Item_list in setupRecyclerView(item_list) all use the above method. The code is really much simpler.

Nested Layout Layout

The FrameLayout layout nested in the activity_item_list.xml layout above is configured as follows

    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <include layout="@layout/item_list" />

    </FrameLayout>Copy the code

<include layout=”@layout/item_list” /> refers to the item_list.xml in the layout folder

<? The XML version = "1.0" encoding = "utf-8"? > <android.support.v7.widget.RecyclerView 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/item_list" android:name="com.easy.kotlin.ItemListFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" app:layoutManager="LinearLayoutManager" tools:context="com.easy.kotlin.ItemListActivity" tools:listitem="@layout/item_list_content" />Copy the code

Tools :listitem=”@layout/ item_list_Content “in layout Item_list. XML refers to the layout file item_list_Content.xml under layout folder.

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/id_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/text_margin" android:textAppearance="?attr/textAppearanceListItem" /> <TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/text_margin" android:textAppearance="?attr/textAppearanceListItem" /> </LinearLayout>Copy the code

ItemDetailActivity

This is the Activity for the Item detail page. The Kotlin code is as follows

package com.easy.kotlin import android.content.Intent import android.os.Bundle import android.support.design.widget.Snackbar import android.support.v7.app.AppCompatActivity import android.view.MenuItem import kotlinx.android.synthetic.main.activity_item_detail.* /** * An activity representing a single Item detail screen.  This * activity is only used on narrow width devices. On tablet-size devices, * item details are presented side-by-side with a list of items * in a [ItemListActivity]. */ class ItemDetailActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_item_detail) setSupportActionBar(detail_toolbar) fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own detail action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } // Show the Up button in the action bar. supportActionBar?.setDisplayHomeAsUpEnabled(true) // savedInstanceState is non-null when there is fragment state // saved from previous configurations of this activity // (e.g. when rotating the screen from portrait to landscape). // In this case, the fragment will automatically be re-added // to its container so we don't need to manually add it. // For more information, see the Fragments API guide at: // // http://developer.android.com/guide/components/fragments.html // if (savedInstanceState == null) { // Create the detail fragment and add it to the activity // using a fragment transaction. val arguments = Bundle() arguments.putString(ItemDetailFragment.ARG_ITEM_ID, intent.getStringExtra(ItemDetailFragment.ARG_ITEM_ID)) val fragment = ItemDetailFragment() fragment.arguments = arguments supportFragmentManager.beginTransaction() .add(R.id.item_detail_container, fragment) .commit() } } override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { android.R.id.home -> { // This ID represents the Home or Up button. In the case of this  // activity, the Up button is shown. For // more details, see the Navigation pattern on Android Design: // // http://developer.android.com/design/patterns/navigation.html#up-vs-back navigateUpTo(Intent(this, ItemListActivity::class.java)) true } else -> super.onOptionsItemSelected(item) } }Copy the code

The UI layout XML file item_detail.xml is shown below

<android.support.design.widget.CoordinatorLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.easy.kotlin.ItemDetailActivity"
    tools:ignore="MergeRootFrame">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:toolbarId="@+id/toolbar">

            <android.support.v7.widget.Toolbar
                android:id="@+id/detail_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/item_detail_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|start"
        android:layout_margin="@dimen/fab_margin"
        app:layout_anchor="@+id/item_detail_container"
        app:layout_anchorGravity="top|end"
        app:srcCompat="@android:drawable/stat_notify_chat" />

</android.support.design.widget.CoordinatorLayout>
Copy the code

Opening item_detail.xml, we can see the effect of the design UI

We can see that the details page layout has three main blocks: AppBarLayout, NestedScrollView and FloatingActionButton.

In the ItemDetailActivity onCreate function

setContentView(R.layout.activity_item_detail)Copy the code

Set the ItemDetailActivity display page to use the activity_item_detail. XML layout file.

setSupportActionBar(detail_toolbar)Copy the code

Set the details page of the android. Support. V7. Widget. The Toolbar control layout.

Let’s look at the process of creating an ItemDetailFragment in the ItemDetailActivity. The following code

override fun onCreate(savedInstanceState: Bundle?) {... if (savedInstanceState == null) { // Create the detail fragment and add it to the activity // using a fragment transaction. val arguments = Bundle() arguments.putString(ItemDetailFragment.ARG_ITEM_ID, intent.getStringExtra(ItemDetailFragment.ARG_ITEM_ID)) val fragment = ItemDetailFragment() fragment.arguments = arguments supportFragmentManager.beginTransaction() .add(R.id.item_detail_container, fragment) .commit() } }Copy the code
  1. First we determine whether the current savedInstanceState is empty. If no, go to Step 2.
  2. Create the ItemDetailFragment() object and set its Bundle information (member variables in the Fragment mArguments)
val arguments = Bundle()
arguments.putString(ItemDetailFragment.ARG_ITEM_ID,
        intent.getStringExtra(ItemDetailFragment.ARG_ITEM_ID))
val fragment = ItemDetailFragment()
fragment.arguments = argumentsCopy the code
  1. Add the Fragment mapping to the layout space using supportFragmentManager.
supportFragmentManager.beginTransaction()
        .add(R.id.item_detail_container, fragment)
        .commit()Copy the code

SupportFragmentManager is used to get the FragmentManager that can manage fragments associated with the current Activity. Using supportFragmentManager we can add a Fragment to the Activity state.

The layout corresponding to r.I.D.i.TEM_detail_container in the above code is a NestedScrollView, with the following code

<android.support.v4.widget.NestedScrollView
    android:id="@+id/item_detail_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />Copy the code

The UI design effect is shown in the figure below

Finally, note that the savedInstanceState value is non-null if the current Activity already has Fragment data in front of it. There is no need to manually create a Fragment object and store it in the current Activity. Because when our Activty is destroyed by an exception, the Activity will save its state (which includes the Fragment we added). When the Activity is recreated, the Fragment we saved will be restored.

Therefore, always check to see if there is any saved Activity state before adding a Fragment. If no state is saved, it means that the Acitvity is created for the first time and we add the Fragment. If there is a stateful save, it indicates that the Activity has just been destroyed due to an exception. The previous Fragment will be restored and we will not add any fragments.

FragmentTransaction

In the code above, we use the Add method for FragmentTransaction, which is signed as follows

public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);Copy the code

The parameter containerViewId is passed the ID of a view container in the Activity. If containerViewId passes 0, the Fragment will not be placed in a container. Note that we are not adding a Fragment that has no view and can be used to do some background work like a Service.

The following table lists the commonly used apis for FragmentTransaction

API methods instructions
add(int containerViewId, Fragment fragment, String tag) Add a Fragment to the Activity state. The containerViewId parameter generally passes the ID of a view container in the Activity. If containerViewId passes 0, the Fragment will not be placed in a container. Check whether there is any saved Activity state before adding a Fragment.
remove(Fragment fragment) Remove an existing Fragment. After a Fragment is removed, onDetach is executed throughout the Fragment’s life cycle, after which the Fragment instance is removed from the FragmentManager.
replace(int containerViewId, Fragment fragment) Replace a Fragment that has been added to the view container. Previously added fragments are removed by the view container at replace.
addToBackStack(String name) Records committed transactions (transations) that can be used for rollback operations. The name argument is a name (or identifier) for the rollback operation. Null is not required.
show(Fragment fragment) Hide an existing Fragment.
hide(Fragment fragment) Displays a previously hidden Fragment. The Fragment is hide/show, just hide/show the view of the Fragment, and no lifecycle methods are called. Override the onHiddenChanged method in the Fragment to listen for the hide and show states of the Fragment.
attach(Fragment fragment) Re-associate a Fragment (after the detach of the Fragment has been executed). After the Fragment is detach, attach will cause the Fragment to start onCreateView and continue onResume. Attach cannot be used alone like Add. If it is used alone, an exception will be thrown. The purpose of the detach method is to interface restore the Fragment after detach.
detach(Fragment fragment) Separates the UI view of the specified Fragment. When a Fragment is detach, the Fragment life cycle terminates after onDestroyView is executed. This means that the Fragment instance is not destroyed, but the UI is removed.
setCustomAnimations(int enter, int exit) Sets the specified animation resource for Fragment entry/exit.
commit() Commit the transaction. Schedule a commit for this transaction. The commit does not happen immediately and is scheduled to be executed when the main thread is next ready.
commitNow() Commit the transaction synchronously. Any fragments added will be initialized, bringing them fully into their life-cycle state. No stack rollback can be added with commitNow(), and addToBackStack(String) will throw an IllegalStateException.