Actuvity 是最容易吸引用户的地方,它是一种能够包含用户界面的组件,主要用于和用户进行交互。一个应用中能够包含0个或多个 Activity,但不包含任何 Activity 的应用程序是没法被用户看见的。java
点击 Empty Activity 建立名为 FirstActivity 的 Activity android
右键 app/src/main/res 目录->New->Directory,会弹出一个新建目录的窗口,如今这里建立一个名为 layout 的目录,而后对着 layout 目录右键->New->Layout resource file,优惠弹出一个新建布局资源文件的窗口,咱们将这个布局文件命名为first_layout,根元素默认选择为 LinearLayout。编程
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
复制代码
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在此加载布局文件
setContentView(R.layout.first_layout)
}
}
复制代码
项目中每添加一个元素都会在 R 文件中响应的生成一个资源 id,所以刚才添加的布局文件的 id 就已经添加到了 R 文件中,因此能够经过R.layout.first_layout
找到 first_layout.xml 的 id,而后将这个值传入到 setContentView()
方法便可。bash
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitytest">
<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=".FirstActivity"></activity>
</application>
</manifest>
复制代码
能够看到,Activity 的注册声明要放在 <application>
标签中,这里经过 <activity>
标签来对 Activity 进行注册。在<activity>
中经过 name
属性指定具体注册哪个 Activity,那么这里填入 .FirstActivity
,前面加 “.” 是由于最外层已经声明了 package
属性,在 name
的地方添加.FirstActivity
便可经过全类名找到 FirstActivity
。数据结构
通过了前面的步骤已经注册了 Activity,可是还不能运行程序,由于须要配置 Activity,也就是说须要指定最早启动哪一个 Activity。因此须要在 <activity>
标签中添加 intent-filter
标签,而后在 intent-filter
标签中添加 <action android:name="android.intent.action.MAIN" />
和 <category android:name="android.intent.category.LAUNCHER" />
两行声明便可,修改后的代码以下所示:app
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitytest">
<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=".FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
复制代码
这样,FirstActivity 就成了这个应用的主 Activity 了,点击应用图标最早打开的就是这个 Activity。可是若是没有在应用中声明任何一个 Activity 做为主 Activity,这个程序依然是能够安装的,只是没法在启动器中看到这个应用程序。这种程序一般做为第三方服务供其它应用在内部进行调用。编程语言
运行效果:ide
Toast 是 Adnroid 系统提供的一种很是好的提醒方式,在程序中可使用它将一些短小的信息通知给用户,这些信息在一段时间内会自动消失,而且不会占用任何屏幕空间。函数
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在此加载布局文件
setContentView(R.layout.first_layout)
val button1: Button = findViewById(R.id.button1)
button1.setOnClickListener {
// 使用 Toast
Toast.makeText(this, "You clicked Button 1",
Toast.LENGTH_SHORT).show()
}
}
}
复制代码
在 Activity 中经过 findViewById
找到布局文件中定义的元素,因为该方法会返回一个继承自 View
的泛型对象,所以 Kotlin 没法推导出获得的是什么类型的控件,因此须要将 button
显式的声明为 Button 类型,接着经过 setOnClickListener
注册一个监听器,点击按钮就会触发 onClick()
方法,因此在此书写代码逻辑,建立 Toast
首先须要传入三个参数,第一个是 Context
,在这里传入 this
便可,第二个参数是显示的文本内容,第三个参数是 Toast
显示的时长,点击按钮后会有以下效果:工具
上面的代码实现是经过findViewById
找到的控件,可是当控件过多时就会频繁的写这段代码,在使用 Java 开发时由于没法避免这种写法因此产生了 ButterKnife 之类的第三方开源库,可是在 Kotlin 中这个问题就不复存在了,由于使用 Kotlin 编写的 Android 程序在 app/build.gradle 文件中引入了 kotlin-android-extensions
插件,这个插件会根据布局文件中定义的控件的 id
自动生成一个具备相同名字的变量,咱们能够直接使用,从而替代 findViewById
。
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在此加载布局文件
setContentView(R.layout.first_layout)
// val button1: Button = findViewById(R.id.button1)
button1.setOnClickListener {
// 使用 Toast
Toast.makeText(this, "You clicked Button 1",
Toast.LENGTH_SHORT).show()
}
}
}
复制代码
由于手机屏幕不如电脑屏幕那么大,因此为了节省屏幕空间,Android 中提供了菜单这个功能,下面就来使用一下。
建立 menu 类型的文件夹,并建立 menu resource file 命名为 main
修改 main 的代码
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:title="Add" />
<item
android:id="@+id/remove_item"
android:title="Remove" />
</menu>
复制代码
在这里添加两个菜单选项,其中 id
属性时这个选项的惟一标识符,title
是显示的文本信息。
在 FirstActivity 中重写方法
/**
* 建立 menu
*/
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
// 加载 menu 资源文件
menuInflater.inflate(R.menu.main, menu)
return true
}
/**
* 经过 item.itemId 监听点击了哪一个 item,对点击了的 item 作出提示
*/
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.add_item ->
Toast.makeText(this, "You clicked Add",
Toast.LENGTH_SHORT).show()
R.id.remove_item ->
Toast.makeText(this, "You clicked Remove",
Toast.LENGTH_SHORT).show()
}
return true
}
复制代码
在onCreateOptionsMenu
方法中须要加载菜单资源文件,并指明菜单项添加到哪一个 menu 中,最后要返回 true
,若是返回 false
菜单就没法显示了,除此以外还要根据用户点击了的 item
作对应的逻辑处理,因此须要重写 onOptionsItemSelected
方法,而后经过 itemId
判断,并作出提示。
销毁 Activity 只须要在对应的方法中调用 finish()
便可。
button1.setOnClickListener {
finish()
}
复制代码
点击按钮会触发监听事件,而后回调用 finish()
方法完成 Activity 的销毁。
Intent 是 Android 中各组件之间进行交互的一种重要方式,它不只能够置名当前组件想执行的动做,还能够在不一样的组件之间传递数据。Intent 通常可用于启动 Activity、启动 Service以及发送广播等场景。Intent 大体分为两种:显式和隐式。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".SecondActivity">
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 2" />
</LinearLayout>
复制代码
button1.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
复制代码
在点击事件中首先须要建立一个 Intent
对象,而后在第一个参数中指出当前的环境上下文,第二个参数指定要跳转的页面,这里的SecondActivity::class.java
和 Java 中的 SecondActivity.class
做用一致。
隐式 Intent 要比显示 Intent 含蓄的多,他并不明确指定要启动哪一个 Activity,而是经过指定
action
和category
的信息,让系统去分析这个Intent
,并找出合适的 Activity 去启动。
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
复制代码
button1.setOnClickListener {
val intent = Intent("com.example.activitytest.ACTION_START")
startActivity(intent)
}
复制代码
要想使用隐式 Intent
启动 Activity,则必须匹配设置的 <action>
和 <category>
,在这里指定配置文件中 SecondActivity 中<action>
标签的内容,因为 <category>
的值是默认的,因此在这里不须要指定。
注意:一个 Intent 中只能够指定一个 <action>
可是能够指定多个 <category>
。
button1.setOnClickListener {
val intent = Intent("com.example.activitytest.ACTION_START")
intent.addCategory("com.example.activitytest.MY_CATEGORY")
startActivity(intent)
}
复制代码
注意:在指定多个 category
的同时,不要忘记在配置文件中添加 <category>
标签,不然会报错。
隐式 Intent 不只能够用来启动本身的 Activity,还能够启动其它程序的 Activity,这就让多个应用程序之间有了共享的可能。
使用 Intnet 代开百度
button1.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("http://www.baidu.com")
startActivity(intent)
}
复制代码
在这里将 action
指定为 ACTION_VIEW
,并将须要跳转的 URI 进行解析,设置到 data
属性中,能够实现点击按钮跳转到这个 URL 界面的效果。与此对应,还能够在 <intent-filter>
标签中再配置一个 <data>
标签,为了更精准的响应数据,还能够在 <data>
标签中设置以下属性:
<data>
标签中指定的内容和 Intent 中携带的 Data 数据彻底一致时,当前 Activity 才会响应该 Intent,不过 <data>
标签中通常不会指定过多的内容。 使用 Intent 打开拨号页面
button1.setOnClickListener {
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
}
复制代码
将 action
指定为 DIAL,并设置号码便可。
修改 FirstActivity 代码
button1.setOnClickListener {
val data = "Hello SecondActivity"
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("extra_data", data)
startActivity(intent)
}
复制代码
在这里使用 putExtra
方法一键值对的形式传递数据。
修改 SecondActivity 代码
val extraData = intent.getStringExtra("extra_data")
Log.d("SecondActivity", "extra data is $extraData")
复制代码
button1.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, 1)
}
复制代码
button2.setOnClickListener {
val intent = Intent()
intent.putExtra("data_return", "Hello FirstActivity")
setResult(RESULT_OK, intent)
finish()
}
复制代码
这里须要在 setResult
方法中传入状态码和intent
对象,并将 Activity 销毁掉/**
* 使用 startActivityForResult 启动的 Activity 被销毁后会回调
* 上一个 Activity 的 onActivityResult
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
1 -> if (resultCode == Activity.RESULT_OK) {
val returnedData = data?.getStringExtra("data_return")
Log.d("FirstActivity", "returned data is $returnedData")
}
}
}
复制代码
因为在一个 Activity 中可能会调用 startActivityForResult
方法去启动不少不一样的 Activity,每一个 Activity 返回的数据都会回调到 onActivityResult
方法,因此须要使用经过检查 requestCode
的值判断数据来源。
注意:按照上面的书写方式用户必须点击按钮才能够将数据返回,若是点击了 back 键则不会返回数据,要想改变这个状况,须要在 SecondActivity 中再重写一个方法。
重写方法处理 back 键问题
/**
* 在用户点击返回键后回调的方法,
* 用于解决用户点击返回键没法返回数据的问题
*/
override fun onBackPressed() {
val intent = Intent()
intent.putExtra("data_return", "Hello FirstActivity")
setResult(Activity.RESULT_OK, intent)
finish()
}
复制代码
Android 是使用任务来管理 Activity 的,一个任务就是一组存放在栈里的 Activity 的集合,这个栈也被称为返回栈(back stack)。栈是一种后进先出的数据结构,在默认状况下,每当咱们启动新的 Activity,它就会在返回栈中入栈,并处于栈顶的位置。而每当咱们按下 Back 键或调用 finish()
方法后,处于栈顶的 Activity 就会出栈,前一个入栈的 Activity 就会从新处在栈顶的位置,下图展现了返回栈如何管理 Activity 入栈出栈操做。
运行状态
当一个 Activity 处于栈顶时,Activity 就处于运行状态,系统最不肯回收的就是处于运行状态的 Activity,由于这会带来很是差的用户体验。
暂停状态
当一个 Activity 再也不处于栈顶的位置,但仍然可见,Activity 就进入了暂停状态。例如在 Activity 上面显示了一个对话框,可是并无彻底遮挡住 Activity,系统也不肯意回收这种 Activity,可是会在内存极低的状况下会考虑进行回收。
中止状态
当一个 Activity 再也不处于栈顶位置,而且彻底不可见的时候,就进入了中止状态。系统仍然会为这种 Activity 保存相应的状态和成员变量,可是这并非彻底可靠的,当其它地方须要内存时,处于中止状态的 Activity 有可能会被系统回收。
销毁状态
一个 Activity 从返回栈中移除后就变成了销毁状态。系统最倾向于回收这种状态的 Activity,以保证手机的内存充足。
Activity 类中定义了7个回调方法,覆盖了 Activity 生命周期的每个环节,下面就来介绍一下这7个方法。
onCreate()
该方法会在 Activity 第一次建立时进行调用,在这个方法中一般会作 Activity 初始化相关的操做,例如:加载布局、绑定事件等。
onStart()
这个方法会在 Activity 由不可见变为可见的时候调用。
onResume()
这个方法在 Activity 准备好和用户进行交互时调用,此时的 Activity 必定会位于栈顶,并处于运行状态。
onPause()
这个方法在系统准备去启动或者恢复另外一个 Activity 的时候调用。咱们一般会在这个方法中将一些消耗 CPU 的资源释放掉,以及保存一些关键数据,但这个方法的执行速度必定要快,否则会影响新的栈顶 Activity 的使用。
onStop()
这个方法会在 Activity 彻底不可见的时候调用。它和 onPause()
的主要区别在于,若是启动的新 Activity 是一个对话框式的 Activity,那么 onPause()
方法会获得执行,而 onStop()
方法并不会执行。
onDestroy()
这个方法在 Activity 销毁以前调用,以后 Activity 的状态将变成销毁状态。
onRestart()
这个方法在 Activity 由中止状态变为运行状态以前调用,也就是 Activity 被从新启动了。
注意:以上7个方法除了 onRestart()
方法,其它的都是两两相对的,从而又能够将 Activity 分为一下3种生存期。
完整生存期:
Activity 在 onCreate()
方法和 onDestroy()
方法之间所经历的就是完整生存期,通常状况下,一个 Activity 会在 onCreate()
方法中完成各类初始化操做,而在 onDestroy()
方法中完成释放内存的操做。
可见生存期:
Activity 在 onStart()
方法和 onStop()
方法之间所经历的就是可见生存期。在可见生存期内,Activity 对于用户老是可见的,即使有可能没法和用户进行交互,咱们也能够经过这两个方法合理的管理那些对用户可见的资源。好比在 onStart()
方法中对资源进行加载,而在 onStop()
方法中对资源进行释放,从而保证处于中止状态的 Activity 不会占用过多内存。
前台生存期:
Activity 在 onResume()
方法和 onPause()
方法之间所经历的就是前台生存期,在前台生存期内,Activity 老是处于运行状态,此时的 Activity 是能够和用户进行交互的,平时咱们接触最多的就是这个状态下的 Activity。
Activity 生命周期示意图:
将 DialogActivity 的主题设置为 Dialog 风格。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/startNormalActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start NormalActivity" />
<Button
android:id="@+id/startDialogActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start DialogActivity" />
</LinearLayout>
复制代码
class MainActivity : AppCompatActivity() {
private val tag = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(tag, "onCreate")
setContentView(R.layout.activity_main)
startNormalActivity.setOnClickListener{
val intent = Intent(this, NormalActivity::class.java)
startActivity(intent)
}
startDialogActivity.setOnClickListener{
val intent = Intent(this, DialogActivity::class.java)
startActivity(intent)
}
}
override fun onStart() {
super.onStart()
Log.d(tag, "onStart")
}
override fun onResume() {
super.onResume()
Log.d(tag, "onResume")
}
override fun onPause() {
super.onPause()
Log.d(tag, "onPause")
}
override fun onStop() {
super.onStop()
Log.d(tag, "onStop")
}
override fun onDestroy() {
super.onDestroy()
Log.d(tag, "onDestroy")
}
override fun onRestart() {
super.onRestart()
Log.d(tag, "onRestart")
}
}
复制代码
程序启动后:
点击 dialog button 后
当咱们在 ActivityA中进行了一些操做后,须要跳转到 ActivityB,这时 ActivityA 就进入了中止状态,在内存不足时可能会被回收,当用户从 ActivityB 返回后,ActivityA 就会被从新建立,这样以前保存的数据也就没有了,这显然是不合理的。
在 Android 中提供了一个 onSaveInstanceState()
方法,会在 ActivityA 被回收以前调用,所以咱们能够经过这个方法保存数据。保存的数据会存储在 Bundle
中,咱们能够经过 Bundle
对象取出数据。
/**
* 当 Activity 被销毁时会回调此方法,从而保存已存在的数据
*/
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.d(tag, "onCreate")
val tempData = "Something you Just typed"
outState.putString("data_key", tempData)
}
复制代码
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(tag, "onCreate")
setContentView(R.layout.activity_main)
if (savedInstanceState != null) {
val tempData = savedInstanceState.getString("data_key")
Log.d(tag, tempData)
}
}
复制代码
这里在 Bundle
不为空的状况下获取其中存储的数据并进行打印。Activity 的启动模式一共有4种,分别是:standard、singleTop、singleTask、singleInstance,具体使用哪一种启动模式要根据项目特定的需求,咱们能够在
AndroidManifest.xml
配置文件中经过给<activity>
标签指定android:launchMode
属性设置具体的启动模式。
standard 模式是 Activity 默认的启动模式,在不进行显式指定的状况下,全部 Activity 都会自动使用这种启动模式,在 standard 模式下,每当启动一个新的 Activity,他就会在返回栈中入栈,并处于栈顶的位置,对于使用 standard 模式的 Activity,系统不会在意 Activity 是否已经在返回栈中存在,每次启动都会建立一个该 Activity 的新实例。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
button1.setOnClickListener {
val intent = Intent(this, FirstActivity::class.java)
startActivity(intent)
}
}
复制代码
在这里每点击一次按钮就会启动一个新的 Activity。
上面的 standard 模式不管须要启动的 Activity 是否在栈顶,都会被从新建立,这或者有些不合理,可是使用 singleTop 模式会检查栈顶 Activity 是否是要启动的 Activity,若是是就不会再从新建立了。
button1.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
button2.setOnClickListener {
val intent = Intent(this, FirstActivity::class.java)
startActivity(intent)
}
复制代码
在这里只须要修改点击事件中须要跳转的页面。
前面使用的 singleTop 模式会判断要启动的 Activity 是否在栈顶,若是不在会进行建立,可是这样也会形成屡次建立的问题。可是使用 singleTask 模式就能够解决这个问题,它会判断返回栈中是否又要启动的 Activity 的实例,若是有则直接使用该实例,并将该 Activity 上的 Activity 实例所有出栈。
override fun onRestart() {
super.onRestart()
Log.d("FirstActivity", "onRestart")
}
复制代码
override fun onDestroy() {
super.onDestroy()
Log.d("SecondActivity", "onDestroy")
}
复制代码
singleInstance 启动模式和其它启动模式不一样的是它会启动一个新的返回栈来管理这个 Activity(若是 singleTask 模式指定了不一样的 taskAffinity,也会启动一个新的返回栈)。该启动模式主要是为了解决共享 Activity 实例的问题。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
Log.d("FirstActivity", "Task id is $taskId")
button1.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
}
复制代码
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("SecondActivity", "Task id is $taskId")
setContentView(R.layout.activity_second)
button2.setOnClickListener {
val intent = Intent(this, ThirdActivity::class.java)
startActivity(intent)
}
}
复制代码
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("ThirdActivity", "Task id si $taskId")
setContentView(R.layout.activity_third)
}
复制代码
点击 back 键后会发现直接返回到了 FirstActivity 中,再按 back 会返回到 SecondActivity,再次返回才会退出应用程序。产生这种结果是由于 FirstActivity 和 ThirdActivity 处于同一个返回栈,当这个返回栈为空后因而就会显示另外一个返回栈栈顶的 Activity。
Kotlin 的标准函数指的是 Standard.kt 文件中定义的函数,任何 Kotlin 代码均可以自由地调用全部的标准函数。
with 函数接收两个参数,第一个参数能够是一个任意类型的对象,第二个参数是一个 Lambda 表达式。with 函数会在 Lambda 表达式中提供第一个参数对象的上下文,并使用 Lambda 表达式中的最后一行代码做为返回值返回
val result = with(obj) {
// 这里是 obj 的上下文
"value" // with 函数的返回值
}
复制代码
它能够在连续调用同一个对象的多个方法时让代码变得更加精简
需求:有一个水果列表,如今咱们想吃完全部水果,并将结果打印出来
val list = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
val builder = StringBuilder()
builder.append("Start eating fruit. \n")
for (fruit in list) {
builder.append(fruit).append("\n")
}
builder.append("Ate all fruits")
val result = builder.toString()
println(result)
复制代码
val list = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
val result = with(StringBuilder()) {
append("Start eating fruit. \n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
复制代码
这里在上下文的位置传入了 StirngBuilder
对象,那么接下来整个 Lambda 表达式的上下文就是 StringBuilder
,在 Lambda 表达式中不须要写 builder.append()
或者 builder.toString()
,只须要简写成 append()
和 toString()
便可,而且 Lambda 的最后一行代码会做为 with
函数的返回值。run
函数的使用场景和 with
函数的使用场景很是相似,只是稍微作了一些语法改动而已。首先 run
函数时不能直接调用的,必需要调用某个对象的 run
函数才行,其次 run
函数只接受一个 Lambda 参数,而且会在 Lambda 表达式中提供调用对象的上下文。其它方面和 with 函数是同样的,包括也会使用 Lambda 表达式中的最后一行代码做为返回值。
val result = obj.run {
// 这里的 obj 是上下文
"value" // run 函数的返回值
}
复制代码
需求:使用 run
函数实现吃水果的代码
val list = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().run {
append("Start eating fruit. \n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
toString()
}
println(result)
复制代码
整体变化很是小,作出的改变只是须要使用对象调用 run
函数,参数只有 Lambda。
apply 函数和 run 函数也是极其相似的,都要在某个对象上调用,而且只接受一个 Lambda 参数,也会在 Lambda 表达式中提供调用对象的上下文,可是 apply 函数没法指定返回值,而是会自动返回调用对象自己。
val list = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
val result = StringBuilder().apply {
append("Start eating fruit. \n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
}
println(result.toString())
复制代码
这里与 run 函数的不一样体如今返回值须要本身在输出语句中调用,其它的都是相似的。
静态方法在某些编程语言里面又叫作类方法,指的就是这种不须要建立实例就能调用的方法,全部主流的编程语言都会支持静态方法这个特性。
在 Java 中定义一个静态方法很是简单,只须要在方法上加一个 static
关键字便可
public class Util {
public static void doAction() {
System.out.println("do action");
}
}
复制代码
这是一个简单的工具类,这个类中的方法只须要使用Util.doAction()
调用便可,于是静态方法很是适合编写工具类,觉得工具类一般没有建立实例的必要,基本是全局通用的。
在 Kotlin 中要想实现这种功能则须要使用单例类的方式实现,好比上述的Util
工具类使用 Kotlin 要这样写
object Util {
fun doAction() {
println("do action")
}
}
复制代码
虽然这里的 doAction()
方法并非静态方法,可是咱们仍然可使用 Util.doAction()
的方式来调用,这就是单例类所带来的便利性。
可是这么写会将单例类中的全部方法变成相似于静态方法的调用方式,若是只但愿让某一个方法变成静态方法的调用方式就可使用 companion object
了
class Util {
fun doAction1() {
println("do action1")
}
companion object {
fun doAction2() {
println("do action2")
}
}
}
复制代码
这里首相将 单例类改为了一个普通类,在普通类中的 doAction1()
方法必需要经过实例化 Util
类才能够调用,而在companion object
中定义的 doAction2()
方法则能够直接经过Util.doAction2()
的形式进行调用。
不过,doAction2()
方法其实也并非静态方法,companion object
这个关键字实际上会在 Util 类的内部建立一个伴生类,而 doAction2()
方法就是定义在这个伴生类里面的实例方法。只是 Kotlin 会保证 Util 类始终只会存在一个伴生类对象,所以调用 Util.doAction2()
方法实际上就是调用了Util 类中伴生对象的 doAction2()
方法。
因而可知,Kotlin 确实没有直接定义静态方法的关键字,可是提供了一些语法特性来支持相似于静态方法调用的写法。若是确实须要定义真正二点静态方法的话,在 Kotlin 中能够以注解和顶层方法实现。
注解方式
class Util {
fun doAction1() {
println("do action1")
}
companion object {
@JvmStatic
fun doAction2() {
println("do action2")
}
}
}
复制代码
经过添加 @JvmStatic
注解就可让 Kotlin 编译器将这个方法编译成真正的静态方法了。
注意:@JvmStatic
注解只能加在单例类和companion object
中的方法上。
顶层方法
顶层方法是指那些没有定义在任何类中的方法,好比 main()
方法,Kotlin 会将全部的顶层方法所有编译成静态方法,所以只要你定义了一个顶层方法,那么它必定是静态的。
fun doSomething() {
println("do something")
}
复制代码
这个方法定义在一个新的 .kt 文件中,那么这个方法就是一个顶层方法。
在 Kotlin 中要是想调用顶层方法,直接输入函数名便可。
若是想在 Java 文件中调用这个方法这么写是调用不到的,由于 Java 中的方法必须写在类里。
可是 Kotlin 的编译器会根据 Kotlin 文件的名称生成一个 Java 文件,文件名为 Kotlin文件名+Kt.java
,因此能够像下面这种方式进行调用。