本节内容对应《第一行代码 第三版》:第三章
一、在AndroidManifest中注册
Manifest简介
如果我们创建了一个空的项目(no activity),自己新建了一个FirstActivity,此时需要在Manifest中手动注册。
幸运的是,AS已经为我们写好了以下代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lrgy.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>
其中,com.lrgy.activitytest表示应用整体的包名,android:name=".FirstActivity"表示这个application具体注册了com.lrgy.activitytest.FirstActivity这个Activity。
我们需要指定程序运行时首先启动哪个Activity,即在<activity>
标签中添加<intent-filter>
标签
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
这两个标签指明了应用启动时首先启动该Activity
<intent-filter>的用处
另外,<intent-filter>
这个标签还有额外的作用,即在里面添加<action>
、<category>
、<data>
标签
· intent介绍见:Intent 和 Intent 过滤器
- 指定该Activity可以响应com.lrgy.activitytest.ACTION_START这个动作,这个动作名可以自己写,只需要和隐式Intent中
val intent = Intent("com.lrgy.activitytest.ACTION_START")
传入的参数相同即可
<action android:name="com.lrgy.activitytest.ACTION_START" />
- 指定详细信息,android.intent.category.DEFAULT表示不设置category(即默认category)时响应
<category android:name="android.intent.category.DEFAULT" />
- data,如下操作可以响应链接,不过需要在
<intent-filter>
标签中添加tools:ignore="AppLinkUrlError"
<activity android:name=".ThirdActivity">
<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>
二、Activity导航
1.显式导航
//构建Intent对象,参数:上下文Context,目的地Activity
val intent = Intent(this, SecondActivity::class.java)
//执行intent
startActivity(intent)
2.隐式Intent
val intent = Intent("com.lrgy.activitytest.ACTION_START")//传入要响应界面的action值
intent.addCategory()//传入要响应界面的category值,也可以不设置,即默认
startActivity(intent)
- 实现打开浏览器访问百度
val intent = Intent(Intent.ACTION_VIEW) //内置动作,跳转至其他应用
intent.data = Uri.parse("https://baidu.com") //接受Uri对象,设置目的uri
startActivity(intent)
3.传递数据
- 向下一个Activity传递
发送:
intent.putExtra("extra_data", "携带的信息")
接收:
intent.getStringExtra("extra_data")
- 返回给上一个Activity
接受:
startActivityForResult(intent,10086)//带有返回结果的导航,10086是请求码,在这里设置
//重写onActivityResult方法,requestCode是请求码,resultCode是处理结果如RESULT_OK,data是回传的Intent
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
10086 -> Log.d("mydebug", "收到下层反馈")
}
}
发送:
//按下返回键即调用该方法
override fun onBackPressed() {
super.onBackPressed()
val intent = Intent()
intent.putExtra("data_return","返回给上层的数据")
setResult(RESULT_OK,intent)//第一个参数是向上返回结果,用RESULT_OK就行
finish()
}
三、菜单
- 创建menu资源,使用
<item>
指定菜单项
<item
android:id="@+id/add_item"
android:title="Add"/>
- 重写创建菜单方法
//创建菜单,即在Activity中重写该方法
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
//传入menu资源和要添加至的Menu对象,后者一般用onCreateOptionsMenu中自带参数menu
menuInflater.inflate(R.menu.main, menu)
//return true才能显示菜单
return true
}
- 重写菜单选择方法
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
//使用toast弹出框。参数:上下文上下文Context、显示内容、常量LENGTH_SHORT或Toast.LENGTH_LONG
R.id.add_item -> Toast.makeText(this,"add item",Toast.LENGTH_SHORT).show()
R.id.remove_item -> Toast.makeText(this,"remove item",Toast.LENGTH_LONG).show()
}
/*关于返回值
有人说:
* true表示该方法执行完毕后,点击事件不会再向下一个事件处理方法传递了。
* false表示执行完该方法后,点击事件继续向下传递。
* */
return false
}
四、Activity的生命周期
五、 在Activity被回收时保存数据
fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle)
这个函数将会在Activity被回收前调用,我们可以把数据存放在outState: Bundle
中。
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
val tempData = "something you want to save"
outState.putString("data_key",tempData)
}
然后在onCreate
中使用savedInstanceState: Bundle?
取出数据
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
savedInstanceState?.let {
Log.d(tag,it.getString("data_key"))
}
}
PS:关于手机屏幕旋转导致Activity重建的问题,我们有更优雅的解决方法,在13.2节
六、Activity的启动模式
通过在AndroidManifest.xml
中的<activity>
标签可以指定启动方式
<activity
android:name=".FirstActivity"
android:launchMode="standard">
</activity>
- standard
即默认启动模式,不断创建新的FirstActivity实例 - singleTop
若FirstActivity在返回栈的顶端,则不创建新的实例 - singleTask
保持应用只有一个FirstActivity实例 - singleInstance
创建新的返回栈,放入新的栈中
七、kotlin标准函数with/run/apply
- 原生:
val list = listOf<String>("apple", "banana", "pear", "orange")
val builder = StringBuilder()
builder.append("start eating fruits...\n")
for (fruit in list){
builder.append(fruit).append("\n")
}
builder.append("ate all fruits.")
val result = builder.toString()
println(result)
- with
val result2 = with(StringBuilder()){
append("start eating fruits...\n")
for (fruit in list){
append(fruit).append("\n")
}
append("ate all fruits.")
toString()
}
println(result2)
- run
val result3 = StringBuilder().run{
append("start eating fruits...\n")
for (fruit in list){
append(fruit).append("\n")
}
append("ate all fruits.")
toString()
}
println(result3)
- apply
val result4 = StringBuilder().apply{
append("start eating fruits...\n")
for (fruit in list){
append(fruit).append("\n")
}
append("ate all fruits.")
}
println(result4.toString())
八、Kotlin中的静态方法
- 使用单例类(伪静态方法,会创建单例类)
object Util {
fun doAction(){
println("do action")
}
}
- 使用companion object(伪静态方法,会创建单例类)
class Util {
companion object{
fun doAction(){
println("do action")
}
}
}
- 使用companion object加注解
JvmStatic
(真静态方法)
class Util {
companion object{
@JvmStatic
fun doAction(){
println("do action")
}
}
}
- 使用顶层方法,即没有类的方法
package com.lrgy.activitytest
fun doAction(){
println("do action")
}
在Kotlin中直接用方法名调用,JAVA中用文件名Kt.方法名
调用
九、Activity的最佳启动方法
1、使用一个静态方法:
companion object{
fun actionStart(context: Context, title: String, content: String){
val intent = Intent(context, NewsContentActivity::class.java).apply {
putExtra("news_title", title)
putExtra("news_content", content)
}
context.startActivity(intent)
}
}
这样可以在启动其他Activity时只使用一行代码进行启动,一目了然。
请给你的每个Activity都添加这样的启动方法
2、随时随地退出程序
- 建立ActivityCollector方便管理所有的Activity
//单例类,伪静态方法
object ActivityCollector {
private val activities = ArrayList<Activity>()
fun addActivity(activity: Activity){
activities.add(activity)
}
fun removeActivity(activity: Activity){
activities.remove(activity)
}
fun finishAll(){
//finish所有activity
for (activity in activities){
if (!activity.isFinishing){
activity.finish()
}
}
activities.clear()//将集合中的所有activity元素清空
}
}
- 建立BaseActivity类,使得所有activity都可以被ActivityCollector控制
/*该项目中所有Activity的父类*/
//添加open才可以被继承
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity", javaClass.simpleName)//javaClass相当于java中的getClass方法,获取当前类的对象
ActivityCollector.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivity(this)
}
}