本篇内容是根据《第一行代码》中的内容写的,相当于做了个笔记。
1.Activity的基本用法
2.使用Intent跳转Activity
3.Activity的生命周期
4.Activity的启动模式
5.Kotlin进阶知识①
①Generate Layout File表示会自动为你创建的Activity创建一个对应的布局文件
②Launcher Activity表示会自动将你创建的Activity设置为当前项目的主Activity
③Backwards Compatibility表示会为项目启动向下兼容旧版系统的模式(一般都要勾上)
注意:项目中的任何Activity都应该重写onCreate()方法
①最好是每一个Activity都能对应一个布局(事实也是基本如此),布局是用来显示界面内容的。
②项目中添加的任何资源都会在R文件中生成一个相应的资源id,所以可以这样得到某一个layout:R.layout.myLayout
①所有的Activity都要在AndroidManifest.xml中进行注册才能生效
②若将某个Activity设置为主Activity(点击桌面应用程序图标时首先打开的Activity),则只需要在activity标签内部加入intent-filter标签,并且在此标签中添加
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
这两句声明即可
③还可以使用android:label指定Activity中标题栏的内容,标题栏是显示在Activity最顶部的,除此之外,给主Activity指定的label不仅会成为标题栏中的内容,还会成为启动器(Launcher)中应用程序显示的名称
比如
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="FirstActivity"
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>
①基本概念:Toast是一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。
②使用示例
Toast.makeText(this,"这里是Toast",Toast.LENGTH_LONG).show()
解释:makeText是一个静态方法,调用此方法可以创建出一个Toast对象。
第一个参数是 Context,因为Activity本身就是一个Context对象,因此直接是this即可。
第二个参数是显示的内容。
第三个参数是时间,有Toast.LENGTH_LONG和Toast.LENGTH_SHORT,对应长时间和短时间。
我们可以通过findViewById()方法来通过某个控件的id得到此控件,但是这样过于麻烦。
在Kotlin中,由于使用Kotlin编写的Android项目在app/build.gradle文件的头部默认引入了一个kotlin-android-extensions插件,这个插件会根据布局文件中定义的控件id自动生成一个具有相同名称的变量,我们可以直接在Activity里直接使用这个变量,而不再调用findViewById()方法了
(kotloin-android-extensions这个插件背后也是通过调用findViewById()方法来实现的)
(1)基本概念:这个Menu可以理解为一般在应用右上角的三个点,点击后会弹出一个菜单。这种方式可以让菜单都能得到展示,还不占用任何屏幕空间
(2)使用:
①右击res目录-》New-》Directory,输入文件夹名称“menu”
②在这个文件夹下新建一个名叫main的菜单文件,也就是右击menu文件夹-》New-》Menu Resource file,文件名输入main,然后创建
③item标签是用来创建具体的某一个菜单项
android:id是给这个菜单项指定一个唯一的标识符
android:title是给这个菜单项指定一个名称
比如
<?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>
④然后在Activity里面重写这个方法,可以让菜单得到显示
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main,menu)
return true
}
解释:menuInflater实际上是调用了父类的getMenuInflater()方法(类似于intent得到传入数据时的写法),getMenuInflater()能够得到一个MenuInflater对象,再调用它的inflate()方法,就可以给当前的Activity创建菜单了
第一个参数是用于指定我们通过哪一个资源文件来创建菜单,毫无疑问是R.menu.main。
第二个参数用于指定我们的菜单项将添加到哪一个Menu对象当中,这里直接使用onCreateOptionsMenu()方法中传入的menu参数。
最后给这个方法返回true,表示允许创建的菜单显示出来
以上只是做了菜单显示,这是远远不够的,我们还要定义菜单响应事件
⑤在Activity中重写onOptionsItemSelected()方法
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
R.id.add_item -> Toast.makeText(this,"您点击了add_item",Toast.LENGTH_LONG).show()
R.id.remove_item -> Toast.makeText(this,"您点击了 remove_item",Toast.LENGTH_LONG).show()
}
return true
}
有两种方式
①按返回键
②调用finish()方法
效果是一样的
Intent是安卓程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。一般可用于启动Activity,启动Service以及发送广播等场景。
①一般需要调用这个方法
Intent(Context packageContext,Class<?>cls)
第一个参数Context要求提供一个启动Activity的上下文,第二个参数Class用于指定想要启动的目标Activity
②启动时就需要使用
startActivity()方法或者后面讲到的startActivityForResult()方法,前者接收一个Intent参数,后者不仅接收Intent参数,还接收请求码
③使用示例
button.setOnClickListener {
val intent = Intent(this,secondActivity::class.java)
startActivity(intent)
}
(secondActivity::class.java就相当于Java中secondActivity.class写法)
由于使用这种方式来启动Activity,Intent的“意图”非常明显,因此称之为显式Intent
隐式Intent不明确指出想要启动哪一个Activity,而是制定了一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并帮我们找出合适的Activity(也就是能响应这个隐式Intent的Activity)去启动
①前期准备
通过在activity标签下配置intent-filter的内容,可以指定当前Acticity能够响应的action和category
<activity android:name=".secondActivity">
<intent-filter>
<action android:name="android.intent.action.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
在action标签中我们指明了当前Activity可以响应android.intent.action.ACTION_START这个action,而category标签则包含了一些附加信息,更精确地指明了当前Activity能够响应的Intent中还可能带有的category。只有action和category中的内容同时匹配Intent中指定的action和category时,这个Activity才能响应该Intent
注意:每个intent中只能指定一个action,但能指定多个category
使用起来就是
button.setOnClickListener {
val intent = Intent("android.intent.action.ACTION_START")
startActivity(intent)
}
android.intent.category.DEFAULT是一种默认的category,在调用startActivity方法的时候会自动将这个category添加到Intent中。如果觉得这个默认的不行,还可以再在Manifest中添加一个category,然后再使用addCategory方法添加,代码如下
button.setOnClickListener {
val intent = Intent("android.intent.action.ACTION_START")
intent.addCategory("android.intent.category.DEFAULT")
startActivity(intent)
}
隐式Intent不仅可以启动自己程序内的Activity,还可以启动其他程序的Activity,这就使得多个应用程序之间的功能共享成为了可能
比如
button.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)
}
点击按钮后就会启动百度。
注意:这里的intent.data实际上调用了Intent的setData()方法,它接收一个Uri对象,主要用于指定当前Intent正在操作的数据。
我们也可以再intent-filter标签中再配置一个data标签,用于更精确地指定当前Activity能够响应地数据。只有当data标签中指定的内容和Intent中携带的Data完全一致时,当前Activity才能够响应该Intent,不过,一般再在data标签中不会指定过多内容,比如在上面的例子中,其实只需要指定android:scheme为https,就可以响应所有https协议的Intent了
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"/>
</intent-filter>
比如我们把某一个Activity的intent-filter改成这样,那么它就应该和浏览器一样,能够响应一个打开网页的Intent了,到时候启动的时候系统就会弹出一个列表,显示目前能够响应这个Intent的所有程序,一个当然是浏览器,另外一个就是此Activity
Intent提供了一系列putExtra()方法的重载,存放数据是以键值对的方式进行的。
可以把我们想要的数据暂时存在Intent中,在启动另一个Activity后,只要把这些数据从Intent中取出来就可以了
比如在某一Activity中
button.setOnClickListener {
val intent = Intent(this,secondActivity::class.java)
intent.putExtra("first","传递的数据")
startActivity(intent)
}
另一Activity接收数据的时候就可以这样
val extraData = intent.getStringExtra("first")
Log.v("ljh",extraData)
}
注意:这里的intent实际上调用的是父类的getIntent()方法,该方法会获取用于启动该Activity的Intent
①要把startActivity换为startActivityForResult,它接收两个参数,第一个还是Intent,第二个是请求码,用于在之后的回调中判断数据的来源。
②setResult方法
在FirstActivity中
button.setOnClickListener {
val intent = Intent(this,secondActivity::class.java)
startActivityForResult(intent,1)
}
secondActivity中
button2.setOnClickListener {
val intent = Intent()
intent.putExtra("first","返回的数据")
setResult(Activity.RESULT_OK,intent)
finish()
}
这里的intent没有任何的“意图”,仅仅是用于传递数据而已。
setResult方法非常重要,第一个参数用于向上一个Activity返回处理结果,一般只使用Activity.RESULT_OK或者Activity.RESULT_CANCELED,第二个参数则把带有数据的Intent传递回去
③在secondActivity被销毁之后会回调上一个Activity的onActivityResult方法,所以我们要在FirstActivity中重写这个方法来得到返回的数据
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
1 -> if(resultCode == Activity.RESULT_OK){
val returneData = data?.getStringExtra("first")
Log.v("ljh",returneData)
}
}
}
首先要通过检查requestCode的值来判断数据来源,再通过resultCode的值来判断处理结果是否成功
如果用户没有点击按钮,而是按了返回键,那么这样该如何返回数据呢?
我们只需要在secondActivity中重写onBackPressed方法就可以解决这个问题,方法里面的内容和上面返回数据的代码内容一样
override fun onBackPressed() {
val intent = Intent()
intent.putExtra("first","返回的数据")
setResult(Activity.RESULT_OK,intent)
finish()
}
也就是说这个方法就是用户按返回键时你需要执行的逻辑
安卓是利用任务(task)来管理Activity的,一个任务就是一组堆放在栈里的Activity的集合,这个栈叫做返回栈。栈是一种后进先出的数据结构。在默认情况下,每当我们启动了一个新的Activity,它就会在返回栈中入栈,并且出于栈顶的位置。而每当我们按下返回键或者调用finish()方法去销毁一个Activity时,处于栈顶的Activity就会出栈,前一个入栈的Activity就会重新处于栈顶的位置。系统总是会显示处于栈顶的Activity给用户。
此时Activity处于栈顶。
系统最不愿意回收此时的Activity,因为这会带来极差的用户体验
此时Activity虽然不再处于栈顶位置,但仍然可见。(并不是每个Activity都会占满整个屏幕,比如对话框形式的Actiivity只会占用屏幕中间的部分区域)
只有在内存极低的情况下,系统才会考虑回收这种Activity
此时Activity不再处于栈顶的位置并且完全不可见。
此时的Activity有可能会被系统回收
一个Activity从返回栈中移除后就变成了销毁状态。
系统最喜欢回收这样的Activity
在Activity第一次被创建的时候调用
当Activity由不可见变为可见的时候调用
在Activity准备好和用户进行交互的时候调用,此时的Activity一定处于返回栈的栈顶
在系统准备去启动或者恢复另一个Activity的时候调用。
通常在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据
在Activity完全不可见的时候调用
和onPause()的区别:如果启动的新Activity是一个对话框形式的Activity,那么onPause方法会执行,而onStop不会执行
在Activity被销毁之前调用,之后Activity的状态将会变为销毁状态
在Activity由停止状态变为运行状态之前调用
此时意味着Activity被重新启动了
在onCreate和onDestory之间是完整生存期。一般一个Activity会在onCreate中完成各种初始化操作,在onDestory中完成释放内存的操作
在onStart和onStop之间是可见生存期。在此期间Activity对于用户总是可见的,即便有可能无法和用户进行交互。
在onResume和onPause之间是前台生存期,在此期间Activity总是处于运行状态,可以和用户进行交互。
我们平时看到和接触最多的就是这个状态下的Activity
onSaveInstanceState方法可以保证Activity在被回收之前一定被调用。
从这个方法中保存数据,从onCreate中获得数据
使用示例如下
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("你输入的","something you just typed")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(savedInstanceState != null){
val temoString = savedInstanceState.getString("你输入的")
}
}
可以在Manifest中通过给activity标签指定 android:launchMode来选择启动模式
standard是Activity默认的启动模式。在此模式下,系统不会在乎某个Activity是否已经在返回栈中存在,每次启动都会创建一个该Activity的新实例。
在此模式下,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则认为可以直接使用它,不会在创建新的Activity实例。不过如果该Activity未处于栈顶位置时,再启动Activity时还是会创建新的实例
在此模式下,每次启动Activity时,系统首先会在返回栈中检查是否存在该Activity的实例,如果发现已经存在则直接使用该实例,并把在这个Activity之上的所有其他Activity统统出栈,如果没有发现就会创建一个新的Activity实例
指定为singleInstance模式的Activity会启用一个新的返回栈来管理这个Activity。
用途:假如我们的程序中有一个Activity是允许其他程序调用的,如果想实现其他程序和我们的程序可以共享这个Activity的实例,该如何做呢?
首先使用前面三种启动模式是肯定做不到的,因为每个应用程序都会有自己的返回栈,同一个Activity在不同的返回栈中入栈时必然创建了新的实例。而使用singleInstance模式就可以解决这个问题。在这种模式下,会有一个单独的返回栈来管理这个Activity,不管是哪个应用程序来访问这个Activity,都共用同一个返回栈,也就解决了共享Activity的问题。
Kotlin中的javaClass表示获取当前实例的Class对象,相当于在Java中调用getClass()方法
而Kotlin中 A ::class.java表示获取A类的Class对象,相当于在Java中调用A.class
Kotlin的标准函数指的是Standard.kt文件中定义的函数,任何Kotlin代码都可以自由地调用所有的标准函数
它接收两个参数,第一个参数是一个任意类型的对象,第二个参数是一个Lambda表达式,with函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回
使用示例如下
val a = A()
with(a){
//这里是a的上下文
"value"//with函数的返回值
}
它和with非常像.
区别:首先run函数是不能直接调用的,而是一定要调用某个对象的run函数才行。其次run函数只接收一个Lambda参数,并且会在Lambda表达式中提供调用对象的上下文。其他的和with没啥区别了
使用示例
val a = A()
a.run {
//这里是a的上下文
"value"//run函数的返回值
}
apply和run非常像,只不过apply函数无法指定返回值,而是会自动返回调用对象本身
使用示例
val a1 = a.apply {
//这里是a的上下文
}
这里 a1 == a
Kotlin极度弱化了静态方法这个概念,因为Kotlin提供了比静态方法更好用的语法特性,也就是单例类
object Util{
}
这种写法会将整个类中的所有方法全部变成类似于静态方法的调用方式
class Util{
companion object{
fun doAction(){
}
}
}
这里的doAction虽然可以Util.doAction去调用,但是它不是真正的静态方法,companion object这个关键字实际上会在Util类的内部创建一个伴生类,而doAction()方法就是定义在这个伴生类里面的实例方法。只是Kotlin会保证Util类始终只会存在一个伴生类对象,因此调用Util.doAction()方法实际上就是调用了Util类中伴生对象的doAction方法
上面的都不是真正的静态方法,而注解和下面讲的顶层方法才是真正的静态方法
注解使用:给单例类或companion object中的方法加上@JvmStatic注解
class Util{
companion object{
@JvmStatic
fun doAction(){
}
}
}
注意:这个注解只能加在单例类或者companion object中的方法上。
这个注解其实并不常用
顶层方法指的是那些没有定义在任何类中的方法,Kotlin编辑器会将所有的顶层方法全部编译成静态方法。因此如果定义了一个顶层方法,那么它一定是静态方法。
使用:只需要创建一个Kotlin文件(类型选File),比如输入Helper,那么刚刚的包名路径下就会出现一个Helper.kt文件,在这个文件中定义的任何方法都是顶层方法
比如定义一个doSomething()方法,在Kotlin代码中就可以直接输入doSomething()调用。
如果在Java代码中:我们刚才创建的Kotlin文件名叫Helper.kt,那么Kotlin编译器会自动创建一个叫HelperKt的Java类,doSomething()方法就是以静态方法的形式定义在HelperKt类里面的,到时候就可以使用HelperKt.doSomething()的写法来调用了