Android 桌面小组件 AppWidgetProvider

article/2025/10/7 5:17:18

废话

桌面小组件,绝对是小程序中的小程序,说白了就是任何复杂一丁点的操作都不适合做成桌面小组件。

所以这里采用的演示的例子,就只有一个白色圆角背景,外加一个文本框,显示文字。

小组件的教程网上一搜一大堆,所以我这里主要就是介绍一些坑的地方,跟大致处理流程,具体细节还得看其他大神的骚操作。

预览图

注意事项

1、UI 适配

小组件的宽高是可以支持用户自行调整的,只需简单的设置最低宽高,但是可调整的最小粒度是根据手机的 icon 为标准,这样就会导致一个比较难处理的点。

如果手机是 4x 布局的,即一行可以显示 4 个 APP 图标,那调节的粒度就是 90dp(理想情况下),实际情况的话,还得考虑小组件的固定边距,这个边距,不同牌子的手机可能还不一样。

如果手机是 5x 布局的,即一行可以显示 5 个 APP 图标……

解决方案:小组件数量无限制,用户也是用就加不用就不加,所以解决方案就简单粗暴一点,你能想到的适配尺寸,每种尺寸搞一个,用户自己选择合适的尺寸就好。大、中、小、大中、中小、微小、超大等乱七八糟的,全部一股脑上。

2、更新时间

更新时间为主动更新和定时更新;

主动更新:即在 APP 中可以动态更新这个桌面小组件,这种情况更新没有时间限制。

定时更新:小组件需要展示的数据可能已经发生了变化,但是 APP 已经被系统杀死了,无法主动更新数据,就会导致小组件展示的数据可能是已过期的或者是旧的,这时候就可以用到小组件的定时更新功能,但是这个定时更新有一个限制,基于省电逻辑,最快的更新周期为 30 分钟。(如果是再 onUpdate 方法中写个定时器定时更新,抱歉,不行,会被系统杀死,杀死之后小组件不会消失,而是一直显示最后一次更新时候的状态,直到下一次更新数据,类似于电子水墨屏的逻辑。)

3、点击事件

我这里图省事,只用了最简单的,点击整个小组件直接调起 APP,所以其他复杂一点的点击事件的处理方法我就不懂了。

点击跳转页面需要用到 PendingIntent,这玩意的 Flag 有很多种模式,具体可以查看文章底部的参考文档,坑就坑在这个 Flag,31 之后的系统有改动,会报错,所以 31 的系统需要用 PendingIntent.FLAG_IMMUTABLE,具体看代码。

4、调起 APP

通过 PendingIntent 就可以直接调起 APP 的相关页面,不过这里也有坑,假设你 APP 的启动页面是 MainActivity 页面,点击小组件你就让它跳转到 MainActivity 页面走正常的 APP 启动流程,就等同于是点击小组件就能启动 APP,哪怕 APP 被杀死了,也不影响启动(听着好像没毛病)。

坑就坑在于,通过这种方式打开的 APP,他…… 他不走 Application 类,也就是你如果是在 Application 中初始化了某些东西,但是 APP 已经被系统杀死了,这时候你再点击小组件启动 APP,就会发现,好多组件用不了(没初始化)。

我这里图省事的做法就是把 Application 的所有需要初始化的东西都放 MainActivity 里面初始化了(但是 Content 还是用的 Application,而不是用 MainActivity)。

开搞

需求

一个小组件,居中显示一个文本,点击可进入 APP

1、准备一个布局文件 widget_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/lly_bg"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/bg_test"android:orientation="vertical"><TextViewandroid:id="@+id/tv_test"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:text="测试" /></LinearLayout>

附上背景文件 bg_test.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><!--    背景色--><solid android:color="#ffffff" /><!--    圆角--><corners android:radius="20dp" />
</shape>

2、res 文件夹下新建一个 xml 文件夹,新建 app_widget_test.xml 配置文件

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"android:minWidth="360dp"android:minHeight="120dp"android:updatePeriodMillis="1800000"android:previewImage="@drawable/ic_widget_big"android:initialLayout="@layout/widget_test"android:resizeMode="horizontal|vertical"android:widgetCategory="home_screen">
</appwidget-provider>

minWidth、minHeight    最小宽高

updatePeriodMillis    更新周期

previewImage    添加桌面小组件时候显示的预览图

initialLayout    布局

widgetCategory    home_screen 是代表的桌面小组件,其他参数自行百度了

3、合适的地方新建一个 TestAppWidget 类,继承 AppWidgetProvider


/*** 桌面小组件** @author Admin*/
public class TestAppWidget extends AppWidgetProvider {/*** 每次窗口小部件被更新都调用一次该方法(创建、时间到更新周期都会调起这里)*/@Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {super.onUpdate(context, appWidgetManager, appWidgetIds);//更新数据updateWidgetView(context, UUID.randomUUID().toString());}/*** 接收窗口小部件点击时发送的广播*/@Overridepublic void onReceive(Context context, Intent intent) {super.onReceive(context, intent);}/*** 每删除一次窗口小部件就调用一次*/@Overridepublic void onDeleted(Context context, int[] appWidgetIds) {super.onDeleted(context, appWidgetIds);}/*** 当最后一个该窗口小部件删除时调用该方法*/@Overridepublic void onDisabled(Context context) {super.onDisabled(context);}/*** 当该窗口小部件第一次添加到桌面时调用该方法*/@Overridepublic void onEnabled(Context context) {super.onEnabled(context);}/*** 当小部件大小改变时*/@Overridepublic void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);}/*** 当小部件从备份恢复时调用该方法*/@Overridepublic void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {super.onRestored(context, oldWidgetIds, newWidgetIds);ALog.e("当小部件从备份恢复时调用该方法");}/*** 更新桌面小组件数据用,APP中也可以在任意地方传入任意数据进来主动更新小组件数据*/public static void updateWidgetView(Context context, String str) {//初始化RemoteViewsComponentName componentName = new ComponentName(context, TestAppWidget.class);RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_test);//点击事件,点击跳转到MainActivity页面Intent startActivityIntent = new Intent(context, MainActivity.class);PendingIntent processInfoIntent;if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {//31,Android11以上系统processInfoIntent = PendingIntent.getActivity(context, 0, startActivityIntent, PendingIntent.FLAG_IMMUTABLE);} else {processInfoIntent = PendingIntent.getActivity(context, 0, startActivityIntent, PendingIntent.FLAG_ONE_SHOT);}remoteViews.setOnClickPendingIntent(R.id.lly_bg, processInfoIntent);//更新文本数据remoteViews.setTextViewText(R.id.tv_test, str);//开始更新视图AppWidgetManager awm = AppWidgetManager.getInstance(context);awm.updateAppWidget(componentName, remoteViews);}}

4、AndroidManifest.xml 中配置小组件,与 Activity 页面同级

