使用Kotlin 进行 Android 开发

Posted 禅与计算机程序设计艺术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Kotlin 进行 Android 开发相关的知识,希望对你有一定的参考价值。


第14章 使用Kotlin 进行 android 开发(1)





第14章


Kotlin Android


根据Realm Report (2017-Q4,​​https://realm.io/realm-report/2017-q4​​ ) ,过去的一年在Android 端的开发:Java 从 95% 降低到 Java 85%, 而 Kotlin 从 5% 涨到 15% ,如下图所示



第14章


Kotlin is about to change the whole Android ecosystem


从这个趋势来看,加上最新 Android Studio 3.0的发布(内置 Kotlin 开发 Android 项目的支持),Kotlin 将会很快颠覆 Java 在 Android 领域的地位。

本章将带领大家快速入门使用 Kotlin 进行 Android 应用程序的开发。

14.1 快速开始 Hello World

我们从一个简单的 Kotlin 版本的Hello World Android 应用程序开始。

14.1.1 准备工作

首先准备好开发工具。Android 开发还是用 Google 官方支持的 IDE Android Studio 好。

Android Studio 3.0 简介

Google 在 2017-10-26 发布了 Android 8.1 Oreo 开发者预览版的同时还正式发布了 Android Studio 3.0 ,为其 IDE 引入了一系列新功能。

Android Studio 3.0 专注于加速 Android 应用开发,包含大量更新内容,其中最主要的功能之一就包括对 Kotlin 的支持。正如谷歌在 Google I / O 2017.5 所宣布的那样,Kotlin 已被官方支持用于 Android 开发。 Android Studio 3.0是第一个支持 Kotlin 语言的里程碑式版本(在此之前,可以使用Android Studio 的 Kotlin 插件的方式)。

在该版本中提供了许多方便实用的功能如代码自动补全和语法高亮显示,另外,Android Studio 内置转换工具可以非常方便地把 Java 代码转换成 Kotlin 代码,如下图所示



第14章


原始的Java 代码



第14章


Java 转 Kotlin 工具



第14章


转换之后的 Kotlin 代码


安装 Android Studio 3.0

Android Studio 是 Android 的官方 IDE。Android Studio 3.0的一个亮点就是内置了 Kotlin 的支持(​​https://developer.android.google.cn/kotlin/index.html​​)。正如 Google I/O 2017 所说的那样, Kotlin 已成为 Android 官方开发语言。

使用 Android Studio 3.0, 我们可以方便地把Java 源代码自动转换成 Kotlin 代码,也可以直接创建 Kotlin 语言开发的 Android 项目, 只需要在新建项目的时候勾选 Include Kotlin support 即可。

首先去官网下载安装:​​https://developer.android.google.cn/studio/install.html​​ 。笔者当前下载的安装包版本是 android-studio-ide-171.4408382-mac.dmg ,下载完毕点击 dmg 文件



第14章


安装 android studio ide


拷贝至应用程序即可。

14.1.2 创建基于 Kotlin 的Android 项目

首先新建项目。如果您未打开项目,请在 Welcome to Android Studio 窗口中点击 Start a new Android Studio project



第14章


Welcome to Android Studio 窗口


如果您已打开项目,请依次点击 File > New > New Project ,如下图所示



第14章


新建项目


进入 Create Android Project 对话框。在创建 Android 项目对话框中配置应用基本信息,注意勾选 Kotlin 支持选项,点击 Next。如下图所示



第14章


创建基于 Kotlin 的Android 项目


进入 Target Android Devices 配置应用运行 SDK 以及环境信息



第14章


Target Android Devices


我们勾选 Phone and Tablet 选项,API 15:Android 4.0.3 ,点击 Next 进入添加 Activity 界面



第14章


添加 Activity 界面


我们选择 Empty Activity,点击 Next,进入配置 Activity 界面



第14章


配置 Activity 界面


配置好 Activity Name与 Layout Name 之后,点击 Finish。我们将得到一个 Kotlin 版本的Hello World Android 应用程序。工程目录如下



第14章


工程目录


14.1.3 工程目录文件说明

其中,在顶层的 Gradle 配置文件 build.gradle 中添加了 kotlin-gradle-plugin 插件的依赖

buildscript 
ext.kotlin_version = 1.1.51
...
dependencies
classpath com.android.tools.build:gradle:3.0.0
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

...

app 目录下的build.gradle 配置文件内容如下

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

dependencies
...
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
...

其中的apply plugin: kotlin-android-extensions 表示使用 Kotlin Android Extensions插件。这个插件是 Kotlin 专门针对 Android 扩展的插件,实现了与 Data-Binding、 Dagger等框架的功能。

布局文件activity_main.xml内容如下

<?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_
android:layout_
tools:context="com.easy.kotlin.myapplication.MainActivity">

<TextView
android:layout_
android:layout_
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>

MainActivity.kt 代码如下

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)


AndroidManifest.xml 文件内容如下

<?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>

14.1.4 安装运行

点击功能菜单栏中的运行按钮



第14章


运行


会提示我们选择应用程序部署运行的目标设备



第14章


螢幕快照 2017-10-28 23.58.37.png


需要注意的是,手机要打开连接 USB 调试模式。点击 OK,Android Studio 会为我们完成打包、安装等事项。最终的运行效果如下



第14章


HelloWord.png


14.2 综合项目实战:开发一个电影指南应用程序

本节我们将开发一个Android 应用程序, 列出流行/最高评级的电影, 显示预告片和评论。

14.2.1 创建 Kotlin Android 项目



第14章


螢幕快照 2017-10-29 20.07.55.png



第14章


螢幕快照 2017-10-29 20.08.09.png



第14章


螢幕快照 2017-10-29 20.09.04.png



第14章


螢幕快照 2017-10-29 20.10.21.png



第14章


螢幕快照 2017-10-29 20.12.46.png


运行效果



第14章


MovieList.png



第14章


DetailActivity.png


14.2.2 工程说明

AndroidManifest.xml

<?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>

其中 android.intent.action.MAIN 处的配置指定了应用程序的启动Activity 为 .ItemListActivity , 其中的点号 “.” 表示该类位于 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>

下面我们来介绍应用程序的启动主类 ItemListActivity 。

ItemListActivity

Kotlin 代码如下

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



布局文件 XML 代码 activity_item_list.xml

<?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_
android:layout_
android:fitsSystemWindows="true"
tools:context="com.easy.kotlin.ItemListActivity">

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

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_
android:layout_
app:popupTheme="@style/AppTheme.PopupOverlay" />

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

<FrameLayout
android:id="@+id/frameLayout"
android:layout_
android:layout_
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_
android:layout_
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />


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

对应的 UI 设计效果图如下



第14章


XML 代码 activity_item_list.xml 设计效果图


AppCompatActivity

在使用Android Studio开发Android应用的时候,创建项目时,自动继承的是AppCompatActivity。这样我们可以在自定义的 Activity 类中添加 android.support.v7.app.ActionBar( API level 7 +)。例如activity_item_list.xml 布局中的

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

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_
android:layout_
app:popupTheme="@style/AppTheme.PopupOverlay" />

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

Activity 中添加 Toolbar 的代码是

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)

