Android 小组件 AppWidgetProvider

news2024/12/23 3:36:34

一、相关文档
二、小组件是什么?
三、AppWidget 核心类 AppWidgetProvider 源码解读和原理分析
     1、先看 AppWidgetProvider 源码
     2、AppWidgetProvider 回调方法分析
          onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
          onReceive(Context context, Intent intent)
     3、开发自己的小组件时,写 AppWidgetProvider 实现类,有没有必要重写     onReceive(Context context, Intent intent) 方法?
四、开发自己的小组件时,必须的配合和常用方法
     1、编写实现类,继承 AppWidgetProvider
     2、定义基本配置文件
     3、在清单文件中注册 AppWidgetProvider
     4、创建布局文件
     5、更复杂的配置,创建配置 Activity,创建集合布局等,暂不介绍了,用得少。

一、相关文档

如何开发自己的 AppWidget 小组件程序:构建应用微件

如何开发「托管 AppWidget 小组件」的宿主应用(类似于手机桌面 Launcher 应用):构建应用微件托管应用

如何设置 AppWidget 小组件的最小尺寸:应用微件设计指南

二、小组件是什么?

就是可以添加到手机桌面的窗口小程序。比如热榜卡片:

三、AppWidget 核心类 AppWidgetProvider 源码解读和原理分析

1、先看 AppWidgetProvider 源码

package android.appwidget;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class AppWidgetProvider extends BroadcastReceiver {
     
    public AppWidgetProvider() {

    }

    public void onReceive(Context context, Intent intent) {
        // Protect against rogue update broadcasts (not really a security issue,
        // just filter bad broacasts out so subclasses are less likely to crash).
        String action = intent.getAction();
        if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if (appWidgetIds != null && appWidgetIds.length > 0) {
                    this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                }
            }
        } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
                final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                this.onDeleted(context, new int[] { appWidgetId });
            }
        } else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
                    && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
                int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                Bundle widgetExtras = extras.getBundle(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS);
                this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
                        appWidgetId, widgetExtras);
            }
        } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
            this.onEnabled(context);
        } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
            this.onDisabled(context);
        } else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
                int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if (oldIds != null && oldIds.length > 0) {
                    this.onRestored(context, oldIds, newIds);
                    this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
                }
            }
        }
    }

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // updatePeriodMillis 手机系统定期更新时会调用此方法
        // 当用户添加应用微件时也(可能)会调用此方法,所以它应执行基本设置,如定义视图的事件处理脚本以及根据需要启动临时的 Service
    }

    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
            int appWidgetId, Bundle newOptions) {
        // 当首次放置微件时以及每当调整微件的大小时,会调用此方法。您可以使用此回调来根据微件的大小范围显示或隐藏内容。您可以通过调用 getAppWidgetOptions() 来获取大小范围,该方法会返回包含以下各项的 Bundle:
        // OPTION_APPWIDGET_MIN_WIDTH - 包含微件实例的当前宽度的下限(以 dp 为单位)。
        // OPTION_APPWIDGET_MIN_HEIGHT - 包含微件实例的当前高度的下限(以 dp 为单位)。
        // OPTION_APPWIDGET_MAX_WIDTH - 包含微件实例的当前宽度的上限(以 dp 为单位)。
        // OPTION_APPWIDGET_MAX_HEIGHT - 包含微件实例的当前高度的上限(以 dp 为单位)。
    }

    public void onDeleted(Context context, int[] appWidgetIds) {
        // 每次从应用微件托管应用中删除应用微件时,都会调用此方法。
    }

    public void onEnabled(Context context) {
        // 首次创建应用微件的实例时,会调用此方法。
        // 例如,如果用户添加应用微件的两个实例,只有首次添加时会调用此方法。
        // 如果您需要打开一个新的数据库或执行只需要对所有应用微件实例执行一次的其他设置,则此方法非常合适。
    }

    public void onDisabled(Context context) {
        // 从应用微件托管应用中删除了应用微件的最后一个实例时,会调用此方法。
        // 您应使用此方法来清理在 onEnabled(Context) 中完成的所有工作,如删除临时数据库。
    }

    public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
        // 如果你的 AppWidgetProvider 需要从 backup 中恢复时,需要重新此方法,做一些数据恢复/状态更新的操作。
    }

}

从源码了解到,AppWidgetProvider 是一个广播实现类,那么 AppWidgetProvider 在清单文件中注册是不是要使用 <receiver> 呢?是的,后面会讲到。

public class AppWidgetProvider extends BroadcastReceiver {...}

2、AppWidgetProvider 回调方法分析

其他的不介绍了,上面已经注释说明。重点说两个:

onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)

这个回调方法是 AppWidgetProvider 的核心方法

