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
,因此能夠像下面這種方式進行調用。