创建 app 工程
创建 Empty Activity
项目配置
- 程序名
- 包名
- 路径
- 语言
- SDK API
- 构建程序
打印输出
java
Log.d("调试", "调试信息");
Log.e("错误", "错误信息");
Log.w("警告", "警告信息");
Log.i("一般", "一般信息");
Log.v("冗余", "冗余信息");
工程目录
切换到 App 查看目录
- manifests/AndroidManifest.xml 是 app 运行下的配置信息
- java 存放java 测试文件和源文件
- res:
- drawable:存放图形描绘文件与图片文件
- layout:存放App页面的布局文件
- mipmap:存放App 启动图标
- values:存放一些常量定义文件,例如字符string.xml、像素常量colors.xml、样式风格定义style.xml
Gradle Scripts 工程编译配置文件
- build.gradle:该文件分为项目级别与模块级别,用于描述 app 工程编译规则
- proguard-rules.pro:该文件描述java代码的混淆规则
- gradle.properties 该文件用于配置编译工程的命令行参数,一般无需改动
- settings.gradle:配置需要编译哪些模块,初始为 include app 表示编译app模块
- local.properties:项目的本地配置文件,在工程编译时生成,描述开发者电脑的环境配置,包括sdk本地路径,Ndk本地路径
gradle 配置
Gradle 是一个项目自动化构建工具,帮我们做了依赖、打包、部署、发布、各种渠道的差异管理工作。
阅读 模块的 gradle 配置信息
kts
plugins {
// 插件用于构建 Android 应用程序
id("com.android.application")
}
android {
// 应用程序的命名空间
namespace = "com.example.myapplication"
// api 33,对应 Android 13
compileSdk = 33
// 默认配置
defaultConfig {
// 应用程序包名
applicationId = "com.example.myapplication"
// 最小支持的sdk
minSdk = 21
// 当前sdk
targetSdk = 33
// 版本代码
versionCode = 1
// 版本名称
versionName = "1.0"
// 单元测试的位置
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
// 构建类型设置
buildTypes {
// 发布版本相关的设置
release {
// 是否开启代码混淆
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
// 编译选项
compileOptions {
// 使用java8 的特性
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
// 库依赖
dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
gradle 配置插件使用国内库
AndroidManifest.xml Activity 认识
Activity 是一个应用程序组件,用户可以交互完成某一项任务
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">
<!--
allowBackup:允许备份
icon:手机上的图标
label:应用程序的显示名称
roundIcon:圆角名称、
supportsRtl:是否支持阿拉伯语波斯语,从右往左排列,true支持
theme:显示主题
-->
<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 舞台
name:默认的舞台也是第一个页面,展示是什么,配置主页
-->
<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>
界面显示与应用
使用 html 描绘布局,使用java完成逻辑
LinearLayout 是 Android 中的一个布局容器,用于按照水平或垂直方向排列其子视图。它是 ViewGroup 的子类,允许通过指定方向将子视图排列在一条线上
自适应写一个
xml
<?xml version="1.0" encoding="utf-8"?><!--
LinearLayout: 是ViewGroup 的子类
layout_width 和 layout_height 设置布局的宽度为与父容器相匹配,即占满整个父容器的宽度
orientation="vertical" 设置布局的方向为垂直方向,子视图将垂直排列
android:gravity="center" 是一个布局属性,设置 ViewGroup 内部的子视图相对于容器的对齐方式
-->
<LinearLayout 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:gravity="center"
android:orientation="vertical">
<!--
这是一个简单的 TextView 元素的 XML 布局代码片段。
在这个 TextView 中,展示了 "Hello Word" 这个文本。
通过设置 id 属性,我们可以在代码中引用和操作这个 TextView。
tools:ignore="HardcodedText" 用于告诉开发工具在静态分析时忽略硬编码文本的警告。
宽度高度为适应宽高
-->
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Word"
tools:ignore="HardcodedText" />
</LinearLayout>
在java中修改
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 加载布局文件
setContentView(R.layout.activity_main);
// 获取界面上的文本框
TextView viewById = findViewById(R.id.tv);
// 修改文本
viewById.setText("你好世界!");
}
创建新页面与java代码
页面在 app\src\main\res\layout 下面创建,选择 Layout xml file
- 输入文件名称,下面是默认的跟标签
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_name2"/>
</LinearLayout>
在主 xml 中添加按钮,点击跳转新页面
xml
<!--
创建一个按钮,高度和宽度由内容撑开,文本引用 string中的button
-->
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button"/>
- 创建 java 代码,在 java目录下面第一个包下创建一个java文件
java
package com.example.myapplication;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class myActivity2 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
* AppCompatActivity 中的方法,用于指定布局,布局为 activity_main2
*/
setContentView(R.layout.activity_main2);
}
}
- 在 main 根目录下 AndroidManifest.xml 中添加 activity 活动页
创建 myActivity2
java
public class myActivity2 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
* AppCompatActivity 中的方法,用于指定布局,布局为 activity_main2
*/
setContentView(R.layout.activity_main2);
}
}
在主 activity 中跳转
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView viewById = findViewById(R.id.tv);
viewById.setText("你好世界!");
Button button = findViewById(R.id.button);
// 创建点击事件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 跳转到 我们先创建的页面
Intent intent = new Intent();
// 跳转指定类
/*
* MainActivity.this 因为在匿名内部类,拿不到this
* myActivity2.class 跳转到这里
*/
intent.setClass(MainActivity.this, myActivity2.class);
// 执行跳转
startActivity(intent);
}
});
}
}
单位
- px:图像元素,在大小不一样的屏幕上,越大的屏幕,越清晰,每一个px的像素占据的大小不一样
- dpi(像素密度):它的尺寸只与屏幕尺寸有关,也叫做dp,相同英寸的屏幕,计算的dp是一样的
计算规则:假设屏幕英寸 4.95,宽 1920 高 1080
dpi = <math xmlns="http://www.w3.org/1998/Math/MathML"> 192 0 2 + 108 0 2 4.95 \frac{\sqrt[]{1920^2 + 1080^2}}{4.95} </math>4.9519202+10802
dpi = 444.62
- density(密度):指屏幕上每平方英寸(2.54^2 平方厘米)含有的像素点数量,上面得出的结果是 444.62 ^ 2
- dip:就是将 dpi 转化成 px 的工具,计算1 dpi = 多少 px,因为最终是以 px显示在页面上
总结:像素密度越高,越清晰。
对于相同尺寸的手机,即使分辨率不同,同dp的组件占用的屏幕比例也会相同
文本
设置文本颜色
java
TextView viewById = findViewById(R.id.tv);
// 使用 color 类设置文本颜色
viewById.setTextColor(Color.GREEN);
// 另一种设置方式,0xff00ff00
// 0x 十六进制,ff 不透明,00 红色,ff绿色,00
viewById.setTextColor(0xff00ff00);
视图
设置 组件的宽高
- 视图宽度通过属性 android:layout_width 设置
- 视图高度通过属性 android:layout_height 设置
值下列 3 种
- match_parent:表示与上级视图保持一致。
- wrap_content:表示与内容自适应。
- 以dp为单位的具体尺寸
xml
<TextView
android:id="@+id/tv"
android:layout_width="50sp"
android:layout_height="wrap_content"
android:text="无什么也没有"
tools:ignore="HardcodedText" />
使用 java 代码修改宽高
需要确保 xml 中 设置的是由内容撑开大小
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取 组件
TextView viewById = findViewById(R.id.tv);
// 获取布局参数,注意默认用的是px布局,需要把 dp 转换成 px
ViewGroup.LayoutParams params = viewById.getLayoutParams();
params.width = dip2px(this, 300);
// params.height = 0;
// 重新设置
viewById.setLayoutParams(params);
}
// 根据手机屏幕从 dp 转换成 px像素
public static int dip2px(Context context, float dbValue) {
// 获取当前手机像素密度(1个dp = 几个px)
float density = context.getResources().getDisplayMetrics().density;
// 四舍五入取整
return Math.round(dbValue * density);
}
间距
设置视图的间距有两种方式:
- 采用layout_margin属性
- layout_margin
- layout_marginLeft
- layout_marginTop
- layout_marginRight
- layout_marginBottom
- 采用padding属性
- padding
- paddingLeft
- paddingTop
- paddingRight
- paddingBottom
xml
<!--设置背景色为黄色-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="300dp"
android:gravity="center"
android:background="#00aaff"
android:orientation="vertical">
<!--中间布局颜色为黄色-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffff99"
android:layout_margin="20dp"
android:padding="60dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000"/>
</LinearLayout>
</LinearLayout>
对齐方式
设置视图的对齐方式有两种途径:
- 采用layout_gravity属性,它指定了当前视图相对于上级视图的对齐方式。
- 采用gravity属性,它指定了下级视图相对于当前视图的对齐方式。
layout_gravity与gravity的取值包括:left、top、right、bottom,还可以用竖线连接各取值,例如left|top表示即靠左又靠上,也就是朝左上角对齐。
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#ffff99"
android:gravity="center"
android:orientation="horizontal">
<!-- 第一个子布局背景红色,中下对齐,下级视图靠左对齐
layout_weight="1":分配权重最大
底部对齐
gravity:控制自己的内部属性
-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_gravity="bottom"
android:layout_marginEnd="10dp"
android:layout_weight="1"
android:background="#ff0000"
android:gravity="left">
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#00ffff" />
</LinearLayout>
<!-- 顶端对齐 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_gravity="top"
android:layout_weight="1"
android:background="#ff0000"
tools:ignore="Suspicious0dp"
android:gravity="right">
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#00ffff" />
</LinearLayout>
</LinearLayout>
布局
线性布局LinearLayout
线性布局内部的各视图有两种排列方式:
- orientation属性值为:
- horizontal:内部视图在水平方向从左往右排列。
- vertical:内部视图在垂直方向从上往下排列。
- 默认水平方向排列
权重:指的是线性布局的下级视图各自拥有多大比例的宽高。
权重属性名叫:layout_weight
- layout_width填0dp时:layout_weight 表示水平方向的宽度比例。
- layout_height填0dp时,layout_weight表示垂直方向的高度比例。
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="#ffff99"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="竖排排第一个"
android:textColor="#000000"
android:textSize="17sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="竖排排第二个"
android:textColor="#000000"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="横排第一个"
android:textColor="#000000"
android:textSize="17sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="横排第二个"
android:textColor="#000000"
android:textSize="17sp" />
</LinearLayout>
</LinearLayout>
相对布局RelativeLayout
- 相对布局的下级视图位置由其他视图决定。
- 用于确定下级视图位置的参照物分两种:
- 与该视图自身平级的视图
- 该视图的上级视图(也就是它归属的RelativeLayout)
如果不设定下级视图的参照物,那么下级视图默认显示在RelativeLayout内部的左上角。
相对位置的属性取值 | 相对位置说明 |
---|---|
layout_toLeftOf | 当前视图在指定视图的左边 |
layout_toRightOf | 当前视图在指定视图的右边 |
layout_above | 当前视图在指定视图的上方 |
layout_below | 当前视图在指定视图的下方 |
layout_alignLeft | 当前视图与指定视图的左侧对齐 |
layout_alignRight | 当前视图与指定视图的右侧对齐 |
layout_alignTop | 当前视图与指定视图的顶部对齐 |
layout_alignBottom | 当前视图与指定视图的底部对齐 |
layout_centerInParent | 当前视图在上级视图中间 |
layout_centerHorizontal | 当前视图在上级视图的水平方向居中 |
layout_centerVertical | 当前视图在上级视图的垂直方向居中 |
layout_alignParentLeft | 当前视图与上级视图的左侧对齐 |
layout_alignParentRight | 当前视图与上级视图的右侧对齐 |
layout_alignParentTop | 当前视图与上级视图的顶部对齐 |
layout_alignParentBottom | 当前视图与上级视图的底部对齐 |
演示一个指定id,和不指定id,不指定id跟父级,并且可以指定多个
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<TextView
android:id="@+id/ssss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我在中间"
android:layout_centerInParent="true"
android:textColor="#000000"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我在水平中间"
android:layout_centerHorizontal="true"
android:textColor="#000000"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="在中间的左边"
android:layout_toLeftOf="@id/ssss"
android:textColor="#000000"
android:textSize="16sp" />
</RelativeLayout>
网格布局GridLayout
网格布局,能实现多行多列的排序。
默认从左往右、从上到下排列
新增了两个属性:
- columnCount属性,它指定了网格的列数,即每行能放多少个视图
- rowCount属性,它指定了网格的行数,即每列能放多少个视图
xml
<?xml version="1.0" encoding="utf-8"?><!--
columnCount:2 行
rowCount:2列
-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnCount="2"
android:rowCount="2">
<TextView
android:layout_width="180dp"
android:layout_height="60dp"
android:background="#ffcccc"
android:gravity="center"
android:text="浅红色" />
<TextView
android:layout_width="180dp"
android:layout_height="60dp"
android:background="#ffaa00"
android:gravity="center"
android:text="浅红色" />
</GridLayout>
滚动视图ScrollView
滚动视图有两种:
- ScrollView,垂直方向的滚动视图
- HorizontalScrollView 水平方向的滚动视图
注意事项:
-
垂直方向滚动时,layout_width 属性值设置为 match_paren,layout_height属性值设置为wrap_content。
-
水平方向滚动时,layout_width 属性值设置为 wrap_content,layout_height 属性值设置为match_parent
-
Binary XML file line #32 in com.example.myapplication:layout/activity_main: HorizontalScrollView can host only one direct child,如果报错上面的错误,表示出现了多个节点。
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--水平方向的滚动视图,当前高度 200-->
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="200dp">
<!-- 滚动视图,只能出现唯一的节点 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="300dp"
android:layout_height="match_parent"
android:background="#aaffff" />
<TextView
android:layout_width="300dp"
android:layout_height="match_parent"
android:background="#ffff00"/>
</LinearLayout>
</HorizontalScrollView>
<!--
垂直方向滚动视图,高度自适应
android:fillViewport="true":强制填充满整个屏幕,即使子节点未达到
-->
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<!-- 滚动视图,只能出现唯一的节点 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="#00ff00" />
<TextView
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="#ffffaa"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
按钮
Button 由 TextView 派生而来,Button 默认拥有背景,内部居中对其,需要注意下面两个属性。
- textAllCaps:默认为false,表示不转不写,开启会将文本转换为大写
- onClick属性:用来接管用户的点击动作,指定了点击按钮时要触发哪个方法
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--定义一个按钮,不转大写文字,定义函数 onclick-->
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Word"
android:textAllCaps="false"
android:onClick="dbClick"
/>
<TextView
android:id="@+id/tv_string"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击按钮查看哦!"/>
</LinearLayout>
java代码
java
public class MainActivity extends AppCompatActivity {
// 声明一个文本视图
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 在主方法中获取
textView = findViewById(R.id.tv_string);
}
// activity_main.xml 中按钮绑定了点击方法doClick
// 必须是公共的,私有的会报错
// view 实际是点击的源
public void dbClick(View view) {
textView.setText("我看到内容啦!");
// 获取 button 按钮定义的文本内容
Button view1 = (Button) view;
// 转成文本
String text = view1.getText().toString();
Log.d("2222", text);
}
}
代码实现监听点击
java
@Override
protected void onCreate(Bundle savedInstanceState) {
// 在主方法中获取
Button view = findViewById(R.id.tv_string);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((Button) v).setText("我点击了!");
}
});
}
技巧:这样可以全部按钮事件进行整合
java
findViewById(R.id.btn_cancel).setOnClickListener(this);
代码实现监听长按点击
java
@Override
protected void onCreate(Bundle savedInstanceState) {
请 // 在主方法中获取
Button view = findViewById(R.id.tv_string);
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
((Button) v).setText("我长按了!");
return true;
}
});
}
不可用按钮
- 不可用按钮:按钮不允许点击,即使点击也没反应,同时按钮文字为灰色;
- 可用按钮:按钮允许点击,点击按钮会触发点击事件,同时按钮文字为正常的黑色;
是否允许点击由enabled属性控制,属性值为true时表示允许点击,为false时表示不允许点击。
xml 禁用按钮
xml
<!--
android:enabled="false" false 禁用按钮,默认true
-->
<Button
android:id="@+id/tv_string"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text="Hello Word"
android:textAllCaps="false" />
代码设置禁用
java
// 在主方法中获取
Button view = findViewById(R.id.tv_string);
// 设置为 true
view.setEnabled(true);
图像
图像视图ImageView
注意导入图片必须以英文开头
默认 fitCenter
- src="@drawable/apple":引用图片路径
xml
<!--
android:scaleType="fitXY" 全面拉伸
android:scaleType="fitStart" 使图片位于上方或者左侧并拉伸
android:scaleType="fitEnd" 使图片位于下方方或者右侧并拉伸
android:scaleType="fitCenter" 使图片位于视图中间并拉伸
android:scaleType="center" 使图片位于中间
android:scaleType="centerCrop" 使图片充满视图并拉伸中间
android:scaleType="centerInside" 使图片缩小并位于中间,只能缩小
-->
<ImageView
android:layout_width="wrap_content"
android:layout_height="220dp"
android:scaleType="center"
android:src="@drawable/apple" />
在代码中设置图片
java
// 获取布局中的 ImageView 组件,通过 ID 为 R.id.img 来查找
ImageView imgId = findViewById(R.id.img);
// 设置 ImageView 显示的图像资源,R.drawable.apple 是一个指向 res/drawable 目录下的图像资源的引用
imgId.setImageResource(R.drawable.apple);
图像按钮ImageButton
xml
<!--
src 设置资源 scaleType 设置缩放
-->
<ImageButton
android:layout_width="match_parent"
android:layout_height="80dp"
android:src="@drawable/apple"
android:scaleType="fitCenter"
/>
按钮背景色不能更换问题
在 res/values/themes.xml 将 Style 更改下面
xml
<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
同时展示文本与图像
xml
<!--
android:drawableLeft="@drawable/ic_launcher_foreground" 设置图标在左侧
android:padding="8dp" 图片与文字的间距
-->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000000"
android:drawableLeft="@drawable/ic_launcher_foreground"
android:text="图标在左边"
android:padding="8dp"
android:textColor="@color/white" />
活动组件---Activity
打开新页面
java
// 跳转新页面
findViewById(R.id.nextButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 当前页面,目标页面
startActivity(new Intent(MainActivity.this, MainActivity2.class));
}
});
回到上一个页面,关闭当前页面
java
// 点击完成,回到上一个页面,也就是关闭当前页面
findViewById(R.id.finiesh).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 关闭当前页面
finish();
}
});
生命周期
- onCreate:创建活动。把页面布局加载进内存,进入了初始状态
- onStart:开始活动。把活动页面显示在屏幕上,进入了就绪状态
- onResume:恢复活动。活动页面进入活跃状态,能够与用户正常交互
- onPause:暂停活动。页面进入暂停状态,无法与用户正常交互。
- onStop:停止活动。页面将不在屏幕上显示。
- onDestroy:销毁活动。回收活动占用的系统资源,把页面从内存中清除。
- onRestart:重启活动。重新加载内存中的页面数据。
- onNewIntent:重用已有的活动实例。
打开新页面的方法调用顺序为
关闭旧页面的方法调用顺序为onPause→onStop→onDestroy
跳转页面声明周期执行顺序
- 主页面活动创建(onCreate)
- 主页面活动就绪(onStart)
- 主页面活动中(onResume)
跳转到第二页面
- 主页面活动暂停(onStop)
- 第二页面活动创建(onCreate)
- 第二页面活动就绪(onStart)
- 第二页面活动中(onResume)
- 主页面活动停止(onStop)
从第二页面返回到第一页面
- 第二页面活动暂停(onStop)
- 主页面活动重启(onRestart)
- 主页面活动就绪(onStart)
- 主页面活动中(onResume)
- 第二页面活动停止(onStop)
- 第二页面活动销毁(onRestoty)
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("one", "主页面活动创建");
}
@Override
protected void onStart() {
super.onStart();
Log.e("one", "主页面活动就绪");
}
@Override
protected void onResume() {
super.onResume();
Log.e("one", "主页面活动中");
}
@Override
protected void onPause() {
super.onPause();
Log.e("one", "主页面活动暂停");
}
@Override
protected void onStop() {
super.onStop();
Log.e("one", "主页面活动停止");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("one", "主页面活动销毁");
}
@Override
protected void onRestart() {
super.onRestart();
Log.e("one", "主页面活动重启");
}
活动存入栈中,是先进后出的数据结构。
配置启动模式
xml
<!--
android:launchMode="standard" 配置启动模式为 标准模式,也就是默认的
android:launchMode="singleTop" 单顶模式
android:launchMode="singleTask" 单任务模式
android:launchMode="singleInstance" 单实例模式
-->
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
标准模式:按照栈的先进后出
活动栈状态 | 操作 | 结果 |
---|---|---|
ActivityA | 启动 ActivityB | ActivityA -> ActivityB |
ActivityA -> ActivityB | 启动 ActivityC | ActivityA -> ActivityB -> ActivityC |
ActivityA -> ActivityB -> ActivityC | 返回 | ActivityA -> ActivityB |
ActivityA -> ActivityB | 启动 ActivityD | ActivityA -> ActivityB] -> ActivityD |
单顶模式:检查栈顶是否是启动的activity,如果是就不会重新启动,采用复用
活动栈状态 | 操作 | 结果 |
---|---|---|
ActivityA | 启动 ActivityB | ActivityA -> ActivityB |
ActivityA -> ActivityB | 启动 ActivityC | ActivityA -> ActivityB -> ActivityC |
ActivityA -> ActivityB -> ActivityC | 启动 ActivityC | ActivityA -> ActivityB -> ActivityC |
ActivityA -> ActivityB -> ActivityC | 启动 ActivityD | ActivityA -> ActivityB -> ActivityC -> ActivityD |
单任务实例:栈里面存在活动,复用该活动,并清除该活动以上所有活动
活动栈状态 | 操作 | 结果 |
---|---|---|
ActivityA | 启动 ActivityB | ActivityA -> ActivityB |
ActivityA -> ActivityB | 启动 ActivityC | ActivityA -> ActivityB -> ActivityC |
ActivityA -> ActivityB -> ActivityC | 启动 ActivityB | ActivityA -> ActivityB |
单实例模式:启动新活动时,将该活动的实例放入一个新的栈中,原栈的实例列表保持不变。 适用于需要与应用的其余部分隔离的任务。
栈1状态 | 栈2状态 | 操作 | 结果 |
---|---|---|---|
[ActivityA] | 启动 ActivityB | [ActivityA] -> [ActivityB] | |
[ActivityA] | [ActivityB] |
通过代码设置启动标识
启动标志 | 说明 | 备注 |
---|---|---|
Intent.FLAG_ACTIVITY_NEW_TASK |
开辟一个新的任务栈。 | 类似于 Standard ,不同于如果原来没有栈,则会创建新的栈 |
Intent.FLAG_ACTIVITY_SINGLE_TOP |
当栈顶为待跳转的活动实例时,则重用栈顶的实例。 | 等同于 singleTop |
Intent.FLAG_ACTIVITY_CLEAR_TOP |
当栈中存在待跳转的活动实例时,则重新创建一个新实例,并清除原实例上方的所有实例。 | 类似于 singleTask 不同的是会先 onDestory 在 onCreate |
Intent.FLAG_ACTIVITY_NO_HISTORY |
栈中不保存新启动的活动实例。 | 与 Standard 类似,区别于不保存活动实例 |
Intent.FLAG_ACTIVITY_CLEAR_TASK |
跳转到新页面时,栈中的原有实例都被清空。 | 该标志非常暴力,需要结合 Intent.FLAG_ACTIVITY_NEW_TASK 使用 |
java
// 跳转新页面
findViewById(R.id.nextButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
// 配置启动方式,开辟一个新的任务线
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
显式Intent和隐式Intent
显示 Intent
Intent是各个组件之间信息沟通的桥梁,它用于Android各组件之间的通信
- 标明本次通信请求从哪里来、到哪里去、要怎么走。
- 发起方携带本次通信需要的数据内容,接收方从收到的意图中解析数据。
- 发起方若想判断接收方的处理结果,意图就要负责让接收方传回应答的数据内容。
Intent 属性
元素名称 | 设置方法 | 说明与用途 |
---|---|---|
Component | setComponent | 组件,它指定意图的来源与目标 |
Action | setAction | 动作,它指定意图的动作行为 |
Data | setData | 即Uri,它指定动作要操纵的数据路径 |
Category | addCategory | 类别,它指定意图的操作类别 |
Type | setType | 数据类型,它指定消息的数据类型 |
Extras | putExtras | 扩展信息,它指定装载的包裹信息 |
Flags | setFlags | 标志位,它指定活动的启动标志 |
java
// (1) 在 Intent 构造函数中指定
Intent intent1 = new Intent(this, ActNextActivity.class);
startActivity(intent1);
// (2) 调用 setClass 方法指定
Intent intent2 = new Intent();
intent2.setClass(this, ActNextActivity.class);
startActivity(intent2);
// (3) 调用 setComponent 方法指定
Intent intent3 = new Intent();
ComponentName component = new ComponentName(this, ActNextActivity.class);
intent3.setComponent(component);
startActivity(intent3);
隐式Intent,没有明确指定要跳转的目标活动,只给出一个动作字符串让系统自动匹配,属于模糊匹配。
隐式 Intent
动作名称既可以通过setAction方法指定,也可以通过构造函数Intent(String action)直接生成意图对象。
系统动作如下:
系统动作常量名 | 系统动作的常量值 | 说明 |
---|---|---|
ACTION_MAIN | android.intent.action.MAIN | App启动时的入口 |
ACTION_VIEW | android.intent.action.VIEW | 向用户显示数据 |
ACTION_SEND | android.intent.action.SEND | 分享内容 |
ACTION_CALL | android.intent.action.CALL | 直接拨号 |
ACTION_DIAL | android.intent.action.DIAL | 准备拨号 |
ACTION_SENDTO | android.intent.action.SENDTO | 发送短信 |
ACTION_ANSWER | android.intent.action.ANSWER | 接听电话 |
实现拨号功能
java
// 找到布局中的按钮,并设置点击事件监听器
findViewById(R.id.nextButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 在按钮点击事件中执行以下操作
// 创建一个新的 Intent 对象,用于实现拨号功能
Intent intent = new Intent();
// 设置 Intent 的动作为拨号操作
intent.setAction(Intent.ACTION_DIAL);
// 创建一个 Uri 对象,指定电话号码
Uri uri = Uri.parse("tel:" + 12345);
// 将 Uri 对象设置为 Intent 的数据,表示拨打指定的电话号码
intent.setData(uri);
// 启动 Intent,系统将打开拨号应用并填充指定的电话号码
startActivity(intent);
}
});
Activity 之间传递参数
Intent 对象通过putExtra
方法可以携带各种类型的参数信息
注意:启动表示会影响数据传递
A 页面
java
// 创建一个新的 Intent 对象,从当前活动 MainActivity 启动到 CalulatotActiviti
Intent intent = new Intent(MainActivity.this, CalulatotActiviti.class);
// 将键值对数据添加到 Intent 中,键为 "key",值为 "value"
intent.putExtra("key", "value");
// 启动目标活动 CalulatotActiviti
startActivity(intent);
B 页面
java
// 获取当前活动收到的 Intent
Intent intent = getIntent();
// 从 Intent 中获取字符串类型的数据,键名为 "key"
String key = intent.getStringExtra("key");
// 打印获取到的数据,如果数据不为空,输出数据;否则输出 "空的"
Log.e("222", key != null ? key : "空的");
类型数据读写
数据类型 | 读方法 | 写方法 |
---|---|---|
整型数 | getInt | putInt |
浮点数 | getFloat | putFloat |
双精度数 | getDouble | putDouble |
布尔值 | getBoolean | putBoolean |
字符串 | getString | putString |
字符串数组 | getStringArray | putStringArray |
字符串列表 | getStringArrayList | putStringArrayList |
可序列化结构 | getSerializable | putSerializable |
使用 Bundle 传递参数
A 页面
java
// 创建日期格式化对象,指定格式为 "yyyy-MM-dd HH:mm:ss"
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 获取当前日期和时间,并使用格式化对象将其格式化为字符串
String formattedDate = dateFormat.format(new Date());
// 创建一个 Bundle 对象,用于存放键值对数据
Bundle bundle = new Bundle();
// 将格式化后的日期字符串添加到 Bundle 中,键为 "key"
bundle.putString("key", formattedDate);
// 创建一个 Intent 对象
Intent intent = new Intent();
// 将 Bundle 对象放入 Intent 中
intent.putExtras(bundle);
// 启动目标活动
startActivity(intent);
B 页面
java
// 获取当前活动收到的 Intent
Intent intent = getIntent();
// 从 Intent 中获取携带的 Bundle 对象
Bundle extras = intent.getExtras();
// 从 Bundle 中获取字符串类型的数据,键名为 "key"
String key = extras.getString("key");
A页面 接收 B 页面的数据
java
// 创建一个 ActivityResultLauncher 对象,用于启动新的 Activity 并处理返回的结果
ActivityResultLauncher<Intent> resultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
// 在这里处理从 CalulatotActiviti 返回的结果
// 从返回的 ActivityResult 中获取返回的 Intent
Intent data = result.getData();
// 通过 Intent 获取附加的 Bundle 数据
Bundle extras = data.getExtras();
// 从 Bundle 中获取具体的字符串数据
String data1 = extras.getString("data");
// 打印获取到的数据
Log.i("222", data1);
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 创建意图对象
Intent intent = new Intent(MainActivity.this, CalulatotActiviti.class);
// 创建包裹
Bundle bundle = new Bundle();
// 创建日期格式化对象,指定格式
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
bundle.putString("key", dateFormat.format(new Date()));
intent.putExtras(bundle);
// 打开新页面,并传递数据
resultLauncher.launch(intent);
}
});
B 页面返回数据
java
// 点击事件
findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建意图
Intent data = new Intent();
// 创建包裹
Bundle bundle = new Bundle();
bundle.putString("data", "数据传递成功!");
data.putExtras(bundle);
// 返回数据
setResult(Activity.RESULT_OK, data);
// 关闭页面
finish();
}
});
获取相册中的图片并展示
java
// 图片标签
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取 ImageView 引用
imageView = findViewById(R.id.image_tu);
// 创建一个 ActivityResultLauncher 对象,用于启动新的 Activity 并处理返回的结果
ActivityResultLauncher<Intent> resultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result != null) {
// 获取返回的 Intent
Intent data = result.getData();
// 从 Intent 中获取选中图片的 Uri
Uri selectedImageUri = data.getData();
Log.i("3333", selectedImageUri.toString());
// 将选中图片的 Uri 设置到 ImageView 中显示
imageView.setImageURI(selectedImageUri);
}
}
});
// 设置按钮点击事件,启动图片选择操作
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 创建意图对象,用于打开图片选择器
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
// 启动图片选择器,并等待返回结果
resultLauncher.launch(intent);
}
});
}
调用相机
java
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.image_tu);
// 获取相机返回值
ActivityResultLauncher<Intent> resultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
// 处理相机返回的结果
Intent data = result.getData();
if (result.getResultCode() == Activity.RESULT_OK && data != null) {
// 从 Intent 中获取缩略图数据
Bundle extras = data.getExtras();
if (extras != null) {
Bitmap thumbnail = (Bitmap) extras.get("data");
imageView.setImageBitmap(thumbnail);
} else {
// 处理无法获取缩略图的情况
// 可以根据之前设置的保存路径加载完整图片
// Uri photoUri = getPhotoUri();
// imageView.setImageURI(photoUri);
}
} else {
// 拍照取消或失败
// 可以根据需要添加相应的逻辑
}
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 启动相机
resultLauncher.launch(intent);
}
});
}
广播组件---Broadcast、服务组件---Service,以及如何在组件之间传递消息数据
Broadcast 用于 Android 组件之间的灵活通信,与Activity的区别在于:
- 活动只能一对一通信,广播可以一对多,一人发送广播,多人接收处理
- 对于发送者来说,广播不需要考虑接收者有没有在工作,接收方在工作就接收广播,不在工作就丢弃广播。
- 对于接收者来说,因为可能会收到各式各样的广播,所以接收方要自行过滤符合条件的广播,才能进行解包处理。
方法:
- sendBroadcast:发送广播。
- registerReceiver:注册接收器,一般在onStart或onResume方法中注册。
- unregisterReceiver:注销接收器,一般在onStop或onPause方法中注销。
广播的收发步骤分为三步:
- 发送标准广播
- 定义广播接收器
- 开关广播接收器(即使关闭,避免内存泄漏)
标准广播
异步性: 标准广播是异步进行的,发送广播的组件不需要等待广播接收器处理完毕。
效率较高: 由于是异步的,发送标准广播不会阻塞发送者的执行,因此效率较高。但也因为是异步的,不能保证接收者按照注册的先后顺序接收广播。
不可终止: 发送标准广播后,无法中止或修改广播的传递。这也意味着无法在广播传递过程中终止其他接收器的执行。
不可取消: 一旦广播发送出去,就无法取消。
适用场景: 标准广播适用于不需要同步和有序执行的场景,例如通知其他应用程序某个事件已经发生。
java
public class MainActivity extends AppCompatActivity {
private TextView textView;
// 默认显示的广播信息
private String mDesc = "这里查看广播信息";
// 标准广播接收器
private StandardReceiver standardReceiver;
// 指定广播动作
private final static String STANDARD_ACTION = "com.itcast";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 TextView
textView = findViewById(R.id.v_count);
// 设置按钮点击事件
findViewById(R.id.btn_guangbo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建指定动作的意图
Intent intent = new Intent(STANDARD_ACTION);
// 发送广播
sendBroadcast(intent);
}
});
}
@Override
protected void onStart() {
super.onStart();
// 在 Activity 启动时注册广播接收器
standardReceiver = new StandardReceiver();
IntentFilter intentFilter = new IntentFilter(STANDARD_ACTION);
registerReceiver(standardReceiver, intentFilter);
}
@Override
protected void onStop() {
super.onStop();
// 在 Activity 停止时注销广播接收器,以避免内存泄漏
unregisterReceiver(standardReceiver);
}
// 广播接收器类
private class StandardReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction() != null && intent.getAction().equals(STANDARD_ACTION)) {
// 接收到广播时更新 TextView 的文本
mDesc = "收到一条标准的广播:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
textView.setText(mDesc);
}
}
}
}
有序广播
一个广播存在多个接收器,这些接收器需要排队收听广播,这意味着该广播是条有序广播。
先收到广播的接收器A,既可让其他接收器继续收听广播,也可中断广播不让其他接收器收听。
设置优先级,让接收器比其他接收器前面收到信息
java
private TextView textView;
private String mDesc = "这里查看广播信息\n";
// 有序广播接收器A
private orderAReceiver orderAReceiver;
// 有序广播接收器B
private orderBReceiver orderBReceiver;
// 指定广播接头对号
private final static String ORDER_ACTION = "有序广播";
// 选中框
private CheckBox ck_abort;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 赋值组件
textView = findViewById(R.id.v_count);
ck_abort = findViewById(R.id.checkFail);
findViewById(R.id.btn_guangbo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建指定动作的意图
Intent intent = new Intent(ORDER_ACTION);
// 发送有序广播
sendOrderedBroadcast(intent, null);
}
});
}
/**
* 给接收器设置优先级,让 B的优先级更高, 即使 A先注册
* B 也会在 A前面收到信息
* 配置接收指定信息
*/
@Override
protected void onStart() {
super.onStart();
orderAReceiver = new orderAReceiver();
IntentFilter intentFilterA = new IntentFilter(ORDER_ACTION);
// 设置 接收器 A 的优先级 8
intentFilterA.setPriority(8);
registerReceiver(orderAReceiver, intentFilterA);
orderBReceiver = new orderBReceiver();
IntentFilter intentFilterB = new IntentFilter(ORDER_ACTION);
// 设置 接收器 B 的优先级 10
intentFilterB.setPriority(10);
registerReceiver(orderBReceiver, intentFilterB);
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(orderAReceiver);
unregisterReceiver(orderBReceiver);
}
/**
* 定义有序广播接收器
* 也要继承 BroadcastReceiver
* 允许中断广播,后面的接收器将不在接收
*/
// 实例 A
private class orderAReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction().equals(ORDER_ACTION)) {
mDesc += "\n接收器A 收到一条有序的广播:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
textView.setText(mDesc);
if (ck_abort.isChecked()) {
// 中断广播
abortBroadcast();
}
}
}
}
// 实例 B
private class orderBReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction().equals(ORDER_ACTION)) {
mDesc += "\n接收器B 收到一条有序的广播:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
textView.setText(mDesc);
if (ck_abort.isChecked()) {
// 中断广播
abortBroadcast();
}
}
}
}
静态广播注册(震动)
创建
java
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
throw new UnsupportedOperationException("Not yet implemented");
}
}
同时 AndroidManifest.xml 自动添加接收器的节点配置
xml
<receiver
android:name="receiver.MyReceiver"
android:enabled="true"
android:exported="true">
</receiver>
在自动接收器中,设置功能,比如震动提醒
由于震动功能需要获取权限,需要在 AndroidManifest.xml 中添加
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 声明应用程序需要的权限 -->
<!-- 请求声音权限 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 请求震动权限 -->
<uses-permission android:name="android.permission.VIBRATE" />
java
public class MyReceiver extends BroadcastReceiver {
// 指定广播接头对号
private final static String ORDER_ACTION = "震动广播";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(ORDER_ACTION)) {
// 从系统服务中获取震动管理器
Vibrator vibrator = context.getSystemService(Vibrator.class);
/**
* createOneShot 一次性震动
* 5000 震动一秒
* VibrationEffect.DEFAULT_AMPLITUDE 默认震动
*/
VibrationEffect vibrationEffect = VibrationEffect.createOneShot(1000, VibrationEffect.DEFAULT_AMPLITUDE);
// 使用创建的震动效果进行震动
vibrator.vibrate(vibrationEffect);
}
}
}
业务实现
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_guangbo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建指定动作的意图
Intent intent = new Intent(MyReceiver.STATIC_ACTION);
// 发送静态广播,需要通过 setComponent 方法指定接收器的接收器类
ComponentName componentName = new ComponentName(MainActivity.this, MyReceiver.class);
intent.setComponent(componentName);
// 发送静态广播
sendBroadcast(intent);
}
});
}
定时器
设置定时器
java
// 获取闹钟管理器
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
// 设置一次性定时器,参数一:类型 参数二:执行时机(毫秒) 参数三:延迟意图 PendingIntent,即要执行的操作
alarmManager.set(AlarmManager.RTC_WAKEUP, 1000, pendingIntent);
设置一次性定时器,不同的是,即使设备保持空闲状态,也会执行定时器。
java
// 获取闹钟管理器
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setAndAllowWhileIdle();
重复定时器
设置重复定时器,参数一:类型,参数二:首次执行时间(毫秒),参数三,下次执行的间隔时间(毫秒)
参数四:延迟的意图
java
// 获取闹钟管理器
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating();
取消定时器
java
// 获取闹钟管理器
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
// 取消,参数意图
alarmManager.cancel();
PendingIntent
- 代表延迟的意图,指向的组件不会马上激活
- 是一类消息的组合,包含目标的 Intent 对象、请求代码和请求方法等信息
定时器广播接收器
java
public class MainActivity extends AppCompatActivity {
// 声明一个闹钟广播事件的标识串
private String ALARM_ACTION = "com.example.chapter04.alarm";
// 描述信息
private String mDesc = "";
private TextView viewById;
// 广播接收器
private AlarmReceiver alarmReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewById = findViewById(R.id.text);
// 发送广播
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建一个意图,用于触发闹钟广播
Intent intent = new Intent(ALARM_ACTION);
// 创建一个与用于广播延迟意图
// FLAG_IMMUTABLE 表示这个意图,不可改变
PendingIntent broadcast = PendingIntent.getBroadcast(MainActivity.this,
0, intent, PendingIntent.FLAG_IMMUTABLE);
// 从系统服务中获取闹钟管理器
AlarmManager alarmManager = getSystemService(AlarmManager.class);
long delayTime = System.currentTimeMillis() + 5 * 1000;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 允许在空闲时发送广播
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, delayTime, broadcast);
} else {
// 设置一次性闹钟,延迟发送闹钟广播
alarmManager.set(AlarmManager.RTC_WAKEUP, delayTime, broadcast);
}
}
});
}
@Override
protected void onStart() {
super.onStart();
// 创建并注册闹钟广播接收器
alarmReceiver = new AlarmReceiver();
// 创建意图过滤器,指定事件来源
IntentFilter filter = new IntentFilter(ALARM_ACTION);
// 注册接收器
registerReceiver(alarmReceiver, filter);
}
@Override
protected void onStop() {
super.onStop();
// 注销接收器
unregisterReceiver(alarmReceiver);
}
// 定义一个闹钟广播接收器
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
// 更新描述信息
mDesc = "闹钟时间到达!" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
Locale.getDefault()).format(new Date());
// 在界面上显示描述信息
viewById.setText(mDesc);
// 获取震动管理器
Vibrator vibrator = context.getSystemService(Vibrator.class);
// 创建一次性震动效果
VibrationEffect oneShot = VibrationEffect.createOneShot(1000, VibrationEffect.DEFAULT_AMPLITUDE);
// 执行震动
vibrator.vibrate(oneShot);
}
}
}
}
重复执行广播,在广播接收器的 onReceive 方法中,再调用点击事件触发的函数,即可