Activity与Intent
学习目标
了解--------------------Activity生命周期,Activity启动模式
掌握--------------------显式Intent,隐式Intent
重点--------------------Intent属性与用法IntentFilter的编码
了解--------------------Activity跳转方法
掌握--------------------Bundle类,Intent类
重点--------------------Bundle类常用方法
了解--------------------Fragment生命周期
掌握--------------------创建Fragment实现切换效果
重点--------------------用户交互界面的设计与实现创建多个Fragment
5.2 Activity概述
1. 安卓四大组件的引出
Activity(活动): Activity是安卓应用程序中最常见的组件之一, 它代表一个用户界面屏幕, 用户可以在屏幕上与应用程序进行交互。例如,当你打开一个浏览器应用时,你将看到一个浏览器窗口,这就是一个Activity。当你在浏览器窗口中点击链接或按钮时,浏览器将启动另一个Activity来显示相关信息。
Service(服务): Service是一种在后台运行的组件,它没有用户界面,但可以执行长时间运行的操作。例如,当你听音乐应用时,音乐播放器可以使用Service来在后台播放音乐。这样,即使你退出音乐应用程序,音乐也可以继续播放。
Broadcast Receiver(广播接收者): Broadcast Receiver是一种用于接收系统广播消息的组件,例如来电、短信、电池电量等。当你的手机接收到这些消息时,它会广播一个消息,应用程序可以使用Broadcast Receiver来接收并处理这些消息。例如,当你的手机电量低于一定水平时,应用程序可以使用Broadcast Receiver来接收这个消息并显示一个警告。
Content Provider(内容提供者): Content Provider是一种用于共享应用程序数据的组件,它可以让应用程序之间共享数据。例如,当你在联系人应用程序中添加新联系人时,联系人应用程序可以使用Content Provider将这些数据存储到手机的数据库中。然后,其他应用程序可以使用Content Provider来读取和修改这些联系人数据。
以王者荣耀为例:
Activity(活动): 在王者荣耀中,每次玩家进入游戏,都会看到一个欢迎界面,这个欢迎界面就是一个Activity。当玩家选择“开始游戏”后,会进入一个选择英雄的界面,这个选择英雄的界面也是一个Activity。当玩家进入游戏后,会进入战斗界面,这个战斗界面也是一个Activity。
Service(服务): 在王者荣耀中,如果你接到一个来电,游戏会自动转入后台,而当你挂断电话时,游戏又会自动回到前台。这是因为王者荣耀使用了一个Service来监听电话状态,确保游戏可以自动切换前后台。
Broadcast Receiver(广播接收者): 在王者荣耀中,当你的好友在游戏中向你发送一个邀请时,你会收到一个弹窗提示,这是通过一个Broadcast Receiver来实现的。它会在后台监听新邀请的广播消息,然后通知你。
Content Provider(内容提供者): 在王者荣耀中,如果你想查看自己的游戏历史记录,这些记录是保存在一个数据库中的,这个数据库可以被其他应用程序(例如社交媒体应用)访问,这是通过一个Content Provider来实现的。
安卓中Activity的存在形式:类、对象
认识Activity的关键:安卓界面由Activity创建和管理,可直接在Activity类里面设计UI界面和控制逻辑。为了实现解耦,现在的安卓开发, 把逻辑控制留在Activity类里面,UI部分放到res/layout目录下, 然后在Activity中使用setContentView方法进行关联。
一个完整的Activity创建和使用:自定义类继承Activity类,自定义布局文件,在Java代码中通过setContentView方法关联UI布局,在清单文件AndroidManifest.xml声明。
组件之间互相通信的中介:Intent
Android 四大组件中的三个(Activity、Service 和 BroadcastReceiver),都是依靠 Intent 启动,Intent 封装了程序想要启动程序的意图,还可以用于被启动组件交换信息。Intent 既可以应用于程序内部的组件交互,也可以用于不同 App 之间。
Content Provider 本身就是一种通信机制,不需要通过Intent。
2. Activity的基本用法
1)创建Activity(创建Java类+创建布局+在清单文件声明)
2)从MainActivity中启动SecondActivity
3)执行finish()结束当前Activity
3. Activity生命周期
生命周期:一个事物从出现到消失的周期性过程
生命周期方法
案例准备:main_activity 和 second_activity 都重写生命周期函数,打印简单内容
1)应用开始创建时
2)从 main_activity 打开 second_activity 时
3)按返回键,回到 main_activity 时
4. Activity启动模式
Android 任务栈:系统中的 Activity 使用栈进行管理。当一个新的 Activity 启动时,它通常被放置在当前堆栈的顶部,并成为正在运行的 Activity ——前一个 Activity 在堆栈中始终位于它的下方,直到新 Activity 退出才会再次出现在前台。在屏幕上可以有一个或多个 Activity 堆栈
Activity的启动模式:standard、singleTop、singleTask、singleInstance。
(1) standard模式:标准模式,也是默认的模式(无需手动配置),只要启动 Activity 就会创建新的实例。
Activity的启动模式:standard、singleTop、singleTask、singleInstance。
(2) singleTop模式:要启动的 Activity 如果刚好在栈顶已存在,则不创建,并且执行它的 onNewIntent 方法。
实验步骤:
1) MainActivity设置为singleTop,SecondActivity设置为默认启动方式;
2)启动1次MainActivity,接着启动1次SecondActivity,再启动2次MainActivity。
实验结果:
Activity的启动模式:standard、singleTop、singleTask、singleInstance。
(3) singleTask模式:要启动的 Activity 在栈内存在,则把它上面的其他 Activity 全部弹出,露出要启动的 Activity,并且执行它的 onNewIntent 方法。
实验步骤:
1) MainActivity设置为singleTask,SecondActivity设置为默认启动方式;
2)启动1次MainActivity,接着启动1次SecondActivity,再启动1次MainActivity。
实验结果:
Activity的启动模式:standard、singleTop、singleTask、singleInstance。
(4) singleInstance模式:设置了该属性的 Activity 在设备上只会存在一个示例,且单独在一个栈里面。
实验步骤:
1) MainActivity设置为singleTask,SecondActivity设置为默认启动方式;
2)启动1次MainActivity,接着启动1次SecondActivity,再启动1次MainActivity。
实验结果:
Activity的启动模式总结:
1.standard:标准模式,也是默认的模式(无需手动配置),只要启动 Activity 就会创建新的实例。应用场景:一般的Activity,都使用这个模式;
2.singleTop:要启动的 Activity 如果刚好在栈顶已存在,则不创建,并且执行它的 onNewIntent 方法。应用场景:Activity被启动起来的渠道多,适合多应用开启调用的Activity,通过这种设置可以避免已经创建过的Activity被重复创建,可以提供共享的,例如:相机、通知栏点击后需要启动一个活动页(该页面也许已经存在栈内);
3.singleTask:要启动的 Activity 在栈内存在,则把它上面的其他 Activity 全部弹出,露出要启动的 Activity,并且执行它的 onNewIntent 方法。应用场景:APP首页/主页;
4.singleInstance:设置了该属性的 Activity 在设备上只会存在一个示例,且单独在一个栈里面。应用场景:APP首页/主页/呼叫来电界面;
5.3 Intent与IntentFilter
Android中提供了Intent机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。
Intent不仅可用于应用程序之间,也可用于应用程序内部的 Activity / Service/Broadcast Receiver之间的交互。 因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。
官方文档: https://developer.android.google.cn/guide/components/intents-filters?hl=zh-cn
Intent种类
显式Intent
显式Intent可以直接通过名称开启指定的目标组件。
实现方法:
- 通过名称开启指定的目标组件:
Intent intent=new Intent(ActivityA.this , ActivityB.class); - 启动Activity,即可跳转:
startActivity(intent);
隐式Intent
隐式Intent,不明确指定启动哪个Activity,而是设置Action、Data、Category等等,让系统来筛选出合适的Activity。筛选是根据所有的来筛选。
通过上面的编码,MainActivity才能通过action name找到SecondActivity。
1.学习隐式Intent就是在不知道目的组件的类名情况下,解决2个问题
如何通过 Intent 跳转到其他的 Activity?
如何给自己的Activity配置IntentFilter,让其他Activity能找到?
所谓 Intent 过滤器,即 AndroidManifest.xml 清单文件中,在声明 Activity 时的子标签配置,这个标签可以配置 action、category 和 data。
如果一个 Intent 对象希望启动 ActivityDemo,该 Intent 对象携带的 action、category、data,要在 ActivityDemo 的配置中完全包含。
2. Intent属性
Intent对象大致包括7大属性:Action(动作)、Data(数据)、Category(类别)、Type(数据类型)、Component(组件)、Extra(扩展信息)、Flag(标志位)。其中最常用的是Action属性和Data属性。
属性之:action
表示要执行的操作类型,例如查看、编辑、发送等。可以使用预定义的动作或自定义动作;
启动intent,安卓系统会检查Action属性是否存在。如果存在,将筛选出具有相同Action属性的Activity,这些Activity将成为潜在的目标Activity;
如果没执行setAction方法,系统会根据所传递的数据类型、URI、数据内容等不同,自动给intent配置一个action;
“android.intent.action.MAIN”表示该 Activity 第一个启动。
属性之:category
表示Intent的类别属性,例如Intent的目的、任务和优先级等。可以使用预定义的类别或自定义类别;
如果Intent的Category属性不为空,则系统会检查与该Intent匹配的Activity是否具有与Intent匹配的Category属性。如果Activity没有匹配的Category属性,则该Activity不是目标Activity。;
必须配置;
“android.intent.category.LAUNCHER”表示显示在程序列表里(即有个图标可以点击)
属性之:category
如果多个Activity的action一样,而category不一样,则会让用户选择使用哪个。
属性之:data
表示Intent要操作的数据,例如文件的URI、电话号码、电子邮件地址等;
Data是用一个 Uri 对象来表示的, Uri 代表数据的地址,属于一种标识符。通常情况下,我们使用Action+Data属性的组合来描述一个意图:做什么;
属性之:data
Intent 的 Data 属性是 Uri 类型,格式为:<scheme>://<host>:<port>/<path>
,这四个部分由四个属性表示。
在 intent-filter 中,这四个的设置都是可选的,但是存在依赖关系:
如果未指定scheme,则系统会忽略host;
如果未指定host,则系统会忽略port;
如果scheme和host都未指定,则系统会忽略path。
当将Intent中的Uri与intent-filter中的Uri进行匹配时,它只与过滤器中包含的URI部分进行比较。例如:
若intent-filter只配置1个scheme,则所有配置了该scheme的intent都能匹配该Activity;
若intent-filter只配置1个scheme和1个权限,且无配置path,则具有相同scheme和权限的intent都能匹配,不管path值如何。
若intent-filter只配置1个scheme、1个权限和1个path,则只有intent配置了同样的该三个属性的可以匹配。
属性之:Component、Type、Extras、Flags
Component(组件):表示要处理Intent的组件,例如Activity、Service或Broadcast Receiver等;
Type(类型):表示Intent要操作的数据类型,例如文本、图片、音频、视频等。可以使用预定义的MIME类型或自定义类型;
Extras(附加数据):表示要传递给组件的额外数据,例如键值对、序列化对象等;
Flags(标志):表示Intent的其他标志,例如FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_CLEAR_TOP等。
3.系统自带的Intent
案例:自己的App打开系统的闹钟App并进行定时。
官方文段:https://developer.android.google.cn/guide/components/intents-common?hl=zh-cn
5.4 数据传递
1. putExtra()
putExtra()方法数据传递原理:Activity之间需要用到Intent提供的。putExtra()方法传递数据,通过getIntent()方法获取lntent对象,然后调用getStringExtra(String name),根据传入的键值,取出相应的数据。如果传递的是字符串类型数据,就要使用getStringExtra()方法获取传递的数据。如果传递的数据是整数类型,则使用getIntExtra()方法,以此类推。
Intent的putExtra()方法
Activity之间使用putExtra()方法传递数据的关键代码:
Activity之间传递数据的方法:
Intent 携带数据的方法 | 说明 |
---|---|
putExtras(Bundle data) | 向 Intent 对象放入需要“携带”的数据包 |
Bundle getExtras() | 从 Intent 对象中取出“携带”的数据包 |
putExtra(String key, Xxx value) | 向 Intent 对象存入 key-value 形式的数据 |
getXxxExtra(String name) | 从 Intent 对象按 key 取出指定内容的数据 |
putXxx(String key, Xxx data) | 向 Bundle 对象放入 Int,long 等各种类型的数据 |
getXxx(String key) | 从 Bundle 中取出 Int,long 等各种类型的数据 |
putSerializable(String key, Serializable data) | 向 Bundle 对象放入可序列化的对象 |
getSerializable(String key, Serializable data) | 从 Bundle 对象取出一个可序列化的对象 |
3. Bundle
Bundle—A mapping from String keys to various Parcelable values.
(1)Bundle对象经常使用在Activity之间或者线程间传递数据,传递的数据可以是boolean、byte、int、long、float、double等基本类型或它们对应的数组,也可以是对象或对象数组。
(2)当Bundle对象传递的是对象或对象数组时,必须实现Serializable(串行)或Parcelable(包装)接口。
(3)Bundle提供了各种常用类型的putXxx()/getXxx()方法,用于读写基本类型的数据。
5. 数据回传
数据回传应用:微信中朋友圈“所在位置”的选择
关键代码分析:
核心方法:
5.5 Fragment
2. Fragment的来由
Android在3.0版本引入了 Fragment(碎片) 功能,它非常类似于Activity,可以像Activity一样包含布局。它出现的初衷是为了适应大屏幕的平板电脑,使用Fragment我们可以把屏幕划分成几块,合理利用屏幕空间。
下图是一个Fragment分别对应平板与手机之间不同情况的处理图。
3. 布局填充器
布局填充器(LayoutInflater)是Android应用程序中用于动态加载布局文件并生成对应View对象的一个工具类,它的主要作用是将XML文件中的布局解析成对应的View对象,供应用程序使用。
核心方法:public View inflateint resource, ViewGroup root, boolean attachToRoot)
resource:表示要加载的布局文件的ID,通常是一个XML文件。这个参数指定了需要加载的布局资源,例如R.layout.main表示要加载的是名为main的布局文件。
root:表示生成的View对象的父容器ViewGroup,如果传入了一个ViewGroup,生成的View对象就会被添加到这个ViewGroup中。如果传入null,表示生成的View对象不需要添加到任何父容器中。
attachToRoot:表示是否将生成的View对象添加到root指定的父容器中。如果为true,表示生成的View对象会被添加到root指定的父容器中,否则不会添加。
4. 创建Fragment
5. Fragment关键方法
当Fragment被创建时,它的生命周期方法onCreateView()会被调用,用于创建Fragment的用户界面。onCreateView()方法会返回一个View对象,该对象会显示在包含Fragment的Activity中。
6. Fragment的加载
1)静态加载
在 main_activity.xml 中引入 fragment 标签,指定对应的 Fragment
2)动态加载
步骤如下:
7. Fragment的生命周期
- Fragment的生命周期
8. Fragment使用要点
(1)Fragment并不能单独使用,它需要嵌套在Activity 中使用,作为Activity界面的一部分组成出现。
(2)可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可在多个Activity中使用。
(3)在Activity运行过程中,可以通过transaction的add()、remove()、replace()方法来添加、移除或者替换Fragment。
(4)Fragment可以响应自己的输入事件,并且有自己的生命周期,但它的生命周期受其宿主Activity的生命周期影响。比如Activity被destroy销毁了,它也会跟着被销毁。
总结
1.Activity生命周期
2.Activity启动模式
3.Intent
4.IntentFilter
5.数据传递
6.数据回传
7.Fragment生命周期
8.在Activity中添加Fragment
复习提问
1.Activity有哪四种启动模式?简述其特点。
standard模式, singleTop模式, singleTask模式, singleInstance模式。
2.简述显式Intent与隐式Intent的区别。
(1)显式Intent可以直接通过名称开启指定的目标组件。
(2)隐式Intent通过指定action和category等属性,系统根据这些信息进行分析后寻找目标Activity。
请简述Intent对象putExtra方法数据传递原理。
putExtra方法数据传递原理:Activity之间需要用到Intent提供的putExtra()方法传递数据,通过getIntent()方法获取lntent对象,然后调用getStringExtra(String name),根据传入的键值,取出相应的数据。如果传递的是字符串类型数据,就要使用getStringExtra()方法获取传递的数据。如果传递的数据是整数类型,则使用getIntExtra()方法,以此类推。
什么是Fragment?
Fragment(碎片)是一种可以嵌入在Activity中的UI片段,它可以用来描述Activity中的一部分布局。
简述在Activity中动态添加Fragment的步骤。
创建一个Fragment的实例对象。
获取FragmentManager(Fragment管理器)的实例。
开启FragmentTransaction(事务)。
向Activity的布局容器(一般为FrameLayout)中添加Fragment。
通过commit()方法提交事务。
闹钟,定时器
https://developer.android.google.cn/guide/components/intents-common?hl=zh-cn#CreateAlarm
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1111111111"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bt1"
android:text="定时器"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bt2"
android:text="闹钟"
/>
</LinearLayout>
MainActivity.java
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.AlarmClock;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//定时器
findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startTimer("定时器消息", 2);
}
});
//闹钟
findViewById(R.id.bt2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createAlarm("llle",6,1);
}
});
}
//定时器
public void startTimer(String message, int seconds) {
Intent intent = new Intent(AlarmClock.ACTION_SET_TIMER)
.putExtra(AlarmClock.EXTRA_MESSAGE, message)
.putExtra(AlarmClock.EXTRA_LENGTH, seconds)
.putExtra(AlarmClock.EXTRA_SKIP_UI, true);
startActivity(intent);
}
//闹钟
public void createAlarm(String message, int hour, int minutes) {
Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM)
.putExtra(AlarmClock.EXTRA_MESSAGE, message)
.putExtra(AlarmClock.EXTRA_HOUR, hour)
.putExtra(AlarmClock.EXTRA_MINUTES, minutes);
startActivity(intent);
}
}
AndroidManifest.xml
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<application
android: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.MyApplication"
tools:targetApi="31">
<activity
android: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>
碎片,Fragment
one1.jpg
one2.jpg
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<fragment
android:id="@+id/frag1"
android:name="com.example.myapplication0421.BlankFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/bt11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="碎片1" />
<Button
android:id="@+id/bt22"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="碎片2" />
</LinearLayout>
</LinearLayout>
MainActivity.java
package com.example.myapplication0421;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//切换第一个碎片
findViewById(R.id.bt11).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction transaction =getSupportFragmentManager().beginTransaction();
// FirstFragment firstFragment = new FirstFragment();
BlankFragment blankFragment = new BlankFragment();
transaction.replace(R.id.frag1,blankFragment);
transaction.commit();
}
}); findViewById(R.id.bt22).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction transaction =getSupportFragmentManager().beginTransaction();
// FirstFragment firstFragment = new FirstFragment();
BlankFragment2 blankFragment2 = new BlankFragment2();
transaction.replace(R.id.frag1,blankFragment2);
transaction.commit();
}
});
}
}
fragment_blank.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/background_light"
android:orientation="vertical"
tools:context=".BlankFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="1111111111"
android:textSize="30dp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/one1"
android:layout_marginTop="50dp"
/>
</FrameLayout>
fragment_blank2.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/background_light"
android:orientation="vertical"
tools:context=".BlankFragment2">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="30dp"
android:text="22222222222" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:src="@drawable/one2"
/>
</FrameLayout>
作业
四,项目实验5——Activity与Intent
http://t.csdnimg.cn/Df4yD