13.2.2 App Widget的界面构造和Remote Views

与一般交互界面不同,App Widget的界面构造和呈现是相分离的,App Widget界面的构造在其提供应用中进行,而具体的展示呈现则是在其宿主应用内。宿主应用会根据App Widget对象中描述的界面信息,将各个界面控件实例化,并设定好相关联的内容和事件。

App Widget的界面信息描述会通过android.widget.RemoteViews来表示,RemoteViews实现了Parcelable接口,这说明它可以进行序列化并在提供应用进程与宿主进程间传递。在Android系统中,当交互界面的构造者与交互界面的实现者相分离时,就需要使用RemoteViews来描述交互界面,这种方式不仅用在App Widget中,在其他类似的场景下,也会被用来进行构造(比如,后续会介绍的状态栏提醒界面,也是通过该方式进行构造的)。

当提供应用的AppWidgetProvider对象收到更新组件内容的广播消息后,可以来构造与App Widget界面相关的RemoteViews对象,并传递给宿主应用,一个示例如下:


//当Widget对象定时更新时,会调用该方法

@Override

public void onUpdate(Context context,

AppWidgetManager appWidgetManager,

int[]appWidgetIds){

//传入该Widget对象的layout信息,构造RemoteViews对象

RemoteViews views=new RemoteViews(getPackageName(),

R.layout.appwidget_layout);

//设置其中控件的值

views.setTextViewText(R.id.content,"你好,我是widget!");

//传递给Widget管理服务

appWidgetManager.updateAppWidget(appWidgetIds, views);

}


AppWidgetProvider. onUpdate函数会在触发器组件的BroadcastReceiver.onReceive函数中调用,也就是说,如果AppWidgetProvider.onUpdate函数执行时间过长,会阻塞应用进程,从而导致提供的应用无法响应用户请求。

因此,如果RemoteViews的构造依赖于复杂计算、网络请求等耗时操作,需要将该流程转换成异步过程。在第3章曾经介绍过,由于触发器组件生命周期模型的限制,在触发器组件中异步执行操作,需要依赖于服务组件。使用Context.startService函数,可以将更新App Widget内容的请求转发至相关的服务组件,当服务组件执行完成该请求后,再构造RemoteViews对象通知宿主应用进行更新:


@Override

public void onUpdate(Context context,

AppWidgetManager appWidgetManager, int[]appWidgetIds){

//将请求转发至服务组件MyAppManagerService

Intent intent=new Intent(context,

MyAppManagerService.class);

intent.putExtra("widgets",appWidgetIds);

context.startService(intent);

}

//在服务组件完成了内容构造后,可以调用该函数来发送请求

public static void updateWidget(Context context,

int[]appWidgetIds, String content){

//传入该Widget对象的layout信息,构造RemoteViews对象

RemoteViews views=new RemoteViews(getPackageName(),

R.layout.appwidget_layout);

//设置其中控件的值

views.setTextViewText(R.id.content, content);

//传递给Widget管理服务

AppWidgetManager manager=

AppWidgetManager.getInstance(context);

manager.updateAppWidget(appWidgetIds, views);

}


当宿主应用收到对应的更新请求后,会根据RemoteViews对象中的内容构造交互界面。用来呈现RemoteViews对象内容的控件是android.appwidget.AppWidgetHostView,它派生自FrameLayout。AppWidgetHostView会根据RemoteViews中的Layout信息,先行构造出App Widget的控件树,然后再对RemoteViews中对应控件的相关信息和事件一一赋值。

App Widget的展示空间有限,交互方式也较为局限,因此RemoteViews的Layout有一些约束,只能支持线性容器控件、文本控件、图片控件等简单样式的控件,在设计中需要额外关注[1]

[1]关于App Widget控件的约束请参考SDK文档:http://develop.android.com/guide/topics/appwidgets/index.html#CreatingLayout。而App Widget的更多设计规范请参考:http://develop.android.com/guide/practices/ui_guidelines/widget_design.html。