Android 桌面小组件使用
借助公司上的几个项目,算是学习了Android桌面小组件的用法,记下踩坑记录
基本步骤
1.创建小组件布局
这里需要注意的事,小组件布局里不能使用自定义View,只能使用原生的组件,比如说LinearLayout,TextView,连约束布局都不能使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tvDate"
style="@style/textStyle14"
android:textColor="#313131"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2023-12-10" />
<ImageView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="@+id/tvTime"
android:textColor="#313131"
style="@style/textStyle14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12:10" />
</LinearLayout>
<LinearLayout
android:layout_marginTop="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/result_clean"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_marginStart="9dp"
android:gravity="center_vertical"
android:layout_height="match_parent"
android:layout_weight="1" >
<TextView
style="@style/textStyle14"
android:textColor="#313131"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="125.4MB"/>
<TextView
style="@style/textStyle14"
android:textColor="#313131"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Junk"/>
</LinearLayout>
<TextView
android:layout_gravity="center_vertical"
android:id="@+id/tvClean"
android:textColor="#313131"
style="@style/textStyle14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clean" />
</LinearLayout>
</LinearLayout>
2.创建provider
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.widget.RemoteViews
import android.widget.RemoteViews.RemoteView
import ten.jou.recover.R
class CleaningWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
appWidgetIds.forEach {
//如果小组件布局中使用不支持的组件,这里创建RemoteViews时候,IDE会报红提示!
val remoteView = RemoteViews(context.packageName, R.layout.widget_layout)
//绑定数据
remoteView.setTextViewText(R.id.tv1,"hello world")
appWidgetManager.updateAppWidget(it, remoteView)
}
}
}
AppWidgetProvider本质就是一个广播接收器,所以在清单文件需要声明(见步骤4)
这里先补充下,RemoteViews对于TextView,ImageView等View,有设置文本,字体颜色,图片等相关方法,但并不是所有方法都支持,绑定数据的时候需要注意下小组件是否支持!
3.创建xml属性声明
在xml文件夹里新建widget_info.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:targetCellWidth="4"
android:targetCellHeight="2"
android:minWidth="250dp"
android:minHeight="110dp"
android:updatePeriodMillis="0"
android:initialLayout="@layout/widget_layout"
tools:targetApi="s">
</appwidget-provider>
Android12版本以上新增的2个属性,声明组件是4*2大小
- targetCellWidth
- targetCellHeight
4.清单文件声明
<receiver
android:name=".view.CleaningWidget"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
5.代码添加小组件
官方说Android12不允许直接通过代码添加小组件,只能让用户手动去桌面拖动添加,但是我手头的三星系统却是支持的(也是Android12),具体还没有细究...
而官方文档上的写的例子如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val context = this@DesktopWidgetActivity
val appWidgetManager: AppWidgetManager =
context.getSystemService(AppWidgetManager::class.java)
val myProvider = ComponentName(context, CleaningWidget::class.java)
//判断启动器是否支持小组件pin
val successCallback = if (appWidgetManager.isRequestPinAppWidgetSupported) {
// Create the PendingIntent object only if your app needs to be notified
// that the user allowed the widget to be pinned. Note that, if the pinning
// operation fails, your app isn't notified.
Intent(context, CleaningWidget::class.java).let { intent ->
// Configure the intent so that your app's broadcast receiver gets
// the callback successfully. This callback receives the ID of the
// newly-pinned widget (EXTRA_APPWIDGET_ID).
//适配android12的
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
PendingIntent.getBroadcast(
context,
0,
intent,
flags
)
}
} else {
null
}
appWidgetManager.requestPinAppWidget(myProvider, null, successCallback)
}
这里提下,上面的设置flags方法
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
有个新项目的targetSdk为34(即Android14),如果使用上面的代码会出现下面崩溃错误提示
Targeting U+ (version 34 and above) disallows creating or retrieving a PendingIntent with FLAG_MUTABLE, an implicit Intent within and without FLAG_NO_CREATE and FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT for security reasons. To retrieve an already existing PendingIntent, use FLAG_NO_CREATE, however, to create a new PendingIntent with an implicit Intent use FLAG_IMMUTABLE.
实际上提示已经告诉我们怎么去改代码了,我这里把PendingIntent.FLAG_MUTABLE
改为FLAG_IMMUTABLE
就不会出现了上述的崩溃问题
应该是Android14添加的限制:
- 如果Intent不传数据,必须使用
PendingIntent.FLAG_IMMUTABLE
- 如果是需要传递数据,则还是需要使用
PendingIntent.FLAG_MUTABLE
定时刷新小组件UI
首先,我们得知道,如何主动去更新数据:
val context = it.context
val appWidgetManager: AppWidgetManager = context.getSystemService(AppWidgetManager::class.java)
val myProvider = ComponentName(context, CleaningWidget::class.java)
val remoview = CleaningWidget.getRemoteViewTest(context)
//更新某类组件
appWidgetManager.updateAppWidget(myProvider,remoview)
//更新具体某个组件id
appWidgetManager.updateAppWidget(widgetId,remoview)
getRemoteViewTest方法就是创建一个remoteview,然后调用remoteview相关方法设置文本之类的进行数据填充,代码就略过不写了,详见上述基本步骤2
上面的方法我们注意到updateAppWidget
可以传不同的参数,一般我们用的第二个方法,指定更新某个组件
但这里又是需要我们传一个组件id,所以就是在步骤2的时候,我们根据需要需要存储下widgetId比较好,一般存入数据库,或者使用SharePreference也可
然后,就是对于定时的情况和对应方案:
- 如果是间隔多长更新一次,可以使用开一个服务,在服务中开启协程进行
- 如果是单纯的时间文本更新,可以使用TextClock组件,比如说 12:21这种
- 小组件的xml中默认可以设置定时更新时长,不过最短只能需要15分钟
- 可以使用闹钟服务AlarmManager来实现定时,不过此用法需要结合pendingintent和广播接收器使用,最终要在广播接收器里调用更新数据方法
- JobScheduler来实现定时更新,似乎受系统省电策略影响,适用于不太精确的定时事件(官方文档上推荐这个)
- WorkManager来实现定时更新(实际上算是JobScheduler升级版),似乎受系统省电策略影响,适用于不太精确的定时事件
应该是除了第一种方法,其他都是可以在应用被杀死的情况进行更新小组件UI
小组件播放动画
progressbar实现
帧动画不手动调用anim.start()
方法是不会播放的,然后在网上看到一篇文章,使用了progressbar来实现,步骤如下:
在drawable文件夹准备帧动画文件
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" android:visible="true">
<item android:drawable="@drawable/cat_1" android:duration="100" />
<item android:drawable="@drawable/cat_2" android:duration="100" />
<item android:drawable="@drawable/cat_3" android:duration="100" />
<item android:drawable="@drawable/cat_4" android:duration="100" />
</animation-list>
<ProgressBar
android:indeterminateDrawable="@drawable/cat_animdrawable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
indeterminateDrawable设置为上面的帧动画文件即可
layoutanim实现
主要是利用viewgroup的初次显示的时候,会展示当前view的添加动画效果,从而实现比较简单的动画效果,如平移,缩放等
可以看实现的敲木鱼一文Android-桌面小组件RemoteViews播放木鱼动画 - 掘金
使用ViewFlipper
ViewFlipper主要是轮播使用的
里面可放几个元素,之后通过设置autoStart为true,则保证自动轮播
flipInterval属性则是每个元素的间隔时间(帧动画的时间),单位为ms
不过在remoteview中使用的话,缺点就是里面的元素数目只能固定死
否则只能通过定义不同layout文件(如3个元素则是某个layout,4个元素则是某个layout,然后根据选择来创建remoteview)
<ViewFlipper
android:id="@+id/viewFlipper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:autoStart="true"
android:flipInterval="800">
<ImageView
android:id="@+id/vf_img_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/peace_talisman_1" />
<ImageView
android:id="@+id/vf_img_2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/peace_talisman_2" />
</ViewFlipper>
补充
获取当前桌面的组件id列表
//获得当前桌面已添加的组件的id列表(可能用户添加了多个)
val context = it.context
val appWidgetManager: AppWidgetManager = context.getSystemService(AppWidgetManager::class.java)
val myProvider = ComponentName(context, CleaningWidget::class.java)
val info =appWidgetManager.getAppWidgetIds(myProvider)
toast(info.size.toString())
参考
- 构建应用微件 | Android 开发者 | Android Developers
- Android 12桌面小组件 - 掘金
- Android 12上焕然一新的小组件:美观、便捷和实用 - 掘金
- baiyuas.github.io | 拜雨个人博客
- Android小部件APP Widget开发 - 掘金
- 【精选】Android 桌面小组件 AppWidgetProvider-CSDN博客
- 【APP Widget】使用代码申请添加小部件,展示添加弹窗。 - 掘金
- Android-桌面小组件RemoteViews播放木鱼动画 - 掘金
- 【Android小知识点】Widget中实现动画的一种极简方式_桌面小控件帧动画-CSDN博客
- 【APP Widget】使用WorkManager定时更新小部件 - 掘金
Android 桌面小组件使用的更多相关文章
- Android桌面小组件的使用
一:建立一个类继承AppWidgetProvider 二:建立AWP的布局文件: 布局自己定义一个,但是在使用控件上是有要求的: 以上是Widget目前支持的控件. 三:编写AWP的信息文件:需要在r ...
- Android Widget小组件开发(一)——Android实现时钟Widget组件的步骤开发,这些知识也是必不可少的!
Android Widget小组件开发(一)--Android实现时钟Widget组件的步骤开发,这些知识也是必不可少的! PS:学习自某网站(不打广告) 这个小组件相信大家都很熟悉吧,以前的墨迹天气 ...
- Android-Widget桌面小组件
1, 掌握Widget的用:Widget的用途,能够添加到手机桌面的程序 2, Widget的特点和用法步骤: 特点:快捷,方便,个性化,可自定义功能,可及时控制更新Widget显示内容 3, 用法步 ...
- Android桌面小插件——Widget
Android桌面小插件--Widget 效果图 实现 1. 创建Widget类 创建一个Widget类,并实现页面创建的时候,就实现显示时间 package com.kongqw.kqwwidget ...
- Android 桌面小部件
1. 添加AppWidgetProvider 实际上就是个带有界面的BroadcastReceiver public class SimpleWidgetProvider extends AppWid ...
- android 桌面小工具(Widget)开发教程
刚学做了哥Widget,感觉不错哦,先来秀下效果(用朋友手机截的图) 这个Widget会每隔5秒钟自动切换内容和图片,图片最好使用小图,大图会导致你手机桌面(UI)线程卡顿 教程开始: 1.首先创建一 ...
- android桌面小火箭升空动画
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceS ...
- Android开发工程师文集-1 小时学会Widget小组件开发
前言 大家好,给大家带来Android开发工程师文集-1 小时学会Widget小组件开发的概述,希望你们喜欢 学会用Widget (小组件) Widget小组件很方便,很快捷,可以个性化,自己定制,相 ...
- Android开发中实现桌面小部件
详细信息请参考原文:Android开发中实现桌面小部件 在Android开发中,有时候我们的App设计的功能比较多的时候,需要根据需要更简洁的为用户提供清晰已用的某些功能的时候,用桌面小部件就是一个很 ...
- Android中有四大组件的简单总结
Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity ...
随机推荐
- HanLP — 词性标注
词性(Part-Of-Speech,POS)指的是单词的语法分类,也称为词类.同一个类别的词语具有相似的语法性质 所有词性的集合称为词性标注集. 词性的用处 当下游应用遇到OOV时,可以通过OOV的词 ...
- centos编译安装tcpdump
环境 CentOS Linux release 7.9.2009 (Core) 准备安装包 libpcap-1.5.3.tar.gz tcpdump-4.9.2.tar.gz 下载地址:https:/ ...
- 教你用JavaScript实现表情评级
案例介绍 欢迎来到我的小院,我是霍大侠,恭喜你今天又要进步一点点了!我们来用JavaScript编程实战案例,做一个表情评价程序.用户打星进行评价,表情会根据具体星星数量发生变化. 案例演示 点击星星 ...
- PostgreSQL-常用命令汇总
1.连接到 PostgreSQL 数据库: psql -h 主机名/服务器IP -p 端口号 -U 用户名 -d 数据库名 注意:(1)在服务器上本地登录时,可以使用主机名或者本机IP地址进行登录,但 ...
- C# 二十年语法变迁之 C# 8参考
C# 二十年语法变迁之 C# 8参考 自从 C# 于 2000 年推出以来,该语言的规模已经大大增加,我不确定任何人是否有可能在任何时候都对每一种语言特性都有深入的了解.因此,我想写一系列快速参考文章 ...
- JS 20道概念虽老但也略有收获的JS基础题,快速做题,高效复习,不妨试试?
壹 ❀ 引 在7月21交接完所有工作后,我也进入了休年假的阶段(没用完的8天年假),看似休息内心的紧张感反而瞬间加倍,到今天为止也面了几家,好消息是工作机会特别特别多,一封简历没投,面试邀请源源不断, ...
- NC17890 方格填色
题目链接 题目 题目描述 给一个m x n的方格,Applese想要给方格填上颜色,每个格子可以是黑色或者白色.他要求左右相邻两格不能同为白色且相邻两列不能全为黑色. 求满足条件的方案数. 输入描述 ...
- NC13224 送外卖
题目链接 题目 题目描述 n 个小区排成一列,编号为从 0 到 n-1 .一开始,美团外卖员在第0号小区,目标为位于第 n-1 个小区的配送站. 给定两个整数数列 a[0]~a[n-1] 和 b[0] ...
- QT C++工程CI环境笔记
开发环境 Ubuntu18.04 or Ubuntu20.04 Qt Creator 4.6.x (Based on Qt 5.11.x) APT list: apt-transport-https ...
- VueRouter导航守卫
VueRouter导航守卫 vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航,简单来说导航守卫就是路由跳转过程中的一些钩子函数,路由跳转是一个大的过程,这个大的过程分为跳转前中后 ...