在小组件手机添加到桌面等宿主程序时(这个时机也可能不会回调的),以及 xml/... 配置中设定的 updatePeriodMillis 更新周期到达时,都会调用此方法。

你需要在此方法中:

→ 请求需要的数据;

→ 创建 RemoteViews 布局包装类,设置点击事件和更新数据;

→ 最后记得更新布局:appWidgetManager?.updateAppWidget(appWidgetIds, remoteViews)

onReceive(Context context, Intent intent)

这个回调方法原本是广播 BroadcastReceiver 的回调方法。

小组件 AppWidgetProvider 在此回调方法中实现了很多逻辑,小组件所有的事件都是系统发送的广播,然后在 onReceive() 方法中接收处理,判断 action 的类型,根据不同类型再回调 AppWidgetProvider 中的其他回调方法。

那 onReceive() 和其他回调方法的执行顺序是怎样的呢?拿 onUpdate() 来举例。

→ 先执行 onReceive(),在其中判断 action,发现 action 等于

AppWidgetManager.ACTION_APPWIDGET_UPDATE
AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE
AppWidgetManager.ACTION_APPWIDGET_RESTORED

三者之一

→ 然后再执行了 onUpdate()。

3、开发自己的小组件时,写 AppWidgetProvider 实现类,有没有必要重写 onReceive(Context context, Intent intent) 方法?

→ 如果只关注 AppWidgetProvider 的生命周期方法,那就没必要再重写 onReceive() 方法了;

→ 如果在 AppWidgetProvider 中,因为进程切换等原因,还需要把 AppWidgetProvider 当成广播来使用,那就要重写 onReceive() 方法了。

重写时要注意,不要把 super.onReceive(context, intent) 删除了:

override fun onReceive(context: Context?, intent: Intent?) {
    // 不要删除父类方法调用
    super.onReceive(context, intent)
    // 针对每个广播调用此方法,并且是在各个回调方法之前调用。
    // 您通常不需要实现此方法,因为默认的 AppWidgetProvider 实现会过滤所有应用微件广播并视情况调用回调方法。
 
    val action = intent?.action?:return
    if (action == xxx) {
        ...
    }
}

四、开发自己的小组件时,必须的配合和常用方法

1、编写实现类,继承 AppWidgetProvider

Demo 如:

class DemoAppWidgetProvider : AppWidgetProvider() {
 
    override fun onUpdate(
        context: Context?,
        appWidgetManager: AppWidgetManager?,
        appWidgetIds: IntArray?
    ) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)
 
        // 构建布局的 RemoteViews 对象
        // 定义布局文件 R.layout.widget_layout
        val remoteViews = RemoteViews(context?.packageName ?: "", R.layout.widget_layout)
         
        // 更新内容
        remoteViews.setTextViewText(R.id.tv_time, getTextV())
         
        // 更新小组件 UI
        appWidgetManager?.updateAppWidget(appWidgetIds, remoteViews)
    }
 
    private fun getTextV(): String {
        val time = System.currentTimeMillis()
        val minute = time / 1000 / 60 % 60
        val second = time / 1000 % 60
        return "${minute}:${second}"
    }
 
}

需要说明的是:

→ appWidgetManager: AppWidgetManager 参数可以通过 onUpdate() 方法的参数获取,也可以通过 AppWidgetManager.getInstance(context) 静态方法获取。

其实 onReceive() 中就是通过 AppWidgetManager.getInstance(context) 静态方法获取并传递给 onUpdate() 方法的。

所以没有必要再自己调用 AppWidgetManager.getInstance(context) 静态方法获取,接收 onUpdate() 的参数保存即可。

验证经过打印 Log 测试,确认 onUpdate() 方法参数中的 appWidgetManager: AppWidgetManager 对象,与 AppWidgetManager.getInstance(context) 静态方法获取的对象,二者内存地址一样。

→ appWidgetIds: IntArray? 参数可以通过 onUpdate() 方法的参数获取也可以通过 AppWidgetManager.getInstance(context).getAppWidgetIds(ComponentName(context!!, XXXAppWidgetProvider::class.java)) 静态方法获取。

其实 onUpdate() 方法中的 appWidgetIds: IntArray? 参数是通过 AppWidgetManager.EXTRA_APPWIDGET_IDS 常量获取的。

全局搜索 AppWidgetManager.EXTRA_APPWIDGET_IDS 使用的地方,能找到一处 com.android.systemui.people.widget.PeopleBackupHelper.updateWidgets(Context context),

其中的 widgetIds 是通过 AppWidgetManager.getInstance(context).getAppWidgetIds(ComponentName(context!!, XXXAppWidgetProvider::class.java)) 静态方法产生的,然后 put 到了 AppWidgetManager.EXTRA_APPWIDGET_IDS 常量中

