Android-Kotlin笔记
Kotlin
类class
定义
1 | open class Person {//open:可被继承,一个类默认(无open)不可被继承 |
实例化
1 | fun main() { |
继承
1 | class Student(val sno:String, val grade:Int):Person() {//Person后的括号代表Student类的主构造函数在初始化时会调用Person类的无参数构造函数 |
接口
1 | interface Study { |
1 | class Student(name:String, age:Int) : Person(name,age), Student { |
在main()函数中调用这两个接口中的函数:
1 | fun main() { |
对接口中定义的函数进行默认实现:
1 | interface Study { |
数据类
1 | data class Cellphone(val brand: String, val price: Double) |
单例类
1 | //创建单例类: |
Lambda编程
集合的创建与遍历
1 | val list = listOf("Apple","Banana","Orange","Pear","Grape")//不可变集合 |
set集合与list集合类似,不过set集合不能存放重复的元素,如果存放,则只会保留其中的一份
**map集合:**Map是一种键值对形式的数据结构
1 | fun main() { |
集合的函数式API
Java函数式API的使用
空指针检查
可空类型系统
**定义:**在类名后加上?
。如Int表示不可空的整型,Int?表示可空的整型。
Kotlin默认参数和变量都不可空
判空辅助工具
?.
:当对象不为空时调用相应函数。1
2
3if (a != null) {
a.doSomething()
}↓↓↓↓↓↓↓↓↓↓↓↓
1
a?.doSomething()
?:
:左右两边各接收一个表达式,如果左边不为空则返回左边表达式,反之返回右边表达式。1
2
3
4
5
6fun getTextLength(text: String?): Int {
if (text != null) {
return text.length
}
return 0
}↓↓↓↓↓↓↓↓↓↓↓↓
1
fun getTextLength(text: String?): text?.length ?: 0
非空断言工具
!!
:在对象后加上!!
意在告诉Kotlin,我非常确信这里的对象不会为空,所以不用你来帮我做空指针检查了,如果出现问题,你可以直接抛出空指针异常,后果由我自己承担。
let函数
1 | obj.let { obj2 -> |
1
2
3
4 fun doStudy(study:Study?) {
study?.readBooks()
study?.doHomework()
}↓↓↓↓↓↓↓↓↓↓↓↓
1
2
3
4
5
6 fun doStudy(study:Study?) {
study?.let {
it.readBooks()//只有一个参数时可以不声明参数名,直接用it代替
it.doHomework()
}//let函数可处理全局变量的判空问题,if则不行
}
小技巧
字符串内嵌表达式
1 | val brand = "Samsung" |
函数的参数默认值
1 | fun printParams(num:Int = 100, str:String) { |
↓↓↓↓↓↓↓↓↓↓↓↓
1 | num is 100, str is world |
活动Activity
创建Acitvity
新建项目,选择
No Activity
右键
app/src/main/java/com.example.(activitytest)
→New
→Activity
→Empty Activity
- 勾选Generate Layout File表示会自动为FirstActivity创建一个对应的布局文件
- 勾选Launcher Activity表示会自动将FirstActivity设置为当前项目的主活动(步骤6)
右键
app/src/main/res
→New
→Directory(目录)
命名为layout
右键
layout
→New
→Layout resource file
布局文件命名为(first_layout),根元素就默认选择为 LinearLayout
1
2
3
4
5<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>在onCreate() 方法中加入
1
2super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);修改
AndroidManifest.xml
文件,将此Activity设为这个程序的主活动1
2
3
4
5
6
7
8
9
10
11
12<activity
android:name=".FirstActivity"
android:label="This is FirstActivity"
android:exported="true">
<!--android:label=""可以更改当前页面的标题-->
<!--插入以下内容-->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-------------->
</activity>
日志工具Log
Log.d(tag,msg)
tag:一般传入当前类名 msg:想要打印的具体内容
Toast
在onCreate()
方法中添加如下代码:
1 | var button1 =findViewById<Button>(R.id.button_1)//findViewById<类型>(R.id.ID名) |
Menu
在
res
目录下新建一个menu
文件夹接着在这个文件夹下再新建一个名叫
main
的菜单文件,右击menu
文件夹→New
→Menu resource file
在main.xml中添加如下代码:
1
2
3
4
5
6
7
8
9
10<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>重新回到
FirstActivity.kt
中,显示出Menu1
2
3
4
5override fun onCreateOptionsMenu(menu: Menu?): Boolean {//输入override fun onCreateOptionMenu后点Tab
menuInflater.inflate(R.menu.main,menu)//插入此行代码
return super.onCreateOptionsMenu(menu)
}
/*menuInflater方法能够得到MenuInflater对象,再调用它的inflate()方法就可以给当前活动创建菜单了。inflate()方法接收两个参数,第一个参数用于指定我们通过哪一个资源文件来创建菜单,这里当然传入R.menu.main。第二个参数用于指定我们的菜单项将添加到哪一个Menu对象当中,这里直接使用onCreateOptionsMenu()方法中传入的menu参数。*/FirstActivity.kt
中,定义菜单响应事件1
2
3
4
5
6
7override fun onOptionsItemSelected(item: MenuItem): Boolean {//输入override fun onOptionsItemSelected后点Tab
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 super.onOptionsItemSelected(item)
}//此方法与4.方法同放在类FirstActivity中
销毁一个活动
finsh()
效果和按下Back键是一样的
使用Intent在活动之间穿梭
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景。
创建SecondActivity:
生成
SecondActivity.kt
和second_layout.xml
(勾选Generate Layout File)将
second_layout.xml
改成和first_layout.xml
一样,但button_1
改为button_2
显性Intent
Intent有多个构造函数的重载,其中一个是Intent(Context packageContext, Class cls) 。这个构造函数接收两个参数,第一个参数Context 要求提供一个启动活动的上下文,第二个参数Class则是指定想要启动的目标活动,通过这个构造函数就可以构建出Intent的“意图”。
修改FirstActivity
的按钮的点击事件:
1 | var button1 =findViewById<Button>(R.id.button_1) |
隐式Intent
相比于显式Intent,隐式Intent则含蓄了许多,它并不明确指出我们想要启动哪一个活动,而是指定了一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并帮我们找出合适的活动去启动。
- 通过在标签下配置的内容,可以指定当前活动能够响应的action和category,打开AndroidManifest.xml,添加如下代码:
1 | <activity android:name=".SecondActivity"> |
在
<activity>
标签中我们指明了当前活动可以响应 com.example.activitytest.ACTION_START这个action,而<category>
标签则包含了一些附加信息,更精确地指明了当前的活动能够响应的Intent中还可能带有的category。只有<activity>
和<category>
中的内容同时能够匹配上Intent中指定的action和category时,这个活动才能响应该Intent。
修改FirstActivity中按钮的点击事件:
1
2var intent = Intent("com.trrrrw.activitytest.ACTION_START")
startActivity(intent)android.intent.category.DEFAULT 是一种默认的category,在调用startActivity()方法的时候会自动将这个category添加到Intent中。
每个Intent中只能指定一个action ,但却能指定多个category。
修改FirstActivity中按钮的点击事件:
1
2
3var intent = Intent("com.trrrrw.activitytest.ACTION_START")
intent.addCategory("com.trrrrw.activitytest.MY_CATEGORY");
startActivity(intent)此时点击Button 1程序会崩溃,因为SecondActivity的
<intent-filter>
标签中并没有声明可以响应这个category 。在SecondActivity的
<intent-filter>
标签中加入<category android:name="com.trrrrw.activitytest.MY_CATEGORY"/>
即可
更多隐式Intent用法
启动其他程序的Intent
- 浏览器打开网页:
1 | button_5.setOnClickListener { |
在
<intent-filter>
标签中配置<data>
标签,用于精确指定当前Activity能够相应的数据。android:scheme
用于指定数据的协议部分;http
android:host
用于指定数据的主机名部分;www.bilibili.com
android:port
用于指定数据的端口部分;一般紧随主机名后
android:mimeType
用于指定可以处理的数据类型;允许使用通配符进行指定
只有当
<data>
标签中指定内容与Intent携带的Data完全一致时再能相应新建Activity相应网页:
- 新建ThirdActivity:
1
2
3
4
5
6
7
8
9<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/second_button_1"
android:id="@+id/second_button_1"/>
</LinearLayout>- 更改AndroidMainfest中代码:
1
2
3
4
5
6
7<activity android:name=".ThirdActivity" android:exported="true">
<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>再点击MainActivity中打开网页就可以选择Demo打开
向下一个活动传递数据
在MainActivity中将想传递的数据暂存在Intent中:
1 | val button_6 = findViewById<Button>(R.id.button_6) |
在SecondActivity中将传递的数据取出:
1 | val extraData = intent.getStringExtra("extra_data") |
返回数据给上一个活动
startActivityForResult()
方法能够在Activity销毁的时候返回一个结果给上一个Activity。
在FirstActivity中添加按钮
button_7
打开SecondActivity1
2
3
4
5val button_7 = findViewById<Button>(R.id.button_7)
button_7.setOnClickListener{
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, 1)//(Intent, 请求码[唯一值即可])
}在SecondActivity中添加按钮点击后返回
EditText
中的文本并关闭该Activity1
2
3
4
5
6
7
8
9val second_button_2 = findViewById<Button>(R.id.second_button_2)
second_button_2.setOnClickListener {
var data = findViewById<EditText>(R.id.second_edittext_1)
var sdata = data.text.toString()
val intent = Intent()
intent.putExtra("data_return", sdata)
setResult(RESULT_OK, intent)
finish()
}//或者将此方法写在onBackPressed()中,用户点击返回按钮后会返回`EditText`中的文本使用
startActivityForResult()
方法打开SecondActivity,在SecondActivity销毁后会回调上一个Activity的onActivityResult()
方法1
2
3
4
5
6
7
8
9
10
11
12override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
1 -> if (resultCode == RESULT_OK) {
val returnedData = data?.getStringExtra("data_return")
val button_7 = findViewById<Button>(R.id.button_7)
button_7.setText("returned data is $returnedData")
//将之前打开SecondActivity的按钮上文字改为返回的值
}
}
}//该方法带有三个参数:requestCode为启动Activity时传入的请求码;resultCode为返回数据时传入的结果;data为携带返回数据的Intent
//通过检测requestCode来判断数据来源
活动的生命周期
返回栈
任务:一组放在栈里的活动的集合
启动一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。
Back键或finish()会销毁栈顶的活动
Activity状态
运行状态:位于栈顶,系统最不愿回收
暂停状态:不处于栈顶,但仍可见,系统也不会回收
停止状态:不处于栈顶也不可见,当其他地方需要内存时,可能会被系统回收
销毁状态:从返回栈中移除后的状态,系统倾向于回收此状态的活动
Activity的生存期
- onCreate()——在活动第一次被创建时调用
- onStart()——在活动由不可见变为可见的时候调用
- onResume()——在活动准备好与用户进行交互的时候调用,此时活动位于栈顶,并处于运行状态
- onPause()——在系统准备去启动或者回复另一个活动的时候调用,通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
- onStop()——在活动完全不可见的时候调用,它和 onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行。
- onDestory——在活动被销毁之前调用,之后活动变为销毁状态
- onRestart——在活动由停止状态变为运行状态之前调用,活动被重新启动
生存期
- 完整生存期:onCreate()与onDestory()之间。一般情况下,一个活动会在onCreate()方法中完成各种初始化操作,而在onDestory()方法中完成释放内存的操作。
- 可见生存期:onStart()与onStop()之间。期间活动对用户总是可见的,即使可能无法交互。可用于合理管理用户可见的资源,如在onStart()中对资源进行加载,在onStop()中对资源进行释放从而保证处于停止状态的活动不会占用过多内存。
- 前台生存期:onResume()与onPause()之间。期间活动总是处于运行状态,且可与用户进行交互。
onSaveInstanceState() 回调方法
onSaveInstanceState() 方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型数据,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面从Bundle中取值,第二个参数是真正要保存的内容。
- 在MainActivity中添加如下代码就可以将临时数据进行保存:
1 | override fun onSaveInstanceState(outState: Bundle) { |
- 修改onCreate()方法:
1 | if (savedInstanceState!=null){ |
Activity的启动模式
standard
活动的默认启动模式,每次启动系统都会为活动创建新的实例放在栈顶。
singleTop
以该模式启动,如果返回栈栈顶是该活动,则会直接使用,不会创建新的活动实例;若不是,则创建新的活动实例。
singleTask
以该模式启动,如果该活动不在栈顶,则将其上方所有活动全部出栈;如果没有,则创建新的活动实例。
singleInstance
以该模式启动,会为该活动创建一个新的返回栈。按Back键会回到该栈中的上一个活动,当该栈中活动清空时,会显示另一个栈。
1 | <activity |
活动的最佳实践
知晓当前是在哪一个Activity
新建一个BaseActivity类:
- 右键com.trrrrw.demo-新建-Kotlin 类/文件-输入BaseActivity
在BaseActivity中:
1
2
3
4
5
6
7
8open class BaseActivity : AppCompatActivity(){
//BaseActivity继承了AppCompatActivity所以在别的Activity继承后功能不会变
override fun onCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
Log.d("BaseActivity",javaClass.simpleName)
//在别的Activity启动时在Logcat中打印当前Activity名
}
}让其余的Activity继承BaseActivity
随时随地退出程序
(+_+)?
Binding视图绑定
MainActivity.kt :
1 | package com.trrrrw.OF |
build.gradle(:app)(在android下插入) :
1 | buildFeatures{ |
Material3
在存储库
的build.gradle
文件中要有:
1 | allprojects { |
在应用
的 build.gradle
文件中添加所需工件的依赖项:
1 | dependencies { |
修改themes.xml中parent为Theme.Material3.Light
1 | <style name="Theme.demo" parent="Theme.Material3.Light"> |
WebView
1 | val webView=binding.webView |