活动(Activity
)是一种可以包含用户界面的组件,主要用于和用户进行交互,一个应用程序中可以包含零个或多个活动。
Android 程序的设计讲究逻辑和视图分离,最好每一个活动能对应一个布局。一个Activity
的用法如下:
- 新建一个
Activity
Activity
调用setContentView()
方法来给当前活动加载一个布局,该方法需要一个布局文件id
- 新建一个
Activity
后,AS 会默认自动创建和加载对应的布局资源文件。而项目中添加的任何资源都会在R
文件中生成一个相应的资源id
,然后将该值传入setContentView()
方法即可。(一般的 AS 都会代劳) - 最后所有的活动都要在
AndroidManifest.xml
中进行注册才能生效,活动的注册声明需要放在<application>
标签中,并通过<activity>
标签对活动进行注册,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="top.seiei.aboutactivity">
<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=".MainActivity"
android:label="this is my first activity">
<!-- android:name 指定具体注册哪个活动 -->
<!--
而 .MainActivity 是 top.seiei.aboutactivity.MainActivity 的缩写,
在最外层的 mainfest 标签已经声明了 package 包名属性为 `top.seiei.aboutactivity`
-->
<!-- 设置主程序入口 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
1 销毁一个活动
手机上按 Back
键,即可销毁当前的活动,而在代码中,Activity
类提供了一个 finish()
方法用于销毁活动。
2 活动之间的跳转
活动之间如何跳转?此时就需要使用到 Intent
(意图)。
Intent
是 Android 程序中各个组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent
一般可被用于启动活动、启动服务及发送广播等场景。
Intent
可以分为两种,显式 Intent
和 隐式 Intent
。
2.1 显式 Intent
Intent
的构造函数由很多,其中一个 Intent(Context packageContext, Class<?> cls)
接收两个参数,分别是:
Context
,启动活动的上下文- 指定想要启动的目标
Activity
代码如下:
Button button1 = (Button) findViewById(R.id.goSecondActivity);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
// 显式 Intent
// 创建 Intent,第一个参数为启动活动的上下文,第二个则是指定想要启动的目标活动
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
使用 startActivity()
方法用于启动这个 Intent
,使用这种方式启动活动,Intent
的 “意图” 十分明显,因此称为 显式 Intent
。
2.2 隐式 Intent
相比于显式 Intent
直接地传入指明想要启动的目标活动,隐式 Intent
则只是指定一系列更为抽象的 action
和 category
等信息,然后交由系统去分析这个 Intent
,并找出合适的活动去启动。
action
标识用来说明这个Activity
可以执行哪些动作category
标识用来说明这个Activity
在哪个环境中才能被激活,不属于这个环境的,不能被激活。- 每个
Intent
只能指定一个action
,但能指定多个category
在 AndroidManifest.xml
文件中通过 <activity>
标签配置 <intent-filter>
可以设置到这些内容,代码如下:
<activity android:name=".SecondActivity">
<intent-filter>
<!-- 隐式 Intent -->
<!-- category 标识用来说明这个activity在哪个环境中才能被激活,不属于这个环境的,不能被激活。 -->
<!-- category android:name="android.intent.category.DEFAULT" 表示默认的category,不能删除 -->
<category android:name="android.intent.category.DEFAULT"/>
<!-- action 标识用来说明这个activity可以执行哪些动作 -->
<action android:name="thisIsAction"/>
<category android:name="thisIsCategory"/>
</intent-filter>
</activity>
在代码中使用 Intent
的另一个构造函数,直接将 action
的字符串传进去,同时通过生产的 Intent
调用 addCategory()
传入 category
的字符串来筛选 category
属性,代码如下:
Intent intent = new Intent("thisIsAction");
intent.addCategory("thisIsCategory");
startActivity(intent);
2.3 更多隐式 Intent
的用法
通过使用隐式 Intent
,不仅可以启动自己程序中的活动,还可以启动其它程序的活动,这可以达到 Android 多个程序之间的功能共享,所以,action
和 category
的命名应该使用包名的形式,做到规范。
说以下代码:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
这是一个启动手机浏览器打开百度,其中:
Intent.ACTION_VIEW
这是一个 Android 系统内置的action
动作,通过Uri.parse()
方法将一个网址字符串解析为一个Uri
对象,在调用setData()
传入Uri
对象。
setData()
方法主要指定当前 Intent
正在操作的数据,与之对应 的是 <intent-filter>
标签中配置的 <data>
标签,它用于规定指明当前设置的活动能够响应什么类型的数据,它主要可以配置一下内容:
android:sheme
:指定数据的协议部分,如http
android:host
:指定数据的主机名,如www.baidu.com
android:port
:指定数据的端口android:mimeTye
:指定可以处理的数据类型
又如调用电话代码如下:
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
3 活动之间传递数据
3.1 向下一个活动传递数据
Intent
中提供一系列 putExtra()
方法的重载,可以将传递的数据存在 Intent
中,在启动活动后,只需将数据从 Intent
中获取出来就可以。
传递数据:
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("saveStr", "ThisIsStr");
startActivity(intent);
}
});
获取数据:
// 获取传递信息
Intent intent = getIntent();
String saveStr = intent.getStringExtra("saveStr");
Toast.makeText(SecondActivity.this, "这是传递过来的数据:" + saveStr, Toast.LENGTH_SHORT).show();
3.2 返回数据到上一个活动
返回数据到上一个活动就需要使用到 Activity
中的另一个启动活动的方法 startActivityForResult()
。它也是用于启动活动,但同时它期待启动活动销毁时能返回一个结果。它接受一个 int
类型请求码,这个只要保证唯一性可以随意设置,作用是在触发活动销毁回调时辨识具体是哪个活动返回的数据。
所以总的具体步骤是:
1、父活动调用 startActivityForResult()
方法启动子活动:
Button button1 = (Button) findViewById(R.id.goSecondActivity);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
// 显式 Intent
// 构建函数参数为:
Intent intent = new Intent("thisIsAction");
// 返回结果給上层活动
// 接受两个参数,一个是 intent,另一个是请求码,用于之后回调中判断数据的来源
startActivityForResult(intent, 1);
}
});
2、子活动在销毁时,创建 Intent
将要传递的数据设置在里面,调用 setResult()
方法设置返回结果状态及返回内容,其中状态码有:
RESULT_OK
:成功RESULT_CANCELD
:失败
// 退出当前活动
Button button = (Button) findViewById(R.id.mySecondButton);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建新的Intent,传递数据回上一个活动
Intent intent = new Intent();
intent.putExtra("returnStr", "thisIsReturnStr");
// 专门返回数据给上活动
// 第一个参数表示处理结果,有 RESULT_OK 和 RESULT.CANCELED
// 第二个参数为 带有数据的 Intent
setResult(RESULT_OK, intent);
finish();
}
});
- 当活动被销毁之后会回调上一个活动的
onActivityResult()
方法,它接受三个参数,分别是:requestCode
:启动活动时设置请求码,用于识别消息来源,究竟是哪个子活动被销毁resultCode
:活动销毁时设置的处理结果状态码intent
:活动销毁时设置的Intent
对象
/**
* 子活动被销毁后的回调方法
* @param requestCode 启动子活动时传入的请求码
* @param resultCode 返回数据时传入的处理结果
* @param data 携带返回信息的 Intent
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 请求码等于设置子活动时设置的
if (requestCode == 1) {
// 子活动返回数据的处理结果是成功
if (resultCode == RESULT_OK) {
String result = data.getStringExtra("returnStr");
Toast.makeText(MainActivity.this, "这是子活动返回的数据:" + result, Toast.LENGTH_SHORT).show();
}
}
}
如果用户是点击
Back
键回到前一个活动,此时可重写onBackPressed()
方法解决问题,该方法当用户按下Back
键是会执行
4 活动的说明周期
Android 中的活动是可以重叠的,每启动一个活动就会覆盖在原活动之上,点击 Back
键或执行 finish()
方法会销毁最上面的活动,下面的活动就会重新显示出来。
Android 是使用任务 Task
来管理活动的,一个任务就是一组放在栈里的活动集合,这个栈就叫做 返回栈。
4.1 活动状态
每个活动在生命周期中最多可能有四个状态:
- 运行状态
当一个活动位于返回栈的栈顶时,这时活动就位于运行状态。 - 暂停状态
当一个活动不再位于返回栈的栈顶时,但仍然可见,这时活动就进入了暂停状态。(不是每个活动就占满整个屏幕,比如说对话框),系统有可能回收这里的内容 - 停止状态
当一个活动不再位于返回栈的栈顶,并处于完全不可见的时候,就进入了停止状态(系统有可能回收这里的内容) - 销毁状态
当一个活动从返回栈中消除后就会变成销毁状态。系统最倾向回收这种状态的活动,从而保证手机的内存充足
4.2 活动的生存期
上图是活动的生命周期,Activity
类定义了 7 个回调方法,覆盖了活动生命周期的每个环节:
onCreate()
:在 活动第一次创建 的时候调用,应该在这个方法中完成活动的初始化操作,如加载布局、绑定事件等onStart()
:活动由 不可见变为可见 的时候调用onResume()
:在活动准备好和用户进行交互的时候调用,此时活动一定处于 运行状态(与onStart()
的区别看图)onPause()
:在系统准备去启动或恢复 另一个活动 时调用(此时活动处于暂停状态)。通常会在这个方法中释放一些消耗 CPU 的资源,以及保存一些关键数据onStop()
:在活动完全不可见的时候调用(此时活动处于停止状态),一般会完成释放资源的操作onDestroy()
:在活动被销毁之前调动,一般会完成释放内存的操作onRestart()
:在活动由 停止状态变为运行状态 之前调用
4.3 活动被回收
当一个活动进入停止状态,它就有可能被系统回收,比如应用中活动A启动了活动B,A处于停止状态,此时内存不足,系统把A回收了,而用户此时从B返回A,结果还是会正常地显示A,只不过此时活动A并不会执行 onRestart()
方法,而是执行 onCreate()
方法,即此时活动A重新创建了一次。
那么导致的问题可能是活动A中的临时数据和状态都会被回收掉。
解决方法就是 Activity
中的 onSaveInstanceState()
回调方法,这个方法可以保证在活动被回收之前一定被调用,它接收一个 Bundle
类型的参数,它用于存储数据,有诸如:putString()
、putInt()
等方法保存数据,接收两个值,第一个参数是键,第二个参数是值。
数据保存下来之后,在对应活动的 onCreate()
方法中的 Bundle
参数就会带有之前所保存的全部数据。
Intent
还可结合Bundle
一起用于传递数据,将数据保存在Bundle
对象中,并将Bundle
对象存在Intent
里。
4.4 活动的启动模式
活动的启动模式有四个,分别是:standard
、singleTop
、singleTask
和 singleInstance
,这些模式可以在 AndroidMainFEST.xml
文件中通过 <activity>
标签 指定 android:launchMode
属性 来选择启动模式。
standard
standard
是活动默认的启动模式,每当启动一个新的活动,新的活动就会自动添加到返回栈的栈顶位置,系统不会在乎该活动是否已经存在,每次启动都会创建该互动的一个新的实例singleTop
设置了singleTop
模式,系统在启动活动时如果发现返回栈的栈顶是该活动,则会继续使用它singleTask
设置了singleTask
模式,系统在启动活动时如果发现返回栈内有该活动,则会直接使用该实例,并把在活动之上的所有活动统统出栈,如果没发现就会创建一个新的活动实例singleInstance
设置了singleInstance
模式,系统在启动活动时会创建一个新的返回栈来管理该活动,这通常用于允许其他程序的调用
Toast
Toast
是 Android 系统提供的一种提醒方式。下面为例子代码:
Button button1 = (Button) findViewById(R.id.goSecondActivity);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "add", Toast.LENGTH_SHORT).show();
}
});
Toast 的用法是:通过静态方法 makeText()
创建一个 Toast
对象,该方法接收三个参数:
Context
,上下文- 显示文本
- 显示时长,有两个内置常量
Toast.LENGTH_SHORT
和Toast.LENGTH_LONG
该静态方法返回一个 Toast
对象直接调用 show()
就可以将 Toast
显示出来。
在活动中,可以通过
findViewById()
方法获取在布局文件中定义的元素,它返回的是View
对象,所以通常需要强制转型。
Menu
Menu
的用法:首先在 res
目录新建 menu
文件夹,然后右键新增一个 Menu resource file
文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 其中 android:title 表示菜单项的名称 -->
<item
android:id="@+id/addItem"
android:title="Add"></item>
<item
android:id="@+id/removeItem"
android:title="Remove"></item>
</menu>
然后在 Activity
文件中 重写 onCreateOptionsMenu()
方法(AS 重写方法快捷键 Ctrl
+ O
),代码如下:
/**
* 添加菜单
* @param menu
* @return
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// getMenuInflater 方法获取 MenuInflater 对象
// inflate 方法给当前活动创建菜单
// 第一个参数是资源文件,第二个指定菜单项添加到哪个 Menu 对象中,这里使用重写方法中传入的 menu 参数
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
定义菜单响应时间,需要在 Activity
重写 onOptionsItemSelected()
方法:
/**
* 菜单项的点击事件
* @param item
* @return
*/
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
// getItemId() 获取点击激活的 item 的 id
switch (item.getItemId()) {
case R.id.addItem:
Toast.makeText(MainActivity.this, "add", Toast.LENGTH_SHORT).show();
break;
case R.id.removeItem:
Toast.makeText(MainActivity.this, "remove", Toast.LENGTH_SHORT).show();
break;
}
return true;
}