Actuvity 是最容易吸引用户的地方,它是一种能够包含用户界面的组件,主要用于和用户进行交互。一个应用中能够包含0个或多个 Activity,但不包含任何 Activity 的应用程序是没法被用户看见的。
点击 Empty Activity 建立名为 FirstActivity 的 Activityjava
勾选 Generate Layout File 表示会建立一个对应的布局文件,勾选Launcher Activity 表示会将 FirstActivity 设置为当前项目的主 Activity,点击Finish。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()
方法便可。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"></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" />
两行声明便可,修改后的代码以下所示:ide
<?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,这个程序依然是能够安装的,只是没法在启动器中看到这个应用程序。这种程序一般做为第三方服务供其它应用在内部进行调用。函数
运行效果:工具
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 中提供了菜单这个功能,下面就来使用一下。
修改 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 中重写方法
/**
*/ 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")
修改 FirstActivity
button1.setOnClickListener { val intent = Intent(this, SecondActivity::class.java) startActivityForResult(intent, 1) }
修改 SecondActivity
button2.setOnClickListener { val intent = Intent() intent.putExtra("data_return", "Hello FirstActivity") setResult(RESULT_OK, intent) finish() }
这里须要在 setResult
方法中传入状态码和intent
对象,并将 Activity 销毁掉
在 FirstActivity 中重写方法
/** * 使用 startActivityForResult 启动的 Activity 被销毁后会回调
*/ 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个方法。
该方法会在 Activity 第一次建立时进行调用,在这个方法中一般会作 Activity 初始化相关的操做,例如:加载布局、绑定事件等。
这个方法会在 Activity 由不可见变为可见的时候调用。
这个方法在 Activity 准备好和用户进行交互时调用,此时的 Activity 必定会位于栈顶,并处于运行状态。
这个方法在系统准备去启动或者恢复另外一个 Activity 的时候调用。咱们一般会在这个方法中将一些消耗 CPU 的资源释放掉,以及保存一些关键数据,但这个方法的执行速度必定要快,否则会影响新的栈顶 Activity 的使用。
这个方法会在 Activity 彻底不可见的时候调用。它和 onPause()
的主要区别在于,若是启动的新 Activity 是一个对话框式的 Activity,那么 onPause()
方法会获得执行,而 onStop()
方法并不会执行。
这个方法在 Activity 销毁以前调用,以后 Activity 的状态将变成销毁状态。
这个方法在 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") } }
程序启动后:
点击 normal button
点击返回按钮
点击 dialog button 后
点击返回按钮后
在 MainActivity 中点击 back 键
当咱们在 ActivityA中进行了一些操做后,须要跳转到 ActivityB,这时 ActivityA 就进入了中止状态,在内存不足时可能会被回收,当用户从 ActivityB 返回后,ActivityA 就会被从新建立,这样以前保存的数据也就没有了,这显然是不合理的。
在 Android 中提供了一个 onSaveInstanceState()
方法,会在 ActivityA 被回收以前调用,所以咱们能够经过这个方法保存数据。保存的数据会存储在 Bundle
中,咱们能够经过 Bundle
对象取出数据。
重写方法
/**
*/ override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) Log.d(tag, "onCreate") val tempData = "Something you Just typed" outState.putString("data_key", tempData) } ```
修改 onCreate 方法
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。
由于点击了3次按钮,因此又建立了3个新的 Activity,因此须要点击4次back键才能够退回到桌面
上面的 standard 模式不管须要启动的 Activity 是否在栈顶,都会被从新建立,这或者有些不合理,可是使用 singleTop 模式会检查栈顶 Activity 是否是要启动的 Activity,若是是就不会再从新建立了。
这样设置以后会发现不管怎么点击都不会新建新的 Activity,由于在这个模式下会先对须要启动的 Activity 进行检查,看看它是否是在栈顶。并且在这个模式下只须要点击一次 back 键就能够回退到桌面。
button1.setOnClickListener { val intent = Intent(this, SecondActivity::class.java) startActivity(intent) } button2.setOnClickListener { val intent = Intent(this, FirstActivity::class.java) startActivity(intent) }
在这里只须要修改点击事件中须要跳转的页面。
在这里点击了两次按钮,第一次由于 SecondActivity 不在栈顶,因此会建立 SecondActivity,当再次点击时会发现 FirstActivity 不在栈顶,因此又会新建 FirstActivity,当点击返回时,会首先回退到 SecondActivity,再次点击会回退到 FirstActivity,再点击一次 back 才会返回到桌面。
前面使用的 singleTop 模式会判断要启动的 Activity 是否在栈顶,若是不在会进行建立,可是这样也会形成屡次建立的问题。可是使用 singleTask 模式就能够解决这个问题,它会判断返回栈中是否又要启动的 Activity 的实例,若是有则直接使用该实例,并将该 Activity 上的 Activity 实例所有出栈。
override fun onRestart() { super.onRestart() Log.d("FirstActivity", "onRestart") }
override fun onDestroy() { super.onDestroy() Log.d("SecondActivity", "onDestroy") }
经过运行结果能够看出,在 SecondActivity 中启动 FirstActivity 时,首先会对返回栈中是否有 FirstActivity 的实例进行检查,在这里确定是有的,因此就会将 SecondActivity 的实例出栈,将 FirstActivity 的实例放在栈顶,点击一次back键就能够退回到桌面。
singleInstance 启动模式和其它启动模式不一样的是它会启动一个新的返回栈来管理这个 Activity(若是 singleTask 模式指定了不一样的 taskAffinity,也会启动一个新的返回栈)。该启动模式主要是为了解决共享 Activity 实例的问题。
FirstActivity
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) } }
SecondActivity
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) } }
ThirdActivity
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d("ThirdActivity", "Task id si $taskId") setContentView(R.layout.activity_third) }
根据运行结果能够看出 SecondActivity 的返回栈 id 和 其它两个不一样,这说明 SecondActivity 处于单独的一个返回栈。
点击 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)
使用 with 函数
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
,因此能够像下面这种方式进行调用。