【Android】初识 Activity
文章目录
- 初识 Activity
- 1. Activity 是什么?
- 2. 如何创建 Activity?
- 2.1 新建一个类继承 Activity 类或其子类
- 2.2 在 AndroidManifeat 文件中注册
- 2.3 创建和加载布局
- 3. Activity 间跳转与传递数据
- 3.1 使用显示 Intent
- 3.2 使用隐式 Intent
- 3.3 向下一个活动传递数据
- 3.4 返回数据给上一个活动
- 3.5 小结
初识 Activity
1. Activity 是什么?
Android 的 Activity 是构建用户界面的基本单元,它代表了一个独立的交互屏幕。可以理解为:Activity = 应用中的一个“窗口”。
它解决了移动应用用户入口点不确定的问题,因为任何 Activity 都可以作为应用的入口。应用由多个松散耦合的 Activity 组成,共同协作完成用户任务。一个 Activity 可以启动自己应用内的其他 Activity,甚至可以启动其他应用的 Activity。
要使用 Activity,开发者必须:
- 在 AndroidManifest.xml 中声明它们;
- 正确实现其生命周期回调方法来管理资源和状态。
Activity 提供了应用绘制 UI 的窗口,是用户与应用交互的核心组件。
参考:activity 简介 | Android Developers
2. 如何创建 Activity?
一般当我们创建一个新项目的时候,Android Studio 会帮我们创建一个 MainActivity,如果我们还想创建新的 Activity,最简单的方式是直接通过 Android Studio 直接 new 一个 Activity。但还是得了解 Activity 具体的创建过程。
手动创建主要有三步:
2.1 新建一个类继承 Activity 类或其子类
所有活动都必须继承某个 Activity 类,例如下面这段代码继承了 AppCompatActivity 类,这是 Activity 的子类。
public class TestActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);}
}
项目中任何活动都应重写 Activity 中的 onCreate() 方法,这里的 onCreate() 方法非常简单,就是调用了父类的 onCreate() 方法。当然这里只是默认的实现,后面还需要在这里面加入很多自己的逻辑。
2.2 在 AndroidManifeat 文件中注册
所有的活动都要在 AndroidManifest.xml 中注册才能生效,打开 app/src/main/AndroidManifest.xml,可以找到类似下面这段代码:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.HelloWorld"tools:targetApi="31"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
需要在 <application> ... </application>
标签内部,添加新的 <activity>
标签
<applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.HelloWorld"tools:targetApi="31"><!-- 注册新添加的 SecondActivity --><activity android:name=".TestActivity" android:exported="false" /><!-- 启动入口 MainActivity --><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>
</application>
android:name
-
.TestActivity
:表示类名是TestActivity
,位于主包(com.example.xxx
)下; -
如果类在子包中(比如
ui.TestActivity
),就写成:android:name=".ui.TestActivity"
android:exported
- 必须显式写明(从 Android 12(API 31)开始强制要求);
- 含义:
true
:这个 Activity 可以被外部(如系统、其他 App)启动;false
:只能被你自己 App 内部启动;
- 一般只有有
intent-filter
的 Activity需要设为true
(比如MainActivity
是启动入口),其他默认写false
即可。
如果想将这个活动设置成主活动,只需添加如下 intent-filter
标签:
<activity android:name=".SecondActivity" android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
</activity>
需要注意这里android:exported
必须设为true
。
2.3 创建和加载布局
Android 程序的设计讲究逻辑和视图分离,最好每一个活动都能一个布局,布局就是用来显示界面内容的,新建一个布局,可以看到如下代码:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"></androidx.constraintlayout.widget.ConstraintLayout>
-
<?xml version="1.0" encoding="utf-8"?>
这行属于 XML 标准声明,目的是告诉解析器文件使用的是哪种 XML 版本和编码。必须写在布局文件最开头,否则 Android Studio 无法正确解析。
-
<androidx.constraintlayout.widget.ConstraintLayout ...>
根布局使用
ConstraintLayout
,根布局就是界面的“父容器”,负责托住所有控件,并决定它们的排列方式和界面基础样式。每个 XML 布局文件必须有且只能有一个根布局。 -
xmlns:android="http://schemas.android.com/apk/res/android"
这是 XML 命名空间声明,
xmlns
是 “XML namespace” 的缩写,含义是:“我下面所有的android:
属性都来自这个命名空间”。 -
android:layout_width
和android:layout_height
控制控件的大小行为,是每个控件布局时必须设置的两个核心属性。
对上面代码进行编辑:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/btn_click_me"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="点我"app:layout_constraintTop_toTopOf="parent"app:layout_constraintStart_toStartOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>
这里添加了一个 Button 元素,也就是加了个按钮,创建完布局后,接下来就可以在活动中加载这个布局了。
public class TestActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.test_layout);}
}
这里调用了 setContentView() 方法来给当前活动加载一个布局,在这个方法中,一般会传入一个布局文件的 id 。项目中添加的任何资源都会在 R 文件中生成一个相应的资源 id,可通过 R.layout.test_layout 进行访问。
3. Activity 间跳转与传递数据
准备工作:新建MainActivity 和 SecondActivity,将 MainActivity 设为主活动,在 layout 目录下修改他们的布局文件,代码如下:
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="跳转到 SecondActivity"app:layout_constraintTop_toTopOf="parent"app:layout_constraintStart_toStartOf="parent"android:layout_marginTop="0dp"android:layout_marginStart="0dp" /></androidx.constraintlayout.widget.ConstraintLayout>
<!-- activity_second.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".SecondActivity"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这里是 SecondActivity"app:layout_constraintTop_toTopOf="parent"app:layout_constraintStart_toStartOf="parent"android:layout_marginTop="0dp"android:layout_marginStart="0dp"/></androidx.constraintlayout.widget.ConstraintLayout>
<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools" ><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.IntentTest"tools:targetApi="31" ><activityandroid:name=".SecondActivity"android:exported="false" /><activityandroid:name=".MainActivity"android:exported="true" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
第一个活动添加了一个按钮,第二个活动有一个视图。
要实现活动间的跳转,需要用到 Intent,这是 Android 程序中各组件之间交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent 大致可分为两种:显示 Intent 和隐式 Intent。
3.1 使用显示 Intent
明确指定目标 Activity 的类名,用于同一应用内的跳转。
// 在 MainActivity 中启动 SecondActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
首先创建一个 Intent 对象,这里的两个参数:MainActivity.this
是当前活动自身的对象(实例),表示当前 Activity 的上下文(Context
);SecondActivity.class
是目标页面的类,表示想要跳转到的活动。
startActivity(Intent intent)
是 Android 中启动一个新 Activity 的方法,它属于 Context
类(Activity
继承自 Context
),用来根据传入的 Intent
打开一个新的页面。系统会根据 Intent
里指定的目标类(或者动作)创建新的 Activity 实例,并把它显示出来。
MainActivity 完整代码如下:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});Button button = findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(MainActivity.this, SecondActivity.class);startActivity(intent);}});}
}
Button button = findViewById(R.id.button);
findViewById
是 Activity 提供的方法,用于根据布局文件中控件的 id 找到对应的控件实例。R.id.button
是资源 ID,指向你在 XML 布局里定义的<Button android:id="@+id/button" ... />
。
button.setOnClickListener(new View.OnClickListener() {
- 给按钮
button
设置一个“监听器”,监听用户的点击行为。 setOnClickListener
要传入一个实现了View.OnClickListener
接口的对象。- 这里用的是匿名内部类,直接写一个新的
OnClickListener
对象,简洁方便。
public void onClick(View v) {
onClick
是点击事件被触发时调用的回调方法。- 参数
View v
是被点击的控件对象(这里就是刚才的按钮)。 - 当用户点击按钮时,这个方法里的代码就会执行。
运行程序,结果如下图所示:
点击按钮
3.2 使用隐式 Intent
不直接指定启动组件的意图,而是声明一系列的动作(action)和类别(category),由系统解析这些信息来找到合适的组件进行启动。这种方式的好处是可以使应用更加灵活,不需要知道具体的组件名称,只需知道它能处理的动作类型。
要使用隐式 Intent,首先需要在AndroidManifest.xml中为目标活动配置相应的<intent-filter>
。例如,如果想让 SecondActivity 响应一个特定的动作,可以这样配置:
<activityandroid:name=".SecondActivity"android:exported="false"><intent-filter><action android:name="com.example.activitytest.ACTION_START"/><category android:name="android.intent.category.DEFAULT"/></intent-filter>
</activity>
在<action>
标签中指明了当前活动可以响应 com.example.activitytest.ACTION_START
这个 action,而<category>
标签包含了附加信息,用来更精确地指明活动能够响应的 Intent 中可能带有的 category。只有当 Intent 中指定的 action 和 category 同时匹配时,活动才能响应该 Intent。
修改 MainActivity 中的代码:
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.activitest.ACTION_START");startActivity(intent);}
});
运行程序,效果和上面一样。
每个 Intent 只能指定一个 action,但可以指定多个 category。目前这个程序只有一个默认的 category,我们还可以添加自定义 category,代码如下:
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.activitest.ACTION_START");intent.addCategory("com.example.activitytest.MY_CATEGORY");startActivity(intent);}
});
此时运行程序,点击按钮后程序会直接崩溃。因为我们新增了一个 category 但是 SecondActivity 并没有声明可以响应这个,添加声明后运行程序就恢复正常了。
<activityandroid:name=".SecondActivity"android:exported="false"><intent-filter><action android:name="com.example.activitytest.ACTION_START"/><category android:name="android.intent.category.DEFAULT"/><category android:name="com.example.activitytest.MY_CATEGORY"/></intent-filter>
</activity>
使用隐式 Intent,不仅可以启动自己程序内的活动,还可以启动其他程序内的活动。
例如修改 MainActivity 中代码:
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(Intent.ACTION_VIEW);intent.setData(Uri.parse("http://www.baidu.com"));startActivity(intent);}
});
Intent.ACTION_VIEW
是一个系统内置的 动作常量(action),意思是:“我想查看某个东西”。但这时我们还没有告诉它“查看什么”,需要通过 setData()
设置具体的内容。Uri.parse(...)
会把字符串转为 Uri
类型。启动这个隐式 Intent,Android 系统会去找一个能处理“查看网页”这个动作的组件(通常是浏览器)。然后跳转到那个浏览器界面,加载网页。
重新运行程序,点击按钮,就能看到打开了系统浏览器。
3.3 向下一个活动传递数据
在启动活动时传递数据的思路很简单,Intent 中提供了一系列 putExtra() 方法的重载,可以把我们想要传递的数据暂存在 Intent 中,启动另一个活动后,只需要把这些数据从 Intent 中取出即可。例如下面这段代码:
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String data = "Hello SecondActivity";Intent intent = new Intent(MainActivity.this, SecondActivity.class);intent.putExtra("extra_data", data);startActivity(intent);}
});
这里使用显示 Intent 的方式来启动 SecondActivity,并通过 putExtra() 方法传递了一个字符串。
putExtra(String key, String value)
:给 Intent
添加一个附加信息(键值对)。"extra_data"
是键,data
是值(这里是 "Hello SecondActivity"
)。本质上是把数据附加到 Intent
上,让目标页面接收。这个数据会被打包进 Intent
,随跳转一起传给 SecondActivity
。
然后我们在 SecondActivity 中将传递的数据取出,代码如下:
Intent intent = getIntent(); // 获取传入的 Intent
String data = intent.getStringExtra("extra_data"); // 通过 key 获取字符串
当然除了字符串,其他数据类型也能传递:
/* 发送方(MainActivity) */
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("int_data", 123); // int
intent.putExtra("boolean_data", true); // boolean
intent.putExtra("double_data", 3.14); // double
intent.putExtra("string_data", "Hello"); // String
// 数组
intent.putExtra("string_array", new String[] {"A", "B", "C"});
intent.putExtra("int_array", new int[] {1, 2, 3});
// 集合(ArrayList)
ArrayList<String> list = new ArrayList<>();
list.add("One");
list.add("Two");
intent.putStringArrayListExtra("string_list", list);
startActivity(intent);
/* 接收方(SecondActivity) */
Intent intent = getIntent();
int intData = intent.getIntExtra("int_data", 0);
boolean boolData = intent.getBooleanExtra("boolean_data", false);
double doubleData = intent.getDoubleExtra("double_data", 0.0);
String stringData = intent.getStringExtra("string_data");
String[] strArray = intent.getStringArrayExtra("string_array");
int[] intArray = intent.getIntArrayExtra("int_array");
ArrayList<String> list = intent.getStringArrayListExtra("string_list");
3.4 返回数据给上一个活动
这里还是用书上旧版写法,之后再对新版进行学习。
修改 MainActivity 中的代码如下:
// 定义一个请求码,标识是谁发出的请求
private static final int REQUEST_CODE = 1;...button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(MainActivity.this, SecondActivity.class);startActivityForResult(intent, REQUEST_CODE);}
});
private static final int REQUEST_CODE = 1;
- 定义了一个常量
REQUEST_CODE
,数值为 1。 - 这个叫“请求码”,用于 标记是谁发出的请求,方便多个页面跳转时区分来源。
startActivityForResult(intent, REQUEST_CODE);
- 第一个参数是一个 Intent 对象,用来描述你想启动哪个 Activity,或者你想执行什么操作。
- 第二个参数是一个整型请求码,自定义的数字,代表“你是谁发起的请求”。当跳转多个 Activity,并都期望返回结果时,系统就通过这个请求码来帮你区分是哪一个请求返回的数据。
SecondActivity 中代码如下:
Button backButton = findViewById(R.id.back_button);
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent();intent.putExtra("return_data", "我是 SecondActivity 返回的值");setResult(RESULT_OK, intent); // 设置返回结果finish(); // 结束当前页面}
});
setResult(RESULT_OK, intent);
这句代码告诉系统:当前页面即将结束,并带着一份结果数据返回给上一个页面。
RESULT_OK
常量值 -1 ,表示返回成功(操作成功),常见值还包括:RESULT_CANCELED
常量值 0,表示取消操作或未完成,默认值,不需要调用setResult()
也会返回这个;RESULT_FIRST_USER
常量值 1,自定义的返回码必须从这个值起,避免和系统定义冲突。
- 第二个参数是携带数据的 Intent对象。
finish()
- 关闭当前 Activity(SecondActivity),系统会自动回到之前的 Activity(MainActivity)。
- 同时,MainActivity 的
onActivityResult()
方法会被调用,并收到你通过setResult()
设置的 Intent 和结果码。
使用 setActivityForResult() 方法启动 SecondActivity 活动,在 SecondActivity 被销毁后会回调上一个活动的 onActivityResult() 方法,因此需要在 MainActivity 中重写该方法来获取返回数据。代码如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {String returnedData = data.getStringExtra("return_data");Toast.makeText(this, "返回值:" + returnedData, Toast.LENGTH_SHORT).show();}
}
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK)
requestCode
是之前startActivityForResult(intent, REQUEST_CODE)
传入的编号。用它来确认“是我发出的请求”;resultCode
是目标页面通过 setResult(RESULT_OK) 返回的结果码。确认结果是“成功”的。- 要先判断是谁发出的请求、是否成功,再处理数据。
Toast.makeText(this, "返回值:" + returnedData, Toast.LENGTH_SHORT).show();
- 弹出一个 Toast(短时间提示框);
- 显示的内容是:
"返回值:" + returnedData
; - 举例:如果返回的是
"Hello"
,就显示返回值:Hello
; this
是上下文,Toast.LENGTH_SHORT
表示显示时间短。
3.5 小结
Intent
是 Android 中用于“意图”表达的对象,主要用于启动组件(四大组件:Activity(活动)、Service(服务)、BroadcastReceiver(广播) 和 ContentProvider(内容提供器))和传递数据。上面只是简单介绍了一些简单使用,还有很多用法后续深入学习后会专门写一篇关于 Intent 的文章。