Android 桌面小组件使用
创始人
2025-01-10 13:33:44
0

基本步骤

1.创建小组件布局

这里需要注意的事,小组件布局里不能使用自定义View,只能使用原生的组件,比如说LinearLayout,TextView,连约束布局都不能使用

                                                                                                                         

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文件:

  

Android12版本以上新增的2个属性,声明组件是4*2大小

  • targetCellWidth
  • targetCellHeight

4.清单文件声明

 	 		 	 	 

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也可

然后,就是对于定时的情况和对应方案:

  1. 如果是间隔多长更新一次,可以使用开一个服务,在服务中开启协程进行
  2. 如果是单纯的时间文本更新,可以使用TextClock组件,比如说 12:21这种
  3. 小组件的xml中默认可以设置定时更新时长,不过最短只能需要15分钟
  4. 可以使用闹钟服务AlarmManager来实现定时,不过此用法需要结合pendingintent和广播接收器使用,最终要在广播接收器里调用更新数据方法
  5. JobScheduler来实现定时更新,似乎受系统省电策略影响,适用于不太精确的定时事件(官方文档上推荐这个)
  6. WorkManager来实现定时更新(实际上算是JobScheduler升级版),似乎受系统省电策略影响,适用于不太精确的定时事件

应该是除了第一种方法,其他都是可以在应用被杀死的情况进行更新小组件UI

小组件播放动画

progressbar实现

帧动画不手动调用anim.start()方法是不会播放的,然后在网上看到一篇文章,使用了progressbar来实现,步骤如下:

在drawable文件夹准备帧动画文件

                      

indeterminateDrawable设置为上面的帧动画文件即可

layoutanim实现

主要是利用viewgroup的初次显示的时候,会展示当前view的添加动画效果,从而实现比较简单的动画效果,如平移,缩放等

可以看实现的敲木鱼一文Android-桌面小组件RemoteViews播放木鱼动画 - 掘金

使用ViewFlipper

ViewFlipper主要是轮播使用的

里面可放几个元素,之后通过设置autoStart为true,则保证自动轮播

flipInterval属性则是每个元素的间隔时间(帧动画的时间),单位为ms

不过在remoteview中使用的话,缺点就是里面的元素数目只能固定死

否则只能通过定义不同layout文件(如3个元素则是某个layout,4个元素则是某个layout,然后根据选择来创建remoteview)

                         

补充

获取当前桌面的组件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())

相关内容

热门资讯

一分钟内幕!科乐吉林麻将系统发... 一分钟内幕!科乐吉林麻将系统发牌规律,福建大玩家确实真的是有挂,技巧教程(有挂ai代打);所有人都在...
一分钟揭秘!微扑克辅助软件(透... 一分钟揭秘!微扑克辅助软件(透视辅助)确实是有挂(2024已更新)(哔哩哔哩);1、用户打开应用后不...
五分钟发现!广东雀神麻雀怎么赢... 五分钟发现!广东雀神麻雀怎么赢,朋朋棋牌都是是真的有挂,高科技教程(有挂方法)1、广东雀神麻雀怎么赢...
每日必看!人皇大厅吗(透明挂)... 每日必看!人皇大厅吗(透明挂)好像存在有挂(2026已更新)(哔哩哔哩);人皇大厅吗辅助器中分为三种...
重大科普!新华棋牌有挂吗(透视... 重大科普!新华棋牌有挂吗(透视)一直是有挂(2021已更新)(哔哩哔哩)1、完成新华棋牌有挂吗的残局...
二分钟内幕!微信小程序途游辅助... 二分钟内幕!微信小程序途游辅助器,掌中乐游戏中心其实存在有挂,微扑克教程(有挂规律)二分钟内幕!微信...
科技揭秘!jj斗地主系统控牌吗... 科技揭秘!jj斗地主系统控牌吗(透视)本来真的是有挂(2025已更新)(哔哩哔哩)1、科技揭秘!jj...
1分钟普及!哈灵麻将攻略小,微... 1分钟普及!哈灵麻将攻略小,微信小程序十三张好像存在有挂,规律教程(有挂技巧)哈灵麻将攻略小是一种具...
9分钟教程!科乐麻将有挂吗,传... 9分钟教程!科乐麻将有挂吗,传送屋高防版辅助(总是存在有挂)1、完成传送屋高防版辅助透视辅助安装,帮...
每日必看教程!兴动游戏辅助器下... 每日必看教程!兴动游戏辅助器下载(辅助)真是真的有挂(2025已更新)(哔哩哔哩)1、打开软件启动之...