验证经过打印 Log 测试,确认 onUpdate() 方法参数中的 appWidgetIds: IntArray? 参数,

与 AppWidgetManager.getInstance(context).getAppWidgetIds(ComponentName(context!!, XXXAppWidgetProvider::class.java)) 静态方法获取的数组,二者数组长度和内容一样。

2、定义基本配置文件

在 res/xml/ 文件夹中新建配置文件 demo_appwidget_info.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:updatePeriodMillis="3600000"
    android:previewImage="@drawable/preview"
    android:initialLayout="@layout/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>

具体参数说明请参考:https://developer.android.com/guide/topics/appwidgets?hl=zh-cn 添加 AppWidgetProviderInfo 元数据

要注意如何设置 minWidth 和 minHeight,参考:https://developer.android.com/guide/practices/ui_guidelines/widget_design?hl=zh-cn#anatomy_determining_size 确定微件的尺寸

3、在清单文件中注册 AppWidgetProvider

<receiver android:name="ExampleAppWidgetProvider" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/demo_appwidget_info" />
</receiver>

重点说明:

→ action 的名称 android.appwidget.action.APPWIDGET_UPDATE 必须添加,且名称一摸一样不能改变。否则在手机小组件添加界面会不显示你的小组件了。

→ meta-data 节点必须添加,其名称 android.appwidget.provider 不能改变。除非某厂商要求定制化。

→ meta-data 节点的资源引用(值)为你上面新建的配置文件 demo_appwidget_info.xml。

→ 如果有广播用途,也可以在 intent-filter 中添加自己的 action。

4、创建布局文件

比如上例中的 R.layout.widget_layout

要注意小组件支持的 View:https://developer.android.com/guide/topics/appwidgets?hl=zh-cn 创建应用微件布局

5、更复杂的配置,创建配置 Activity,创建集合布局等,暂不介绍了,用得少。

感兴趣的话,请参考文档 https://developer.android.com/guide/topics/appwidgets?hl=zh-cn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1039304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

索引与切片

索引 生成一个CNN的输入格式张量 a torch.rand(4, 3, 28, 28) &#xff08;batch, channel&#xff0c;high, width)单个索引的方式与python一样 a[0] # 去索引为0的图片即第一张图片 a[0].shape多个索引与python稍微不一样&#xff0c;但很相似 python&#xff1a;a[0][0…

PMP证书在国内已经泛滥了,还有含金量吗?

没有泛滥吧&#xff1f;这个证书现在就是趋向于项目管理人士要去考的呀&#xff0c;也不是考了没用&#xff0c;提升自身个人的能力、找工作方面和晋升加薪方面确实有用呀&#xff0c;不然报名费那么贵&#xff0c;为什么越来越多人考呢&#xff1f; 1、提升自身个人的能力 首…

云原生技术盛会KubeCon即将召开!亚马逊云科技作为钻石赞助商参会

KubeCon2023将于9月26-28日在上海跨国采购会展中心隆重召开。作为云原生领域最负盛名的技术大会之一&#xff0c;KubeConCloudNativeCon是连接全球开发者与云原生社区的最佳平台&#xff0c;此次还新增Open Source Summit环节&#xff0c;吸引了全球顶尖的云原生专家们汇聚其中…

利用C++开发一个迷你的英文单词录入和测试小程序-增强功能

小玩具基本完成之后&#xff0c;在日常工作中&#xff0c;记录一些单词&#xff0c;然后定时再复习下&#xff0c;还真的有那么一点点用&#xff08;毕竟自己做的小玩具&#xff09;。 在使用过程中&#xff0c;遇到不认识的单词&#xff0c;总去翻译软件翻译&#xff0c;然后…

结合Mockjs与Bus事件总线搭建首页导航和左侧菜单

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…

可在电脑桌面显示每日工作计划表的工具用哪个?

当你坐在电脑前&#xff0c;繁忙的工作日开始了。屏幕前的任务排成一列&#xff0c;待处理的事情如潮水般涌来。这时&#xff0c;你需要一款强大的工具来帮助你合理安排时间&#xff0c;确保没有一个任务被忘记。 在这个数字时代&#xff0c;有一款工具可以在你的电脑桌面上显…

关于建筑八大员(住建厅七大员)考试难不难?合格技巧

关于建筑八大员&#xff08;住建厅七大员&#xff09;考试难不难&#xff1f;合格技巧 建筑八大员分为施工员、质量员、资料员、材料员、机械员、劳务员、标准员等7种大类。电脑考核&#xff0c;系统好几万题目&#xff0c;随机抽取一套匹配给你&#xff0c;每个人考试题目都不…

数组01-二分查找算法

