说明: 本文是郭霖《第一行代码-第3版》的读书笔记
Activity是包含用户界面的组件,主要用于和用户交互,一个应用程序中可以包含一个或多个Activity
3.2 基本用法创建一个Empty Activity,之后自己来添加Activity
手动创建Activity
project模式下,在app/src/main/java/com.example.projectname目录下新建一个Activity。
项目的任何Activity都应该重写onCreate()方法。
创建和加载布局
Android程序设计讲究逻辑和视图分离。最好每一个Activity都能对应一个布局。
如果没有在创建Activity时勾选Generate Layout File,则需要我们在res目录下手动添加一个layout文件。
在app/src/main/res/目录下创建layout文件夹,添加layout resource file,这是一个xml文件。
在onCreate()内通过setContentView()方法来传入布局文件,参数为布局文件的ID,如:
setContentView(R.layout.first_layout)
项目中添加的任何资源文件都会在R文件中生成一个相应资源的对应ID
在AndroidManifest文件中注册
所有的Activity都需要在AndroidManifest.xml中注册才能生效。但实际上Studio已经帮我们注册了FirstActivity。
除此之外,程序要想正常允许还需要配置主Activity。即在标签内添加
此时程序就可以正常运行了。
Toast
Toast是Android提供的一种提醒方式,可以将一些短小的信息通知给用户。自动消失、不占屏幕空间
现在想点击button触发toast,首先需要找到定义在Layout文件中的Button,可以根据ID来找:
findViewById()获取布局文件中控件的实例,但是在Kotlin中,会在app/buid.gradle文件头部引入一个kotlin-android-extensions插件,可以根据布局文件中控件的ID自动生成一个具有相同名称的变量,因此可以不需要调用findViewById()
var button1: Button = findViewById(R.id.button1)
//定义button的按下监听事件
button1.setOnClickListener{
//使用Toast,Toast.makeText()静态方法创建一个Toast对象,再调用show()显示
//Toast.makeText()需传入三个参数:1.Context,2.显示的文本内容, 3.Toast显示的时长(内置有Toast.LENGTH_SHORT和LONG)
//Activity本身就是一个Content对象,所以这里可以直接传入this
Toast.makeText(this, "You clicked button1", Toast.LENGTH_SHORT).show()
}
在Activity中使用Menu
首先在res文件夹下新建一个menu文件夹,新建一个Android resource file,这是一个xml文件,选择menu类型。然后可以在menu文件中添加item。可以定义item的id和title,item相当于是一个菜单项,id是它唯一的标识
如何添加这个menu呢?
需要在主Activity中重写onCreateOptionsMenu()方法。这里可以使用Ctrl+O快捷键得到重写下拉表
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
// menuInflater实际上来自getMenuInflater(),是kotlin中的语法糖
// 调用inflate()方法,给当前Activity创建菜单,第一个参数表示要使用哪一个menu文件,第二个参数指定这一个菜单项添加到哪一个
// Menu对象中,这里直接添加到传入的这个menu中
menuInflater.inflate(R.menu.menu, menu)
return true //true表示允许这个menu显示
}
接下来可以定义菜单响应事件,
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// 判断传入的是哪一个Item
when(item.itemId) {
// 如果item.itemId == R.id.add_item
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
}
销毁一个Activity
只需要按一下Back键就可以销毁当前Activity, 代码实现是调用finish()方法
3.3 使用Intent在Activity之间穿梭如果有多个Activity,如何从主Activity跳到另外一个Activity呢?
再建一个Activity和相应的布局文件,Android Studio已经帮我们在AndroidManifest.xml中注册了这个新建的Activity。
另外由于这个Activity不是主Activity,也不需要配置
至此,这个新的Activity已创建完成,接下来是启动它。
Intent
1.显示Intent
Intent有多个构造函数的重载,其中一个是Intent(Context packageContext, Class> cls),构建出intent后,再用startActivity()方法接收intent参数,来启动Activity
// 这里的SecondActivity::class.java相当于Java中的SecondActivity::class val intent = Intent(this, SecondActivity::class.java) startActivity(intent)
2.隐式Intent
隐式Intent通过指定action和category等信息,交由系统分析启动哪个Activity。
只有和 每个Intent只能指定一个action,但能指定多个category
此时如果我们在intent上添加一个categoty 此时程序就相应不了了,因为我们的SecondActivity并没有这个Category 我感觉是 更多隐式Intent的用法 使用隐式的Intent,不仅可以启动本程序的Activity,也可以启动其他程序的Activity,使多个程序之间的功能共享成为可能。 也可以创建一个新的Activity,指定其Action为android.intent.action.VIEW,以及data来响应这个intent: 除了https协议外,还可以指定其他协议,比如geo为地理位置,tel为拨打电话。 如何向下一个Activity传递数据 思路:Intent中提供了一系列putExtra()方法的重载,因此可以把数据先暂存在Intent中,启动另一个activity后,再将数据取出。 返回数据给上一个Activity 上述的onActivityResult()方法是在activity销毁的时候才调用的,为了点击Back键也能保存要返回的数据,需要重写onBackPressed()方法 返回栈 Android的Activity是可以层叠的,放在了栈中,启动新的Activity的时候入栈,Back的时候出栈,栈顶的Activity显示在当前界面。 Activity状态 Activity的生存期 小结: 可以在onStart()内加载用户可见的资源,在onStop()内销毁用户可见的资源可以在onResume()内加载和用户交互的资源,在onStop()内销毁和用户交互的资源
感觉生命周期还是挺重要的,可以帮助管理好资源 使用对话框Activity Activity被回收了怎么办 当Activity进入Stop状态时是有可能被系统回收的,此时再返回该Activity,临时数据和状态就会被销毁。此时执行的是onCreate()方法。我们可以重写onSaveInstanceState()方法,此方法在Activity在销毁前一定会被调用。 如何取出数据呢?如果内存被回收,则返回Activity时会调用onCreate()方法,此时就可以在onCreate()中加载保存的数据 这里需要注意两点: Intent可以和Bundle一起用来传递数据,将数据存入Bundle里,再将Bundle传入Intent中,传给下一个Activity。当手机的屏幕发生旋转时,Activity也会经历一个重新创建的过程,虽然也可用上述的onSaveInstanceState()方法来保存数据,但是有更优雅的解决方案。
3.5 Activity的启动模式
Activity有四种启动模式。 使用taskId查看当前返回栈的id,本质上是getTaskID()方法的语法糖
启动模式在AndroidManifest.xml中更改 再让所有的Activity继承这个Basic类而非之前的AppCompatActivity 这样每当我们进入一个界面时就会打印当前界面对应的Activity类名。 随时随地退出程序 当我们打开了多个Activity,需要一个一个Back才能退出,这样会显得很麻烦。可以通过写一个单例类来管理这些Activity并提供一个finishAll()方法。 接下来修改之前写的BasicActivity,在onCreate()和onDestroy()方法内添加单例类的add和remove: 现在想随时退出程序,只需要调用ActivityCollector单例类的finnishAll()方法即可。 启动Activity的最佳写法 前面学过,通过构建Intent再调用startActicity()或者startActivityForResult()来启动Activity,数据传递也可以借用Intent和Bundle完成。 问题:当某个Activity不是由你开发的,但启动它需要传参,怎么传参呢? 要么询问作者,要么自己看代码。为了方便别人,可以给每个Activity启动时都编写启动方法如下:// AndroidManifest.xml
val intent = Intent("com.example.activitytest.ACTION_START")
// 添加category
intent.addCategory("com.example.activitytest.MY_CATEGORY")
startActivity(intent)
// 指定Intent的Action,这是Android系统内置的Action
val intent = Intent(Intent.ACTION_VIEW)
// 通过Uri.Parse()方法将网址字符串解析为Uri对象,再调用Intent的setData()方法将Uri对象传进去
intent.data = Uri.parse("https://www.baidu.com/") //指定协议是https
startActivity(intent) //start的是哪个activity?
// 调用系统拨打电话界面
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:1008") //指定协议是tel
startActivity(intent) //start的是哪个activity?
//传递数据
val data = "Hello, SecondActivity!"
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("extra_data", data)
//------------------------------------------
//另一个Activity中取出数据
val extraData = intent.getStringExtra("extra_data") // intent其实是getIntent(),即启动第二个Activity的Intent
Log.d("SecondActivity","extra data is $extraData")
// 首先需要在启动Activity的时候选择这个方法,这样在此Activity销毁的时候才能够回调onActivityResult方法
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, 1) // 第二个参数是请求码,是唯一值就可以
//------------------------------------------
// 点击button2让其返回数据
val intent = Intent() //创建一个空的Intent保存数据
intent.putExtra("data_return", "Hello FirstActivity") //将要返回的数据保存到intent中
setResult(Activity.RESULT_OK, intent) //这个方法专门用于向上一个Activity返回数据,第二个参数就是把intent给传递回去
finish() //销毁当前Activity, 从而回调onActivityResult()方法
//------------------------------------------
// 第一个Activity中重写onActivityResult()方法
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// 首先根据请求码判断数据来源,再根据resultCode判断结果是否处理正确
when (requestCode) {
1 -> if (resultCode == Activity.RESULT_OK) {
val returnData = data?.getStringExtra("data_return") //这里不能用intent来获取数据
Log.d("FirstActivity", "returned data is $returnData")
}
}
}
override fun onBackPressed() {
val intent = Intent()
intent.putExtra("data_return", "Hello FirstActivity")
setResult(Activity.RESULT_OK, intent)
//finish()
super.onBackPressed() //如果自己不调用这个父类的处理方法,你就要自己写 finish()
}
3.4 Activity的生命周期
运行状态,位于返回栈的栈顶时处于该状态暂停状态,不处于栈顶,但仍然可见时,如对话框弹出的背景Activity。此时Activity是仍然存活的,系统一般不会回收。停止状态,当Activity不处于栈顶,完全不可见的时,有可能被系统回收销毁状态,当Activity从返回栈中pop后就处于该状态,系统最倾向于回收这种状态的Activity
当Activity对用户可见时,是在onStart()和onStop()内当Activity准备好和用户交互时(当然是可见的),是在onResume()和onPause()内onCreate()和onDestroy()经历的是完整的生命周期,前者进行各种初始化操作,后者释放资源
// 接受Bundle类型的参数,提供了一系列的方法用来保存数据
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val tempData = "Something you just typed."
val intData = 1024
outState.putString("data_key", tempData)
outState.putInt("data2_key", intData)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(tag, "onCreate() called")
// 加载保存的数据
if (savedInstanceState != null) {
val tempData = savedInstanceState.getString("data_key")
val intData = savedInstanceState.getInt("data2_key")
Log.d(tag, "tempData is $tempData and $intData")
}
}
standard,是默认的启动模式。系统不在乎返回栈中是否已有Activity,每次启动都会创建一个该Activity的新实例。singleTop,该模式下,若返回栈的栈顶已经是该Activity,则直接使用,不会创建新的实例。singleTask, 该模式下,整个返回栈中只会存在该Activity的一个实例,启动该Activity时,系统会先检查返回栈中是否存在该Activity的实例,若存在,则直接使用该实例,并将在其之上的其他Activity统统出栈。singleInstance,该模式下,会有一个单独的返回栈管理这个Activity,便于不同应用程序共享Activity。前面三种模式,都是一个应用程序有一个返回栈。按Back返回时,会先返回该返回栈中的Activity,直到本返回栈为空,才返回下一个返回栈中的Activity。当全部返回栈为空时,就退出了应用程序。
object ActivityCollector {
private val activities = ArrayList
open class BasicActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("baseActivity", javaClass.simpleName)
ActivityCollector.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivities(this)
}
}
button3.setOnClickListener {
//点击button3直接全部退出
ActivityCollector.finishAll()
// 为保证程序安全退出,可以杀掉当前进程,killProcess()只能用于杀掉当前程序的进程。
android.os.Process.killProcess(android.os.Process.myPid())
}
// 在你写的这个Activity类中加入
// companion object 使得其内的方法变为静态方法
companion object {
fun actionStart(context: Context, data1: String, data2: String) {
val intent = Intent(context, SecondActivity::class.java)
intent.putExtra("param1", data1)
intent.putExtra("param2", data2)
context.startActivity(intent)
}
}
// 那在启动这个Activity的时候可以简化成如下形式,则传参一目了然。
SecondActivity.actionStart(this, "data1", "data2")



