Android 学习笔记(一)

创建 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 工程编译配置文件

  1. build.gradle:该文件分为项目级别与模块级别,用于描述 app 工程编译规则
  2. proguard-rules.pro:该文件描述java代码的混淆规则
  3. gradle.properties 该文件用于配置编译工程的命令行参数,一般无需改动
  4. settings.gradle:配置需要编译哪些模块,初始为 include app 表示编译app模块
  5. 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

  1. 输入文件名称,下面是默认的跟标签
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"/>
  1. 创建 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);
    }
}
  1. 在 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属性
    1. layout_margin
    2. layout_marginLeft
    3. layout_marginTop
    4. layout_marginRight
    5. layout_marginBottom
  • 采用padding属性
    1. padding
    2. paddingLeft
    3. paddingTop
    4. paddingRight
    5. 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 水平方向的滚动视图

注意事项:

  1. 垂直方向滚动时,layout_width 属性值设置为 match_paren,layout_height属性值设置为wrap_content。

  2. 水平方向滚动时,layout_width 属性值设置为 wrap_content,layout_height 属性值设置为match_parent

  3. 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:重用已有的活动实例。

打开新页面的方法调用顺序为

graph LR onCreate --> onStart --> onResume

关闭旧页面的方法调用顺序为onPause→onStop→onDestroy

graph LR 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方法中注销。

广播的收发步骤分为三步:

  1. 发送标准广播
  2. 定义广播接收器
  3. 开关广播接收器(即使关闭,避免内存泄漏)

标准广播

异步性: 标准广播是异步进行的,发送广播的组件不需要等待广播接收器处理完毕。

效率较高: 由于是异步的,发送标准广播不会阻塞发送者的执行,因此效率较高。但也因为是异步的,不能保证接收者按照注册的先后顺序接收广播。

不可终止: 发送标准广播后,无法中止或修改广播的传递。这也意味着无法在广播传递过程中终止其他接收器的执行。

不可取消: 一旦广播发送出去,就无法取消。

适用场景: 标准广播适用于不需要同步和有序执行的场景,例如通知其他应用程序某个事件已经发生。

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 方法中,再调用点击事件触发的函数,即可

相关推荐
夜月行者1 小时前
如何使用ssm实现基于SSM的宠物服务平台的设计与实现+vue
java·后端·ssm
程序猿小D1 小时前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
Yvemil71 小时前
RabbitMQ 入门到精通指南
开发语言·后端·ruby
sdg_advance1 小时前
Spring Cloud之OpenFeign的具体实践
后端·spring cloud·openfeign
奔跑吧邓邓子1 小时前
npm包管理深度探索:从基础到进阶全面教程!
前端·npm·node.js
前端李易安2 小时前
ajax的原理,使用场景以及如何实现
前端·ajax·okhttp
猿java2 小时前
使用 Kafka面临的挑战
java·后端·kafka
碳苯2 小时前
【rCore OS 开源操作系统】Rust 枚举与模式匹配
开发语言·人工智能·后端·rust·操作系统·os
kylinxjd2 小时前
spring boot发送邮件
java·spring boot·后端·发送email邮件
汪子熙2 小时前
Angular 服务器端应用 ng-state tag 的作用介绍
前端·javascript·angular.js