你或许用过一些 Android APP
的小组件,比方:
- 支付宝的小组件:之前疫情期间增加了对应小组件卡片在桌面,可点击小卡片上的检查健康码的按钮,可一键翻开健康码。
- 音乐类
APP
的小组件:增加对应对应小组件后, 可在 APP 的主屏幕中轻松看到当时播放歌曲的相关信息:歌曲封面、歌曲名、歌手名称、所属专辑名称等。 - 时钟类
APP
的小组件:可增加各种款式的时钟小组件在屏幕,装饰你的主屏幕,在你喜爱的小组件上来检查时刻。 - 气候类
APP
的小组件:可在主屏幕直接看到气候相关信息,不用再翻开气候的APP
。
假如你所开发的项目的产品司理并没有相关开发需求,或许很多 Android
开发并没有触摸过相关桌面主屏幕的相关小组件开发,这篇文章首要介绍下相关开发的知识点。
AppWidgetProvider:
协助完成AppWidget
供给程序的便利类。 你能够用AppWidgetProvider
做的事情,你也能够用一个普通的BroadcastReceiver
做。AppWidgetProvider
仅仅从onReceive(Context,Intent)
中接纳到的Intent
中解分出相关字段,并运用接纳到的extra
调用hook
办法。
扩展此类并掩盖onUpdate
、onDeleted
、onEnabled
或onDisabled
办法中的一个或多个以完成您自己的AppWidget
功用。
public class WidgetDemoProvider extends AppWidgetProvider {
/**
* 当 Widget 第一次被增加时调用,例如用户增加了两个你的 Widget,那么只要在增加第一个 Widget 时该
* 办法会被调用。所以该办法比较合适履行你一切 Widgets 只需进行一次的操作。对用播送的 Action 为
* ACTION_APPWIDGET_ENABLE。
*/
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
}
/**
* 与 onEnabled 恰好相反,当你的最终一个 Widget 被删除时调用该办法,所以这儿用来清理之前在 onEnabled() 中进行的操作。
* 当最终一个该类型的小部件从桌面移除时调用,对应的播送的 Action 为 ACTION_APPWIDGET_DISABLED。
*
*/
public void onDisabled(Context context) {
super.onDisabled(context);
}
/**
* 当 Widget 第一次被增加或许巨细产生改变时调用该办法,能够在此操控 Widget 元素的显现和躲藏。
* 当小部件布局产生更改的时分调用。对应播送的 Action 为 ACTION_APPWIDGET_OPTIONS_CHANGED。
*
* @param appWidgetManager 您能够调用 AppWidgetManager.updateAppWidget 的 AppWidgetManager 目标。
* @param appWidgetId 巨细改动的widget的appWidgetId。
* @param newOptions
*/
@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}
/**
* 当小部件从备份中复原,或许康复设置的时分,会调用,实际用的比较少。对应播送的 Action 为 ACTION_APPWIDGET_RESTORED。
* @param oldWidgetIds
* @param newWidgetIds
*/
@Override
public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
super.onRestored(context, oldWidgetIds, newWidgetIds);
}
/**
* AppWidget 更新事情
*
* 小部件被增加时或许每次小部件更新时都会调用一次该办法,装备文件中装备小部件的更新周期 updatePeriodMillis,每次更新都会调用。对应播送 Action 为:ACTION_APPWIDGET_UPDATE 和 ACTION_APPWIDGET_RESTORED
*
* @param appWidgetManager 更新 AppWidget 状况; 获取有关已安装 AppWidget 供给程序和其他 AppWidget 相关状况的信息。
* @param appWidgetIds 需求更新的 appWidgetIds。 请留意,这或许是此供给程序的一切 AppWidget 实例,也或许仅仅其间的一个子集。
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
}
/**
* 当 Widget 被删除时调用该办法。
*
* 每删除一个小部件就调用一次。对应的播送的 Action 为: ACTION_APPWIDGET_DELETED 。
*
* @param appWidgetIds 已从其组件集群中删除的 appWidgetIds。
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
}
/**
* 接纳播送的回调函数
* onReceive() 中处理的是 Widget 相关的播送事情,然后分发到各个回调函数中onUpdate(), onDeleted(), onEnabled(), onDisabled, onAppWidgetOptionsChanged()。
*
* @param context
* @param intent
*/
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
}
else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
}
...省掉
super.onReceive(context, intent);
}
}
依据自己的事务,写好 WidgetDemoProvider
的自界说代码后,需求在 AndroidManifest.xml
里注册一下:
<receiver android:name=".widget.WidgetDemoProvide"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="xxx..."/> <!-- 此处能够增加自己需求的 -->
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/appwidget_demo" /> <!-- 此处能够增加自己需求的给用户提前预览的自界说小组件布局 -->
</receiver>
<receiver>
元素需求android:name
特点,该特点指定小部件运用的AppWidgetProvider
(AppWidgetProvider
的父类便是BroadcastReceiver
)。
<intent-filter>
中的<action>
元素指定小部件接受ACTION_APPWIDGET_UPDATE
播送。这是有必要明确声明的唯一一项播送,用以接纳小部件的增删改等信息。
<meta-data>
元素指定小部件的资源,而且需求以下特点:
android:name
– 指定元数据名称。有必要运用android.appwidget.provider
将数据标识为AppWidgetProviderInfo
描述符。
android:resource
– 指定AppWidgetProviderInfo
资源方位。
appwidget_demo.xml
的示例代码:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp"
android:minHeight="180dp"
android:updatePeriodMillis="1800000"
android:previewImage="@mipmap/app_widget_preview_3x3"
android:initialLayout="@layout/app_widget_preview_layout_3_3"
android:resizeMode="horizontal|vertical">
</appwidget-provider>
minWidth
和minHeight
:指定小部件默许情况下占用的最小空间。
留意:为使小部件能够在设备间移植,小部件的最小巨细不得超过 4 x 4 单元格。
minResizeWidth
和minResizeHeight
:指定小部件的肯定最小巨细。
updatePeriodMillis
:界说小部件结构经过调用 onUpdate() 回调办法来从 AppWidgetProvider 恳求更新的频率应该是多大。
initialLayout
:指向用于界说小部件布局的布局资源。
configure
:界说要在用户增加小部件时发动以便用户装备小部件特点的 Activity。
previewImage
:指定预览来描绘小部件经过装备后是什么样子的,用户在挑选小部件时会看到该预览。
autoAdvanceViewId
:指定应由小部件的保管应用自动跳转的小部件子视图的视图 ID。
resizeMode
:指定能够按什么规则来调整微件的巨细,可选值为“horizontal|vertical”,一般默许设置横竖都能够进行调整。
minResizeHeight
:指定可将微件巨细调整到的最小高度。
minResizeWidth
:指定可将微件巨细调整到的最小宽度。
widgetCategory
:声明小部件是否能够显现在主屏幕 (home_screen
) 或确定屏幕 (keyguard
) 上。只要低于 5.0 的 Android 版别才支撑确定屏幕微件。关于 Android 5.0 及更高版别,只要home_screen
有用,所以现在将这个值写为home_screen
即可。
假如在自己的相关事务代码里,比方 activity
里如何触发 WidgetDemoProvider
相关数据以及页面更新。能够看以下示例代码:
private void sendNotify(){
try {
Class javaClass = Class.forName("xxx.WidgetDemoProvider");
final Intent intent = new Intent(this, javaClass);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
int[] ids = AppWidgetManager.getInstance(GlobeContext.context).getAppWidgetIds(new ComponentName(GlobeContext.context, javaClass));
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
sendBroadcast(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
一些开发留意事项:
- 小组件的宽高是能够支撑用户自行调整的,只需简略的设置最低宽高,可是可调整的最小粒度是依据手机的
icon
为规范。小组件数量无约束,用户对小组件的巨细和详细功用喜爱都不太一样,所以解决方案就简略粗犷一点,你能想到的适配尺度,每种尺度搞一个,用户自己挑选合适的尺度就好。大、中、小、大中、中小、细小、超大等尺度,能够全部做一遍。
android:resizeMode="horizontal|vertical"
widget 能够被拉伸的方向。horizontal表明能够水平拉伸,vertical表明能够竖直拉伸
- 更新时刻分为自动更新和守时更新。
自动更新:即在APP
中能够动态更新这个桌面小组件,这种情况更新没有时刻约束。
守时更新:小组件需求展现的数据或许现已产生了改变,可是APP
现已被体系杀死了,无法自动更新数据,就会导致小组件展现的数据或许是已过期的或许是旧的,这时分就能够用到小组件的守时更新功用,可是这个守时更新有一个约束,根据省电逻辑,最快的更新周期为 30 分钟。(假如是在onUpdate
办法中写个守时器守时更新,这样是不可的,会被体系给杀死,杀死之后小组件不会消失,而是一向显现最终一次更新时分的状况,直到下一次更新数据,类似于电子水墨屏的逻辑。) - 一般点击整个小组件,我们直接调起
APP
。点击跳转页面需求用到PendingIntent
,这玩意的Flag
有很多种形式,详细能够检查文章底部的参考文档,坑就坑在这个Flag
,31 之后的体系有改动,会报错,所以 31 的体系需求用PendingIntent.FLAG_IMMUTABLE
,详细看代码。
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
}
else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
}
if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
Uri data = intent.getData();
int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
switch (buttonId) {
case R.id.widget_layout:
Intent intent = new Intent(context, RemotePlayerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(goIntent);
RemoteViews remoteView = new RemoteViews(context.getPackageName(),R.layout.app_widget_layout);
//将按钮与点击事情绑定
remoteView.setOnClickPendingIntent(R.id.widget_layout,getPendingIntent(context, R.id.widget_layout));
break;
}
}
super.onReceive(context, intent);
}
private PendingIntent getPendingIntent(Context context, int buttonId) {
Intent intent = new Intent();
intent.setClass(context, WidgetDemoProvider.class);
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
intent.setData(Uri.parse("harvic:" + buttonId));
PendingIntent pendingIntent;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
}
return pendingIntent;
}
- 经过
PendingIntent
就能够直接调起APP
的相关页面,不过这儿也有坑,假定你APP
的发动页面是MainActivity
页面,点击小组件你就让它跳转到MainActivity
页面走正常的APP
发动流程,就等同于是点击小组件就能发动APP
,哪怕APP
被杀死了。坑就坑在于,经过这种方式翻开的APP
,它不会走Application
类,也便是你假如是在Application
中初始化了某些东西,可是APP
现已被体系杀死了,这时分你再点击小组件发动APP
,这时就会发现好多组件用不了,由于没初始化。
所以相对省事的做法便是把Application
的一切需求初始化的东西都放MainActivity
里面初始化了,现在为了工信部隐私相关合规,应该很多APP
的初始化代码应该现已从Application
放到用户点同意隐私协议弹框后再去初始化了。
RemoteViews
,从字面意思理解为它是一个远程视图。是一种远程的View
,它在其它进程中显现,却能够在另一个进程中更新。RemoteViews
在Android
中的运用场景首要有:自界说通知栏和桌面小部件。
RemoteViewsService
,是办理RemoteViews
的服务。一般,当AppWidget
中包含GridView
、ListView
、StackView
等调集视图时,才需求运用RemoteViewsService
来进行更新、办理。RemoteViewsService
更新调集视图的一般过程是:经过setRemoteAdapter()
办法来设置RemoteViews
对应RemoteViewsService
。
之后在RemoteViewsService
中,完成RemoteViewsFactory
接口。然后,在RemoteViewsFactory
接口中对调集视图的各个子项进行设置,例如ListView
中的每一Item
。
RemoteViewsFactory
经过RemoteViewsService
中的介绍,我们知道RemoteViewsService
是经过
RemoteViewsFactory
来详细办理layout
中调集视图的,RemoteViewsFactory
是RemoteViewsService
中的一个内部接口。RemoteViewsFactory
供给了一系列的办法办理调集视图中的每一项。
例如:
public RemoteViews getViewAt(int position):
经过getViewAt()来获取“调集视图”中的第position项的视图,视图是以RemoteViews的目标返回的。
public int getCount() :
经过getCount()来获取“调集视图”中一切子项的总数。
- **用户可从头设置原有 widget。**在
Android 12
之前,从头设置widget
意味着用户有必要删除现有widget
,然后运用新装备从头增加。Android 12
在多个方面改进了widget
的装备方式,然后协助用户采用更简略的方式对widget
进行个性化装备。可重组的widget
答应用户对widget
进行自界说设置。在Android 12
中,用户将无需经过删除和从头增加widget
来调整这些原有设定。
要运用这一功用,您需在appwidget-provider
中把widgetFeatures
特点设置为reconfigurable
。
当用户装备该widget
时,新的装备会被记录在ListWidgetConfigureActivity
中。
假如您的widget
依靠默许设置,在Android 12
中您可跳过初始化操作,经过默许装备来设置widget
。
<appwidget-provider
android:configure="com.example.android.appwidget.ListWidgetConfigureActivity"
android:widgetFeatures="reconfigurable|configuration_optional"
... />
-
Android 12 中 Widget 的尺度约束改进。除了现有的
minWidth
、minHeigh
、minResizeWidth
以及minResizeHeight
以外,Android 12
还增加了新的appwidget-provider
特点。您能够运用新的maxResizeWidth
和maxResizeHeight
特点,来界说用户所能够调整的widget
尺度的最大高度和宽度。新的targetCellWidth
和targetCellHeight
特点能够界说设备主屏幕上的widget
默许尺度。假如之前有targetCellWidth
和targetCellHeight
特点的话,小部件也不至于像现在这么乱而导致用户不想运用。
<appwidget-provider
android:maxResizeWidth="240dp"
android:maxResizeHeight="180dp"
android:minWidth="180dp"
android:minHeight="110dp"
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:targetCellWidth="3"
android:targetCellHeight="2"
... />
<!-- maxResizeWidth:界说用户所能够调整的小部件尺度的最大宽度
maxResizeHeight:界说用户所能够调整的小部件尺度的最大高度
targetCellWidth:界说设备主屏幕上的小部件默许宽度所占格数(即使不同型号的手机中也会占界说好的格数,但手机体系版别有必要在 Android 12 及以上)
targetCellHeight:界说设备主屏幕上的小部件默许高度所占格数 -->
-
新的小部件控件。
Android 12
运用以下现有控件新增了对有状况行为的支撑:CheckBox
、Switch
、RadioButton
,上面这几个控件大家应该非常熟悉了,但在Android 12
之前在小部件中想要运用的话也是不或许的。 -
Android 12
以上能够经过system_app_widget_background_radius
和system_app_widget_inner_radius
体系参数来设置微件圆角的半径。
重视大众号:Android老皮!!!欢迎大家来找我探讨交流