        <receiverandroid:name=".TestAppWidget"android:exported="false"><intent-filter><action android:name="android.appwidget.action.APPWIDGET_UPDATE" /></intent-filter><meta-dataandroid:name="android.appwidget.provider"android:resource="@xml/app_widget_test" /></receiver>

参考文章

https://blog.csdn.net/weixin_43499030/article/details/90264915

https://blog.csdn.net/weixin_43499030/article/details/90264915


http://chatgpt.dhexx.cn/article/8wqn4xfM.shtml

相关文章

Android 约束布局 ConstrainLayout min max width

写一个自定义view package com.anguomob.guidelineimport android.content.Context import android.graphics.Canvas import android.graphics.Color import android.util.AttributeSet import android.view.Viewclass ZeroView constructor(context: Context?, attrs: Attri…

ConstrainLayout 基础教程2,近期想跳槽的程序员必看

特性详解 Visibility behavior (可见性的表现) ConstraintLayout对可见性被标记View.GONE的控件(后称“GONE控件”)有特殊的处理。一般情况下,GONG控件是不可见的,且不再是布局的一部分,但是在布局计算上,ConstraintLayout与传统布局有一个很重要的区别: 传统布局下,…

UGUI源码解析——LayoutElement

一&#xff1a;前言 继承了ILayoutElement和ILayoutIgnorer接口&#xff0c;作为布局元素组件 挂载了Layout Element组件的对象&#xff0c;布局并不会生效&#xff0c;它是受到实现了布局组的控制(HorizontalLayoutGroup、VerticalLayoutGroup、GridLayoutGroup) 二&#xff…

Layui框架的使用技巧

1.选中html代码块&#xff0c;ctrlshift减号- 就会全部折叠 2.加入Thymeleaf模板需要添加命名空间 <!DOCTYPE html> <html xmlns:th"http://www.thymeleaf.org/"></html> 2.1 替换方法 2.2添加 th:fragment 2.3主页面用一行代码替换 3.SpringBo…

UGUI源码解析——LayoutUtility

一&#xff1a;前言 布局的工具类&#xff0c;可以获取到对象的minWidth、preferredWidth、flexibleWidth、minHeight、preferredHeight、layoutPriority的属性值 二&#xff1a;源码解析 ——获取对象属性值的方法 以上方法可以获取到对象的minWidth、preferredWidth、flexi…

Flutter布局指南之深入理解BoxConstraints

点击上方蓝字关注我&#xff0c;知识会给你力量 强烈建议先看下这篇文章——Flutter你竟是这样的布局 不管你是Android开发&#xff0c;还是Flutter开发&#xff0c;当你开始使用Flutter茫茫多的Widget时&#xff0c;可能会猜测Widget在屏幕上的尺寸和位置&#xff0c;但事实上…

vue element-ui el-table表格二次封装 自定义el-table表格组件 vue封装表格组件

CommTable.vue table组件 <template><div><el-table:data"tableData"border:class"tabClass ? tabClass : null":showHeader"showHeader ? showHeader : true":spanMethod"spanMethod ? spanMethod : null"element…

Stage的MinWidth和MinHeight的疑问

设置了Stage的MinWidth和MinHeight,但是显示的时候不是这个高度和宽度&#xff0c;最小化之后再次显示的时候就可以了&#xff0c;奇怪 package stage;import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import jav…

php的width是什么意思,minwidth什么意思?min-width怎么设置

很多人刚刚入门css的新手&#xff0c;不知道minwidth什么意思&#xff1f;min-width怎么设置&#xff0c;下面php中文网就带领大家来学习一下min-width。 一&#xff1a;minwidth什么意思 在css中&#xff0c;minwidth是设置段落的最小宽度&#xff0c;使用该属性是设置一个最小…

width和min-width的区别和差异性比较

1、正常情况下&#xff1a; width :给块级元素/行内块 元素设置固定的宽度&#xff0c;或者固定百分比的宽度。 min-width: 当盒子内部元素宽度小于 min-width的值时&#xff0c;盒子宽度为 min-width的值&#xff0c;当盒子内容宽度大于 min-width的值时&#xff0c;盒子随着…

【NOIP2013提高组】华容道

题目背景 NOIP2013 提高组 Day2 试题。 题目描述 小 B 最近迷上了华容道&#xff0c;可是他总是要花很长的时间才能完成一次。于是&#xff0c;他想到用编程来完成华容道&#xff1a;给定一种局面&#xff0c;华容道是否根本就无法完成&#xff0c;如果能完成&#xff0c;最…

【NOIP2013提高组】花匠

题目背景 NOIP2013 提高组 Day2 试题。 题目描述 花匠栋栋种了一排花&#xff0c;每株花都有自己的高度。花儿越长越大&#xff0c;也越来越挤。栋栋决定把这排中的一部分花移走&#xff0c;将剩下的留在原地&#xff0c;使得剩下的花能有空间长大&#xff0c;同时&#xff…

【NOIP2013提高组】积木大赛

题目背景 NOIP2013 提高组 Day2 试题 题目描述 春春幼儿园举办了一年一度的“积木大赛”。今年比赛的内容是搭建一座宽度为 n 的大厦&#xff0c;大厦可以看成由 n 块宽度为 1 的积木组成&#xff0c;第 i 块积木的最终高度需要是 hi。 在搭建开始之前&#xff0c;没有任何…

7.15 NOIP 2013

NOIP 2013 DAY 1 DAY1 T1 转圈游戏 快速幂模板 #include<bits/stdc.h> using namespace std; int n,m,k,x; long long ans; long long cmd(long long a,long long b){long long sum1;for(;a;a>>1){if(a&1){sumsum*b%n;}bb*b%n; } return sum; } int main(…

noip2013 day2

一道纯模拟就可以过&#xff08;水水水&#xff09;。 考试时本蒟蒻甚至写了个线段树&#xff0c;然后发现其实不如直接模拟。 模拟思路&#xff1a; 从1到n枚举每个最长的不为0的序列&#xff0c;每次每个数减去其中剩余的最小值&#xff0c;答案加上这个最小值&#xff0c…

noip2013

D1&#xff1a; T1&#xff1a;快速幂 #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmath> #define LL long long using namespace std; LL n,m,k,x; inline LL quickpow(LL…

解题报告:NOIP2013 车站分级(拓扑序递推求解差分约束、建图优化O(n+m)) 超详细讲解

本题是2013年NOIP普及组的压轴题 差分约束裸题。 计算当前线路中最小的级别&#xff08;比较始发站和终点站&#xff09;。 整条线路中所有大于这个级别的都必须停靠 所有未停靠的站点的级别一定小于这个级别 也就是说所有未停靠的即为级别低&#xff0c;记为A 所有停靠的站点…

[NOIP2013]记数问题

[NOIP2013]记数问题 1.题目2.分析3.代码方法1&#xff1a;将每个数字的每一位单独算出方法2&#xff1a;转换为字符串再进行遍历 4.反思总结5.更新日志 1.题目 题目链接 题号&#xff1a;NC16538 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C…

ARMv8体系结构基础04:算术和移位指令

目录 1 数据处理指令概述 2 加法指令详解 2.1 ADD指令 2.1.1 ADD&#xff08;extended register&#xff09;指令编码分析 2.1.2 ADD&#xff08;extended register&#xff09;指令编码验证 2.1.3 ADD&#xff08;immediate&#xff09;指令编码分析 2.1.4 ADD&#xf…

8086CPU指令系统--汇编语言逻辑运算和移位操作指令

文章目录 一、逻辑运算指令1、逻辑‘与’指令 AND2、逻辑‘或’指令 OR3、逻辑“非”指令 NOT4、逻辑“异或” XOR5、测试指令TEST 二、移位指令1&#xff09;非循环移位1、算数左移SAL和逻辑左移SHL2、逻辑右移SHR3、算术右移SAR 2&#xff09;循环移位1、带CF的循环左移 RCL2…