AppCompatActivity 背后也是继承的 Activity 。推出Android 5.0之后,提供了很多新功能,于是 support v7 也更新了,出现了AppCompatActivity。AppCompatActivity 是用来替代ActionBarActivity的 。 AppCompatActivity 的类图继承层次如下



第14章


AppCompatActivity 的类图继承层次


Activity 生命周期

Activity 生命周期如下图所示(图来自官网)



第14章


Activity 生命周期


相信不少朋友也已经看过这个流程图了,关于Activity生命周期的几个过程,我们简单说明如下

1.开始启动Activity,系统会先调用onCreate方法,然后调用onStart方法,最后调用onResume,Activity进入运行状态。

2.当前Activity被其他Activity覆盖其上或被锁屏:系统会调用onPause方法,暂停当前Activity的执行。

3.当前Activity由被覆盖状态回到前台或解锁屏:系统会调用onResume方法,再次进入运行状态。

4.当前Activity转到新的Activity界面或按Home键回到主屏:系统会先调用onPause方法,然后调用onStop方法,进入停止状态。

5.用户后退回到此Activity:系统会先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,再次进入运行状态。

6.当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:再次调用onCreate方法、onStart方法、onResume方法,进入运行状态。

7.用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity。

这个过程可以用下面的状态图来简单说明



第14章


Android Activity 生命周期状态图


Kotlin Android Extensions 插件

在上面的ItemListActivity.onCreate 函数中,其中的这行代码

setSupportActionBar(toolbar) 

是设置支持的 ActionBar方法。但是我们发现,这里并没有使用 findViewById()方法来获取这个 android:id="@+id/toolbar" Toolbar 的 View 对象,之前我们可能都是这样写的

Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

而这里直接就使用了 toolbar 这个 Toolbar 的对象变量。这是怎么做到的呢?其实这是通过 Kotlin Android Extensions 插件做到的。我们在app 目录下的 Gradle 配置文件 build.gradle 中添加了这个配置

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

有了这个插件我们就可以永远跟 findViewById 说再见了。Kotlin Android Extensions 插件是 Kotlin 针对 Android 开发专门定制的通用插件, 通过它我们能够以极简的无缝方式实现从 Activity, Fragment 和 View 布局组件中创建和获取视图 View 。使用 Kotlin 开发 Android 大大减少了我们的样板代码。

就像上面的示例代码一样,我们只要在代码中直接使用这个布局组件的 id 名称作为变量名即可,剩下的Kotlin 插件会帮我们全部搞定。Kotlin Android Extensions 插件将会为我们生成一些额外的代码,使得我们可以在布局XML中直接通过 id 获取到其 View 对象。另外,它还生成一个本地视图缓存,当第一次使用属性时,它将执行一个常规的findViewById。但在下一次使用属性的时候,视图将从缓存中恢复,因此访问速度将更快。

只要布局添加一个 View,在 Activity、View、Fragment 中都可以直接用 id 来引用这个 View,Kotlin 把 Android 编程极简风格发挥得淋漓尽致。

