Activity是⼀种可以包含用户界面的组件,主要用于和用户进行交互。
Activity的基本用法
创建和加载布局
<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 1"/>
</LinearLayout>
属性介绍
- id:给当前元素定义的
- layout_width 和 layout_height:
match_parent
:全屏铺满
wrap_content
:自动根据内容调节大小
20dp
:数值设置 单位为dp - text:显示文字内容
- textSize:设置文字大小 单位为sp
给Activity加载布局
setContentView(R.layout.first_layout)
在AndroidManifest文件中注册
android:label=""
用于指定Activity中标题栏的内容
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
用于声明该Activity为主Activity
运行程序
(部分截图)
在Activity中使用Toast
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
val button1: Button = findViewById(R.id.button1)
button1.setOnClickListener{
Toast.makeText(this,"You click Button1.",Toast.LENGTH_SHORT).show()
}
}
补充kotlin特性:
使用Kotlin编写的Android项目在app/build.gradle
文件的头部默认引入了⼀个kotlin-android-extensions
插件,这个插件会根据布局文件中定义的控件id自动生成⼀个具有相同名称的变量,就可以在Activity里直接使用这个变量,而不用再调用findViewById()
方法了
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
button1.setOnClickListener {
Toast.makeText(this,"You click Button1.",Toast.LENGTH_SHORT).show()
}
}
运行程序
在Activity中使用Menu
文件路径
main.xml
<?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>
这里创建了两个菜单项,其中<item>
标签用来创建具体的某⼀个菜单项,然后通过android:id
给这个菜单项指定⼀个唯⼀的标识符,通过android:title
给这个菜单项指定⼀个名称。
代码效果
重写onCreateOptionsMenu()方法
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main,menu)
return true
}
inflate()
中的两个参数:
第⼀个参数用于指定通过哪⼀个资源文件来创建菜单,这里当然是传入R.menu.main
第⼆个参数用于指定菜单项将添加到哪⼀个Menu对象当中,这里直接使用onCreateOptionsMenu()
方法中传入的menu
参数。- 最后给这个方法返回
true
,表示允许创建的菜单显示出来。
重写onOptionsItemSelected()方法
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.add_item -> Toast.makeText(this,"You click Add",Toast.LENGTH_SHORT).show()
R.id.remove_item -> Toast.makeText(this,"You click Remove",Toast.LENGTH_SHORT).show()
}
return true
}
运行代码
销毁Activity
finish()
使用Intent
Intent
是Android程序中各组件之间进行交互的⼀种重要⽅方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent
⼀般可⽤于启动Activity
、启动Service
以及发送广播等场景。
显式Intent
intent
常用的构造函数接收两个参数:
第一个参数Context
要求提供⼀个启动Activity
的上下文
第二个参数Class
用于指定想要启动的目标Activity
通过这个构造函数就可以构建出Intent
的“意图”。
因为它的“意图”十分明显,所以称为显式Intent。
代码实现
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
button1.setOnClickListener {
val intent = Intent(this,SecondActivity::class.java)
startActivity(intent)
}
}
隐式Intent
隐式Intent并不明确指出想要启动哪⼀个Activity
,而是指定了⼀系列更为抽象的action
和category
等信息,然后交由系统去分析这个Intent
,并找出合适的Activity
去启动。
通过在<activity>
标签下配置<intent-filter>
的内容,可以指定当前Activity
能够响应的action
和category
。
代码实现
AndroidManifest文件
FirstActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
button1.setOnClickListener {
val intent = Intent("com.example.activitytest.ACTION_START")
startActivity(intent)
}
}
android.intent.category.DEFAULT
是⼀种默认的category
,在调用startActivity()
方法的时候会自动将这个category
添加到Intent
中。
补充
每个Intent
中只能指定⼀个action
,但能指定多个category
。目前我们的Intent
中只有⼀个默认的categor
,可以通过addCategory()
方法添加。
intent.addCategory("com.example.activitytest.MY_CATEGORY")
其他用法1
button1.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)
}
⾸先指定了Intent
的action
是Intent.ACTION_VIEW
,这是⼀个Android系统内置的动作,其常量值为android.intent.action.VIEW
。然后通过Uri.parse()
方法将⼀个网址字符串解析成⼀个Uri
对象,再调用Intent
的setData()
方法将这个Uri
对象传递进去。
这样在点击按钮时就可以看到在系统浏览器上打开了百度的网站
setData()
方法接收⼀个Uri
对象,用于指定当前Intent
正在操作的数据,这些数据通常是以字符串形式传入Uri.parse()
方法中解析产生的。
补充
与此对应,还可以在<intent-filter>
标签中再配置⼀个<data>
标签,用于更精确地指定当前Activity
能够响应的数据。
<data>
标签中主要可以配置以下内容:
标签 | 用途 |
---|---|
android:scheme | 用于指定数据的协议部分,如上例中的https 部分 |
android:host | 用于指定数据的主机名部分,如上例中的www.baidu.com 部分 |
android:port | 用于指定数据的端口部分,⼀般紧随在主机名之后 |
android:path | 用于指定主机名和端口之后的部分,如⼀段网址中跟在域名之后的内容 |
android:mimeType | 用于指定可以处理的数据类型,允许使用通配符的方式进行指定 |
只有当<data>
标签中指定的内容和Intent
中携带的Data
完全⼀致时,当前Activity
才能够响应该Intent
。
实践
其他用法2
button1.setOnClickListener {
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
}
点击Button1
后,自动跳转到电话并输入号码
向下一个Activity传递数据
Intent
中提供了⼀系列putExtra()
方法的重载,可以把我们想要传递的数据暂存在Intent
中, 在启动另⼀个Activity
后,只需要把这些数据从Intent
中取出就可以了。
putExtra()
方法接收两个参数,第⼀个参数是键,用于之后从Intent
中取值,第⼆个参数才是真正要传递的数据。
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
button1.setOnClickListener {
val data = "Hello SecondActivity"
val intent = Intent(this,SecondActivity::class.java)
intent.putExtra("extra_data",data)
startActivity(intent)
}
}
}
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val extraData = intent.getStringExtra("extra_data")
Log.d("SecondActivity","extra data is $extraData")
}
}
向上一个Activity返回数据
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
button1.setOnClickListener {
val intent = Intent(this,SecondActivity::class.java)
startActivityForResult(intent,1)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
1 -> if (resultCode == RESULT_OK){
val returnData = data?.getStringExtra("data_return")
Log.d("FirstActivity","returned data is $returnData")
}
}
}
}
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
button2.setOnClickListener {
val intent = Intent()
intent.putExtra("data_return","Hello FirstActivity")
setResult(RESULT_OK,intent)
finish()
}
}
}
Activity的生命周期
Android是通过任务来管理
Activity
,一个任务就是一组存放在栈里的Activity
的集合,这个栈被称为返回栈(back stack)
Activity的4种状态
- 运行态 Running:位于当前返回栈的顶部
- 暂停态 Paused:不在栈顶但用户可见
- 停止态 Stopped:不在栈顶且完全不可见
- 销毁态 Destroyed:从返回栈中移除后销毁
Activity的7个回调方法
onCreate()
:activity第一次启动时被调用,在该方法中初始化activity所能使用的全局资源和状态,如:绑定事件,创建线程等。onStart()
:当activity对用户可见时调用,即activity展现在前端,该方法一般用来初始化或启动与更新界面相关的资源onResume()
:当用户与activity进行交互时被调用,此时activity位于返回栈的栈顶,并处于运行状态,该方法完成一些轻量级的工作,避免用户等待onPause()
:启动或恢复另一个activity的时候被调用,该方法一般用来保存界面的持久信息,提交未保存的数据,并释放消耗CPU的资源。onStop()
:该方法在activity不可见状态时调用,如:其他activity启动或恢复并将其覆盖时调用。onDestroy()
:在activity销毁之前被调用。onRestart()
:当activity重新启动时调用。
Activity的3种生存期
- 完整生成期:Activity 在
onCreate()
方法和onDestroy()
方法之间所经历的,就是完整生存期。一般情况下,一个 Activity 会在onCreate()
方法中完成各种初始化操作,而在onDestroy()
方法中完成释放内存的操作。 - 可见生存期: Activity 在
onStart()
方法和onStop()
方法之间所经历的,就是可见生存期。在可见生存期内,Activity 对于用户总是可见的,即便有可能无法和用户进行交互。可以通过这两个方法,合理地管理那些对用户可见的资源。比如在onStart()
方法中对资源进行加载,而在onStop()
方法中对资源进行释放,从而保证处于停止状态的 Activity 不会占用过多内存。 - 前台生存期:Activity 在
onResume()
方法和onPause()
方法之间所经历的,就是前台生存期。在前台生存期内,Activity 总是处于运行状态的,此时的 Activity 是可以和用户进行交互的,我们平时看到和接触最多的也就是这个状态下的 Activity。
图解传送门
onSaveInstanceState()方法
onSaveInstanceState()
方法会携带⼀个Bundle
类型的参数,Bundle
提供了⼀系列的方法用于保存数据。
比如可以使用putString()
方法保存字符串,使用putInt()
方法保存整型数据,以此类推。
每个保存方法需要传入两个参数,第⼀个参数是键,用于后面从Bundle
中取值,第⼆个参数是真正要保存的内容。
该方法在Activity
被回收之前一定会调用。
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
if (savedInstanceState != null) {
val tempData = savedInstanceState.getString("data_key")
Log.d("FirstActivity", "tempData is $tempData")
}
}
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState)
val tempData = "Something you just typed"
outState.putString("data_key", tempData)
}
}
Activity的启动模式
standard
standard
是Activity
默认的启动模式。
在standard
模式下,每当启动⼀个新的Activity
,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard
模式的Activity
,系统不会在乎这个Activity
是否已经在返回栈中存在,每次启动都会创建⼀个Activity
的新实例。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("FirstActivity",this.toString())
setContentView(R.layout.first_layout)
button1.setOnClickListener {
val intent = Intent(this,FirstActivity::class.java)
startActivity(intent)
}
}
singleTop
当Activity
的启动模式指定为singleTop
,在启动Activity
时如果发现返回栈的栈顶已经是该Activity
,则认为可以直接使用它,不会再创建新的Activity
实例。
如果发现该Activity
并未处于栈顶位置时,则创建一个新的Activity
实例。
singleTask
当Activity
的启动模式指定为singleTask
,每次启动该Activity
时,系统首先会在返回栈中检查是否存在该Activity
的实例,如果发现已经存在则直接使用该实例,并把在这个Activity
之上的所有其他Activity
统统出栈,如果没有发现就会创建⼀个新的Activity
实例。
singleInstance
指定为singleInstance
模式的Activity
会启用⼀个新的返回栈来管理这个Activity
。
其实如果 singleTask
模式指定了不同的 taskAffinity
,也会启动⼀个新的返回栈。
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)
}
}
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)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("ThirdActivity","Task id is $taskId")
setContentView(R.layout.activity_third)
}
Activity的最佳实践
知道当前在哪一个Activity
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity", javaClass.simpleName)
}
}
让其他的活动都继承BaseActivity
。
然后运行程序,依次进入其他活动界面
随时随地退出程序
新建一个单例类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(){
for (activity in activities){
if (!activity.isFinishing){
activity.finish()
}
}
activities.clear()
}
}
在BaseActivity
中添加:
在ThirdActivity
中:
修改后,点击Button3
则退出程序
启动Activity的最佳写法
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)
}
}
actionStart()
方法中完成了Intent
的构建,另外所有SecondActivity
中需要的数据都是通过actionStart()
方法的参数传递过来的,然后把它们存储到Intent
中,最后调用startActivity()
方法启动SecondActivity
。
这样写最直接的好处就是⼀目了然,SecondActivity
所需要的数据在方法参数中全部体现出来了
Kotlin课堂
未完待续