目录 数组如何实现随机访问 两个关键词 数组的特点 根据下标随机访问数组元素 为什么数组要从0开始编号&#xff0c;而不是从1开始 LeetCode之路——704. 二分查找 Code 二分查找算法 数组如何实现随机访问 数组&#xff08;Array&#xff09;是一种线性表数据结构。它…

【从0学习Solidity】51. ERC4626 代币化金库标准

【从0学习Solidity】51. ERC4626 代币化金库标准 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#…

https跳过SSL认证时是不是就是不加密的,相当于http?

https跳过SSL认证时是不是就是不加密的,相当于http?&#xff0c;其实不是&#xff0c;HTTPS跳过SSL认证并不相当于HTTP&#xff0c;也不意味着没有加密。请注意以下几点&#xff1a; HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;本质上是在HTTP的基础上…

在docker中删除none镜像

在构建过Docker镜像的电脑上查看本地镜像列表&#xff0c;有可能看到下图红框中的镜像&#xff0c;在列表中展示为:&#xff1a; 这种镜像在Docker官方文档中被称作dangling images&#xff0c;指的是没有标签并且没有被容器使用的镜像。 官方解释 来自官方的解释如下图红框所…

【多线程初阶】多线程案例之阻塞式队列

文章目录 前言1. 什么是阻塞队列2. 生产者消费者模型2.1 生产者消费者模型的优势2.1.1 解耦合2.1.2 削峰填谷 3. Java 标准库中的阻塞队列3.1 生产者消费者模型 4. 自己实现一个阻塞队列总结 前言 本文主要给大家讲解多线程的一个重要案例 — 阻塞式队列. 关注收藏, 开始学习…

如何在Windows11上使用macOS Sonoma全新的慢镜屏幕保护程序

前言 macOS Sonoma是Apple macOS一个大版本的描述&#xff0c;以任何方式使用macOS Sonoma都应确保符合Apple的规定 本文假定你在搭载Intel处理器的Apple产品上通过bootcamp安装了Windows11&#xff0c;且想要让Windows11产生类似于macOS Sonoma全新的慢镜屏幕保护程序的相关…

java面试题-jvm基础知识

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&a…

【记录文】Android自定义Dialog实现圆角对话框

圆角的dialog还是蛮常用的&#xff0c;demo中正好用上了 自定义Dialog&#xff0c;代码中可以设置指定大小与位置 /*** author : jiangxue* date : 2023/9/25 13:21* description :圆角的矩形*/internal class RoundCornerView(context: Context,view: Int, StyleRes theme…

智能网联驾驶测试与评价工业和信息化部重点实验室“车载智能计算基础平台参考架构2.0专家研讨会”圆满结束

近日&#xff0c;智能网联驾驶测试与评价工业和信息化部重点实验室在北京市召开“车载智能计算基础平台参考架构2.0专家研讨会”&#xff0c;本次会议由智能网联驾驶测试与评价工业和信息化部重点实验室、中国软件评测中心&#xff08;工业和信息化部软件与集成电路促进中心&am…

美颜SDK哪家好?2023美颜SDK有哪些新玩法?

在当今的数字世界中&#xff0c;美颜SDK正成为一种强大的工具&#xff0c;可以帮助我们在视频直播和短视频中展现出最美的自己。美摄科技作为一家专注于美颜SDK技术的公司&#xff0c;提供了多种领先的美颜SDK相关产品&#xff0c;以满足不同用户的需求。 美摄科技的美颜SDK是一…

第一章 计算机网络与协议

文章目录 一、计算机网络的基础概念二、计算机网络分类2.1 通信子网/资源子网/网络协议2.2 网络拓补图分类2.3 按照覆盖范围分类2.4 按照交换技术分类2.5 其他分类 三、OSI参考模型3.1 应用层3.2 表示层3.3 会话层3.4 传输层3.5 网络层3.6 数据链路层3.7 物理层3.8 借助OSI模型…

众佰诚:现在开一家抖音小店还来得及吗

随着互联网的迅猛发展&#xff0c;电商行业也进入了一个全新的时代&#xff0c;其中抖音小店作为新兴的销售平台备受瞩目。然而&#xff0c;对于那些考虑开设抖音小店的人来说&#xff0c;一个重要的问题是&#xff1a;现在开一家抖音小店还来得及吗? 答案是肯定的&#xff0c…

Vulnhub-DC-8 靶机复现完整过程

Vulnhub-DC-8 靶机复现完整过程 一、环境搭建 kali的IP地址&#xff1a;192.168.200.14 DC-8的IP地址&#xff1a;192.168.200.13&#xff08;一个flag&#xff09; 靶机和攻击机处于同一个网络方式&#xff1a;nat或桥接 若出现开机错误&#xff0c;适当将dc的兼容版本改低…