最近下定决心潜心学习下Android的基础,开始研读Android的官方文档,先学习下平时不太常用的组件——App Widgets。
那既然要学习App Widgets,首先需要知道App Widgets到底是什么,那我们先来看看官网上的定义:App Widgets are miniature application views that can be embedded in other applications (such as the Home screen) and receive periodic updates. 翻译成中文:App Widgets是可以嵌入到其他应用,如桌面,并能接收到周期性更新的微型应用视图。有了这个定义很清楚的就明白了,App Widgets是什么了,它就是一个小组件。那么怎么实现App Widgets呢?
如果希望实现点击桌面上的小组件,打开应用程序,实现步骤:
完成普通应用程序的相关开发,例如笔者写一个简单的音乐播放器;
在AndroidManifest.xml中,加入以下代码:
<receiver android:name="ExampleAppWidgetProvider" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/example_appwidget_info" /> </receiver>
<receiver>元素需要android:name属性,它指定了App Widget使用的AppWidgetProvider 。
<intent-filter>元素必须包括一个含有android:name属性的<action>元素。该元素指定AppWidgetProvider接受ACTION_APPWIDGET_UPDATE 广播。这是唯一你必须显式声明的广播。当需要的时候,AppWidgetManager 会自动发送所有其他App Widget广播给AppWidgetProvider。
<meta-data> 元素指定了AppWidgetProviderInfo 资源并需要以下属性:
- android:name – 指定元数据名称。
- android:resource – 指定AppWidgetProviderInfo 资源路径。
在layout中创建小组件的视图xml,example_appwidget.xml,如本例中笔者编写的视图中包括TextView和ImageButton,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="56dp" android:orientation="horizontal" android:background="#ffffffff" android:padding="@dimen/widget_margin"> <TextView android:id="@+id/app_widget_text" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@string/main" /> <ImageButton android:id="@+id/app_widget_play" android:layout_width="@dimen/my_button_width" android:layout_height="match_parent" android:contentDescription="@string/play" android:src="@drawable/ic_action_play" /> </LinearLayout>
其中在res/values/dimens.xml中:
<dimen name=”widget_margin”>8dp</dimen>
<dimen name=”my_button_width”>32dp</dimen>
创建xml目录,在xml目录中创建example_appwidget_info.xml,加入如下代码:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="216dp" android:minHeight="56dp" android:updatePeriodMillis="86400000" android:initialLayout="@layout/example_appwidget"> </appwidget-provider>
minWidth和minHeight属性的值指定了这个App Widget布局需要的默认最小区域。默认的App Widgets所在窗口的桌面位置基于有确切高度和宽度的单元网格。如果App Widget的最小长宽和这些网格单元的尺寸不匹配,那么这个App Widget将收缩到最接近的单元尺寸。关于尺寸具体怎样设计比较好,可参考点击此处。
updatePerdiodMillis 属性定义了App Widget框架调用onUpdate()方法来从AppWidgetProvider请求一次更新的频度。实际更新时间并不那么精确,而且我们建议更新频率越低越好-也许每小时不超过一次以节省电源。你也许还会允许用户在配置中调整这个频率-一些人可能想每15分钟一次股票报价,或者一天只要四次。
initialLayout属性指向定义App Widget布局的资源。
创建类ExampleAppWidgetProvider,代码如下:
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final int N = appWidgetIds.length; // Perform this loop procedure for each App Widget that belongs to this provider for (int i=0; i<N; i++) { int appWidgetId = appWidgetIds[i]; // Create an Intent to launch ExampleActivity Intent intent = new Intent(context, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); // Get the layout for the App Widget and attach an on-click listener to the button RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget); views.setOnClickPendingIntent(R.id.app_widget_text, pendingIntent); Intent intentClcik = new Intent(context, PlayMusicService.class); PendingIntent pendingIntentClcik = PendingIntent.getService(context, 0, intentClcik, 0); views.setOnClickPendingIntent(R.id.app_widget_play, pendingIntentClcik); // Tell the AppWidgetManager to perform an update on the current app widget appWidgetManager.updateAppWidget(appWidgetId, views); } } }
AppWidgetProvider类是处理App Widget广播的,继承于BroadcastReceiver。AppWidgetProvider仅能接收到App Widget相关的广播,如更新,删除,启用,禁用。相应会调用如下方法:
- onUpdate()
这个方法调用来间隔性的更新App Widget,间隔时间用AppWidgetProviderInfo 里的updatePeriodMillis属性定义。这个方法也会在用户添加App Widget时被调用,因此它应该执行基础的设置,比如为视图定义事件处理器并启动一个临时的服务Service,如果需要的话。但是,如果你已经声明了一个配置活动,这个方法在用户添加App Widget时将不会被调用,而只在后续更新时被调用。配置活动应该在配置完成时负责执行第一次更新。
- onAppWidgetOptionsChanged()
当小组件第一次被放置在宿主或者被重新定义尺寸时调用。你可以使用这个回调来展示或者隐藏基于小组件尺寸范围的内容。你可以通过调用getAppWidgetOptions()方法来获得尺寸范围,getAppWidgetOptions()方法会返回一个Bundle,包括有:
OPTION_APPWIDGET_MIN_WIDTH—以dp为单位的小组件实例的最小宽度 OPTION_APPWIDGET_MIN_HEIGHT—以dp为单位的小组件实例的最小高度 OPTION_APPWIDGET_MAX_WIDTH—以dp为单位的小组件实例的最大宽度 OPTION_APPWIDGET_MAX_HEIGHT—以dp为单位的小组件实例的最大高度
这个回调API Level16(Android 4.1)以上支持,如果实现了该方法,请确保应用不依赖于此方法,因为在比较老的机器上该方法无法被调用。
- onDeleted(Context, int[])
每次从宿主中删除App Widget实例时调用。
- onEnabled(Context)
当AppWidget实例第一次创建时调用。例如,如果用户添加两个App Widget实例,只会调用一次该方法。如果需要打开一个新的数据库或者完成对所有App Widget实例只进行一次的其他设置,可以放在这里执行。
- onDisabled(Context)
当你的App Widget的最后一个实例被从宿主中删除时被调用。你应该在onEnabled(Context)中做一些清理工作,比如删除一个临时的数据库。
- onReceive(Context, Intent)
每次广播均会调用此方法,并且在上述其他方法之前被调用。一般不需要实现该方法,因为默认的AppWidgetProvider可以过滤出所有的App Widget广播,并且调用以上方法。
最重要的AppWidgetProvider 回调函数是onUpdated(), 因为它是在每个App Widget添加进宿主时被调用的(除非你使用一个配置活动)。如果你的App Widget 要接受任何用户交互事件,那么你需要在这个回调函数中注册事件处理器。如果你的App Widget不创建临时文件或数据库,或者执行其它需要清理的工作,那么onUpdated() 可能是你需要定义的唯一的回调函数。
所以在本程序中,笔者仅实现了onUpdate()方法。
调用PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags)方法,可以启动一个新的Activity,调用PendingIntent.getService(Context context, int requestCode, Intent intent, int flags)方法,可以启动一个新的Service。
这里点击程序入口的TextView可以启动程序的MainActivity,点击播放的ImageButton,可以启动播放音乐的Service。