前言
Anroid上的桌面小组件很早以前就有了,相比iOS的小组件一出来就大火不同,Android桌面小组件一直很难用,最近看到了Google微信号推送的文章:
我以为Android小组件间要崛起了!!开心的去看了[官网demo](user-interface-samples/AppWidget at main · android/user-interface-samples (github.com) )
在Android12上主要更新了可调整窗口大小 ,不同主题下的效果,和一些圆角边距等细节设置,看起来还是不太聪明的样子
桌面小组件被Google成为微件:AppWidget
,查看官网文档
右键菜单栏中创建Widget组件,其会自动创建相关类:
我们以创建一个TODO组件为例
相关类
组件类,通过广播与其连接,可以通过onUpdate
,onEnable
,onDelete
等方法与组件进行通信。
这里我们新建TodoWidget:
class TodoWidget : AppWidgetProvider () { override fun onUpdate ( context: Context , appWidgetManager: AppWidgetManager , appWidgetIds: IntArray ) { } override fun onEnabled (context: Context ) { } override fun onDisabled (context: Context ) { } }
同时我们需要在Mainfest.xml中注册:
<receiver android:name=".TodoWidget" 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/todo_widget_info" /> </receiver >
widgetProvider继承自广播,本身可以通过onReceive()来接受消息
提供了组件的各种信息,尺寸、布局、预览等信息,以xml文件的形式创建
在资源文件下新建xml目录:
<?xml version ="1.0" encoding ="utf-8" ?> <appwidget-provider xmlns:android ="http://schemas.android.com/apk/res/android" android:description ="@string/app_widget_description" android:initialKeyguardLayout ="@layout/todo_widget_min" android:initialLayout ="@layout/todo_widget_min" android:minWidth ="110dp" android:minHeight ="110dp" android:previewImage ="@drawable/example_appwidget_preview" android:previewLayout ="@layout/todo_widget_min" android:resizeMode ="horizontal|vertical" android:targetCellWidth ="1" android:targetCellHeight ="1" android:updatePeriodMillis ="86400000" android:widgetCategory ="home_screen" />
创建布局
Widget组件布局基于:RemoteViews 有很多特殊性,这也是目前Android小窗口的局限,很多组件都无法使用,交互也表困难。
RemoteViews 可以支持以下布局类:
FrameLayout
LinearLayout
RelativeLayout
GridLayout
以及以下微件类:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
对于我们常见的RecyclerView、约束布局都不支持!
布局代码可以看Demo,这里说下狂口调整时候的布局变化,调整窗口的大小在Android12上才可用
我们可以在WidgetProvider类中的onUpdate中更新组件:
override fun onUpdate ( context: Context , appWidgetManager: AppWidgetManager , appWidgetIds: IntArray ) { for (appWidgetId in appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId) } }
创建多个RemoteView,我这里创建了三个不同布局对应不同尺寸:
@SuppressLint("RemoteViewLayout" ) internal fun updateAppWidget( context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int ) { val remoteViewsMin = RemoteViews( context.packageName , R.layout .todo_widget_min ) val remoteViewsNormal = RemoteViews( context.packageName , R.layout .todo_widget_normal ) val remoteViewsMax = RemoteViews( context.packageName , R.layout .todo_widget_max ) val viewMapping: MutableMap<SizeF, RemoteViews> = mutableMapOf() viewMapping[SizeF(180.0f, 110.0f)] = remoteViewsMin viewMapping[SizeF(230.0f, 180.0f)] = remoteViewsNormal viewMapping[SizeF(270.0f, 300.0f)] = remoteViewsMax if (Build.VERSION .SDK_INT >= Build.VERSION_CODES .S) { appWidgetManager.updateAppWidget (appWidgetId, RemoteViews(viewMapping)) }else { appWidgetManager.updateAppWidget (appWidgetId, remoteViewsNormal) } }
查看效果
长按app图标就可在桌面添加组件,不同系统操作方式不一样。
通信交互
打开App,在Activity做出响应
widget和宿主app不属于同一进程,可以通过PendingIntent
通信,和传统的Intent有点不一样,简言之:PendingIntent含有app的context,可以在外部进程打开app进程,PendingIntent不是立即执行的。
RemoteView设置点击事件
setOnClickPendingIntent(R.id .add , createPendingIntent (context ,R.id .add ) )
createPendingIntent()是一个将id传给activity的方法,你也可以设置其他信息,在activity做出响应
private fun createPendingIntent (context: Context ,@IdRes id:Int ) :PendingIntent{ val activityIntent = Intent(context, MainActivity::class .java).apply { setData(Uri.parse("harvic:$id " )) flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK } val appOpenIntent = PendingIntent.getActivity( context, 1 , activityIntent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE ) return appOpenIntent }
在onReceive中处理:
和上述的基本一样,根据id来处理不同的点击事件
if (Build.VERSION .SDK_INT >= Build.VERSION_CODES .S) { val clickIntent = PendingIntent.getBroadcast ( context, 2 , Intent(context, TodoWidget::class.java).apply { putExtra(EXTRA_VIEW_ID, R.id .title) }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE ) setOnClickPendingIntent(R.id .title ,clickIntent) }
最后
点击查看:项目代码 ,欢迎大佬们的Star
感觉Andorid桌面小组件依旧不够完善,和以前相比也没啥大大的改变。=.=
之前的文章介绍了网络封装、组件化、基础工具等,有兴趣的可以查看:
还有一些关于UI的基础工具: