Android Widget 也称桌面小部件,是系统应用开发层面的一部分,可以把一个控件嵌入到另一个进程的串口里面。
一、自定义AppWidget
1 AppWidgetProvider
继承广播,用于更新widget信息,实际是一个广播接受者,核心方法
- onUpdate:更新时触发,直接参与到了widget界面的显示更新
- onReceiver:收到特定广播触发
主要功能接收AppWidgetService发出的相应广播,做出相应的处理
public class ThirdAppWidget extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName componentName = new ComponentName(context, ThirdAppWidget.class);
if ("btn_test".equals(intent.getAction())) {
Toast.makeText(context, TAG, Toast.LENGTH_SHORT).show();
Log.i(TAG, "btntest i" + i);
i++;
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.third_app_widget);
views.setTextViewText(R.id.appwidget_text, "test" + i);
appWidgetManager.updateAppWidget(componentName, views);
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
this.appWidgetIds = appWidgetIds;
Log.i(TAG, "onUpdate");
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
updateAppWidget 可替换为 parttillyUpdateWidget()方法部分更新UI,提高效率
onUpdate遵循的基本流程
1、创建RemoteView
2、调用AppWidgetManager.updateAppWidget方法更新view
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
Log.i(TAG, "updateAppWidget");
CharSequence widgetText = context.getString(R.string.appwidget_text);
// 1 Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.third_app_widget);
views.setTextViewText(R.id.appwidget_text, widgetText);
Intent intent = new Intent(context, ThirdAppWidget.class);
intent.setAction("btn_test");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
views.setOnClickPendingIntent(R.id.btn, pendingIntent);
// 2 Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
2 编写配置文件
主要是widget默认布局和一些参数。对应类AppWidgetProviderInfo
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:initialKeyguardLayout="@layout/third_app_widget"
android:initialLayout="@layout/third_app_widget"
android:minWidth="140dp"
android:minHeight="40dp"
android:previewImage="@drawable/example_appwidget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="20000"/>
3 清单文件中进行注册和配置
<receiver android:name=".ThirdAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.MODE_UPDATE" />
<action android:name="btn_test" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/third_app_widget_info" />
</receiver>
二、AppWidget加载显示
具有系统应用权限的app才可绑定显示widget,system\app目录下
1 创建APPWidgetHost实列。其主要功能
(1)开启Widget更新监听
private fun init() {
//1024 host_id 会被WidgetService记录,依据此id更新widget
mAppWidgetHost = AppWidgetHost(applicationContext, 1024);
mAppWidgetManager = AppWidgetManager.getInstance(applicationContext)
mPackageManager = application.packageManager
mUserManager = applicationContext.getSystemService(Context.USER_SERVICE) as UserManager
mAllWidgetProviders = getAllProviders();
loadAllBindWidgetProviders()
mAppWidgetHost!!.startListening()
}
(2)申请appwidgetId,并通过AppWidgetService进行绑定
val appWidgetId = mAppWidgetHost!!.allocateAppWidgetId()
val success: Boolean = mAppWidgetManager!!.bindAppWidgetIdIfAllowed(
appWidgetId,
info.profile,
info.provider,
Bundle()
)
(3)创建AppWidgetHostView实例
AppWidgetHostView是真正加载视图的地方,通过AppWidgetService查询appWidgetId对应的RemoteViews,传递给AppWidgetHostView
private fun createWidgetView(provider: ComponentName): AppWidgetHostView? {
val info: AppWidgetProviderInfo? = getAppWidgetProviderInfo(provider)
if (info == null) {
Log.i("aaaaaa", "getAppWidgetProviderInfo is null ")
return null
}
val appWidgetId = mAppWidgetHost!!.allocateAppWidgetId()
val success: Boolean = mAppWidgetManager!!.bindAppWidgetIdIfAllowed(
appWidgetId,
info.profile,
info.provider,
Bundle()
)
Log.i("aaaaa", "appWidgetID:" + appWidgetId + "bindSuccess:" + success + "mWidgetProvider:" + info)
if (!success) {
mAppWidgetHost?.deleteAppWidgetId(appWidgetId);
}
val view = mAppWidgetHost!!.createView(applicationContext, appWidgetId, info)
appWidgetHostViews!!.add(view)
return view
}
添加权限
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
<uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
2 获取AppWidgetProvideinfo
主要保存widget相关信息,如布局,大小,更新频率等,在widget所在工程,xml文件路径下配置:
third_app_widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:initialKeyguardLayout="@layout/third_app_widget"
android:initialLayout="@layout/third_app_widget"
android:minWidth="140dp"
android:minHeight="40dp"
android:previewImage="@drawable/example_appwidget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="20000"/>
参考:(15条消息) Android AppWidget系统框架_iteye_6233的博客
(15条消息) android中AppWidgetManager_写Android的媛运气不会太差的博客
实时更新widget_Android技巧_积木网(gimoo.net)
Android Widget工作原理浅析 - Hi ECARX云文档 (feishu.cn)