我们可以通过Kotlin 对应的字节码来更加本质深入地理解 Kotlin 所做的事情。Android Studio 中跟 IDEA 一样提供了 Kotlin 的工具箱。在菜单栏中依次选择 Code > Kotlin > Show Kotlin Bytecode , 如下图所示



第14章


Show Kotlin Bytecode


点击 Show Kotlin Bytecode 之后,我们将会看到如下图所示的 Kotlin Bytecode 界面



第14章


Kotlin Bytecode 界面


其中这两行代码

setSupportActionBar(toolbar)
toolbar.title = title

对应的字节码是

    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
L4

其实从字节码中

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

我们已经看到了 Kotlin 为我们所做的事情了。反编译成 Java 代码可能会看的更加清楚

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;

...

其中的ItemListActivity 类中的 HashMap 类型的私有成员变量 _$_findViewCache 就是本地缓存。这里其实反映出 Kotlin 语言设计的核心思想:通过更高一层的对 Java 的封装,不仅大大简化了程序员的样板化的代码量,同时还根据一些特定的可以优化的问题场景,顺带提供了更好的性能。

同样的,上面的代码中的 fab 变量

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

也是直接使用的布局 XML 中的 android:id="@+id/fab"

    <android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_
android:layout_
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />

item_detail_container 、setupRecyclerView(item_list)中的 item_list 都是使用上面的方式。这样代码确实是大大精简了许多。

嵌套 Layout 布局

上面的 activity_item_list.xml 布局中嵌套的 FrameLayout 布局配置如下

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

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

</FrameLayout>

里面的 <include layout="@layout/item_list" /> 表示引用 layout 文件夹下面的 item_list.xml

<?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_
android:layout_
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context="com.easy.kotlin.ItemListActivity"
tools:listitem="@layout/item_list_content" />



第14章


item_list.xml 布局 UI


而布局 item_list.xml 中的 tools:listitem="@layout/item_list_content" 表示又引用了layout 文件夹下面的 item_list_content.xml 布局文件。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="horizontal">

<TextView
android:id="@+id/id_text"
android:layout_
android:layout_
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />

<TextView
android:id="@+id/content"
android:layout_
android:layout_
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />
</LinearLayout>



第14章


item_list_content.xml 布局 UI


ItemDetailActivity

这个是 Item 详情页的Activity 。Kotlin 代码如下

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 dont 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)


UI 布局 XML 文件 item_detail.xml 如下

<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_
android:layout_
android:fitsSystemWindows="true"
tools:context="com.easy.kotlin.ItemDetailActivity"
tools:ignore="MergeRootFrame">

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

<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_
android:layout_
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_
android:layout_
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_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior" />

<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_
android:layout_
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>

打开item_detail.xml ,我们可以看到设计图 UI 的效果



第14章


item_detail.xml UI 设计图


我们可以看到详情页的布局主要有3大块:AppBarLayout 、NestedScrollView和 FloatingActionButton 。

在 ItemDetailActivity 的onCreate 函数里的

setContentView(R.layout.activity_item_detail)

设置详情页 ItemDetailActivity 的显示界面使用 activity_item_detail.xml 布局文件进行布局。

setSupportActionBar(detail_toolbar)

设置详情页的 android.support.v7.widget.Toolbar 控件布局。

下面我们来看在 ItemDetailActivity 中创建 ItemDetailFragment 的过程。代码如下

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()

  1. 首先我们判断当前 savedInstanceState 是否为空。如果为空, 执行步骤2 。
  2. 创建 ItemDetailFragment() 对象,并设置其 Bundle 信息(Fragment 中的成员变量 mArguments )
val arguments = Bundle()
arguments.putString(ItemDetailFragment.ARG_ITEM_ID,
intent.getStringExtra(ItemDetailFragment.ARG_ITEM_ID))
val fragment = ItemDetailFragment()
fragment.arguments = arguments
  1. 通过 supportFragmentManager 添加 Fragment 与布局空间的映射关系。
supportFragmentManager.beginTransaction()
.add(R.id.item_detail_container, fragment)
.commit()

其中,supportFragmentManager 用来获取能管理和当前 Activity 有关联的Fragment的 FragmentManager,使用supportFragmentManager 我们可以向Activity 状态中添加一个Fragment 。

上面代码中的 R.id.item_detail_container 对应的布局是一个 NestedScrollView ,代码如下

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

UI 界面设计效果如下图所示



第14章


R.id.item_detail_container 对应的布局是一个 NestedScrollView


最后需要注意的是,如果当前 Activity 在前面已经

以上是关于使用Kotlin 进行 Android 开发的主要内容,如果未能解决你的问题,请参考以下文章

FAQ | 使用 Kotlin 进行 Android 开发

Java vs Kotlin 应该使用Kotlin进行Android开发吗

我可以使用第三方 Java 库 (.jar) 来使用 Kotlin 进行 Android 开发吗?

如何使用Kotlin进行Android开发

使用Kotlin简化Android开发

使用Kotlin简化Android开发