赞
踩
1 App Widget简介
应用微件是可以嵌入其他应用(如主屏幕)并接收定期更新的微型应用视图。这些视图称为界面中的微件,您可以使用应用微件提供程序发布微件。能够容纳其他应用微件的应用组件称为应用微件托管应用。下面的屏幕截图显示了闹钟微件。
2 App Widg
要创建应用微件,您需要:
描述应用微件的元数据,如应用微件的布局、更新频率和 AppWidgetProvider 类。此对象应在 XML 中定义。
定义允许您基于广播事件以编程方式与应用微件连接的基本方法。通过它,您会在更新、启用、停用和删除应用微件时收到广播。
3 桌面小部件的开发步骤
1.在应用的 AndroidManifest.xml
文件中声明AppWidgetProvider类。
例如:
- <receiver android:name=".ExampleAppWidgetProvider" >
- <intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
- <action android:name="com.skywang.widget.UPDATE_ALL"/>
- </intent-filter>
- <meta-data android:name="android.appwidget.provider"
- android:resource="@xml/example_appwidget_info" />
- </receiver>
<receiver>元素需要android:name属性,该属性指定应用微件使用的AppWidgetProvider 。
<intent-filter>元素必须包含一个具有 android:name属性的<action>元素。此属性指定AppWidgetProvider接受 ACTION_APPWIDGET_UPDATE广播。这是您必须明确声明的唯一一项广播。AppWidgetManager 会根据需要自动将其他所有应用微件广播发送到AppWidgetProvider。
<meta-data>元素指定AppWidgetProviderInfo资源,并且需要以下属性:
android: name-指定元数据名称。使用android.appwidget.provider将数据标识为AppWidgetProviderInfo 描述符。
android :resource-指定AppWidgetProviderInfo资源位置。
2. 编辑AppWidgetProviderInfo对应的资源文件
- <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="40dp"
- android:minHeight="40dp"
- android:updatePeriodMillis="86400000"
- android:previewImage="@drawable/preview"
- android:initialLayout="@layout/example_appwidget"
- android:configure="com.example.android.ExampleAppWidgetConfigure"
- android:resizeMode="horizontal|vertical"
- android:widgetCategory="home_screen">
- </appwidget-provider>
<!-- android:minWidth : 最小宽度 android:minHeight : 最小高度 android:updatePeriodMillis : 更新widget的时间间隔(ms),"86400000"为1个小时 android:previewImage : 预览图片 android:initialLayout : 加载到桌面时对应的布局文件 android:resizeMode : widget可以被拉伸的方向。horizontal表示可以水平拉伸,vertical表示可以竖直拉伸 android:widgetCategory : widget可以被显示的位置。home_screen表示可以将widget添加到桌面,keyguard表示widget可以被添加到锁屏界面。 android:configure : 定义要在用户添加应用微件时启动以便用户配置应用微件属性的 Activity -->
3. 编辑example_appwidget.xml等资源文件
新建layout/example_appwidget.xml,代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="horizontal" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="HomeScreen Widget" />
-
- <Button
- android:id="@+id/btn_show"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Show" />
- </LinearLayout>
-
- <ImageView
- android:id="@+id/iv_show"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"/>
-
- </LinearLayout>
- 4. 编辑ExampleAppWidgetProvider.java
-
-
- public class ExampleAppWidgetProvider extends AppWidgetProvider {
- private static final String TAG = "ExampleAppWidgetProvider";
-
- private boolean DEBUG = false;
- // 启动ExampleAppWidgetService服务对应的action
- private final Intent EXAMPLE_SERVICE_INTENT =
- new Intent("android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE");
- // 更新 widget 的广播对应的action
- private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL";
- // 保存 widget 的id的HashSet,每新建一个 widget 都会为该 widget 分配一个 id。
- private static Set idsSet = new HashSet();
- // 按钮信息
- private static final int BUTTON_SHOW = 1;
- // 图片数组
- private static final int[] ARR_IMAGES = {
- R.drawable.ic_launcher,
- R.drawable.sample_0,
- R.drawable.sample_1,
- R.drawable.sample_2,
-
- };
-
- // onUpdate() 在更新 widget 时,被执行,
- @Override
- public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
- Log.d(TAG, "onUpdate(): appWidgetIds.length="+appWidgetIds.length);
-
- // 每次 widget 被创建时,对应的将widget的id添加到set中
- for (int appWidgetId : appWidgetIds) {
- idsSet.add(Integer.valueOf(appWidgetId));
- }
- prtSet();
- }
-
- // 当 widget 被初次添加 或者 当 widget 的大小被改变时,被调用
- @Override
- public void onAppWidgetOptionsChanged(Context context,
- AppWidgetManager appWidgetManager, int appWidgetId,
- Bundle newOptions) {
- Log.d(TAG, "onAppWidgetOptionsChanged");
- super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId,
- newOptions);
- }
-
- // widget被删除时调用
- @Override
- public void onDeleted(Context context, int[] appWidgetIds) {
- Log.d(TAG, "onDeleted(): appWidgetIds.length="+appWidgetIds.length);
-
- // 当 widget 被删除时,对应的删除set中保存的widget的id
- for (int appWidgetId : appWidgetIds) {
- idsSet.remove(Integer.valueOf(appWidgetId));
- }
- prtSet();
-
- super.onDeleted(context, appWidgetIds);
- }
-
- // 第一个widget被创建时调用
- @Override
- public void onEnabled(Context context) {
- Log.d(TAG, "onEnabled");
- // 在第一个 widget 被创建时,开启服务
- context.startService(EXAMPLE_SERVICE_INTENT);
-
- super.onEnabled(context);
- }
-
- // 最后一个widget被删除时调用
- @Override
- public void onDisabled(Context context) {
- Log.d(TAG, "onDisabled");
-
- // 在最后一个 widget 被删除时,终止服务
- context.stopService(EXAMPLE_SERVICE_INTENT);
-
- super.onDisabled(context);
- }
-
-
- // 接收广播的回调函数
- @Override
- public void onReceive(Context context, Intent intent) {
-
- final String action = intent.getAction();
- Log.d(TAG, "OnReceive:Action: " + action);
- if (ACTION_UPDATE_ALL.equals(action)) {
- // “更新”广播
- updateAllAppWidgets(context, AppWidgetManager.getInstance(context), idsSet);
- } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
- // “按钮点击”广播
- Uri data = intent.getData();
- int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
- if (buttonId == BUTTON_SHOW) {
- Log.d(TAG, "Button wifi clicked");
- Toast.makeText(context, "Button Clicked", Toast.LENGTH_SHORT).show();
- }
- }
-
- super.onReceive(context, intent);
- }
-
- // 更新所有的 widget
- private void updateAllAppWidgets(Context context, AppWidgetManager appWidgetManager, Set set) {
-
- Log.d(TAG, "updateAllAppWidgets(): size="+set.size());
-
- // widget 的id
- int appID;
- // 迭代器,用于遍历所有保存的widget的id
- Iterator it = set.iterator();
-
- while (it.hasNext()) {
- appID = ((Integer)it.next()).intValue();
- // 随机获取一张图片
- int index = (new java.util.Random().nextInt(ARR_IMAGES.length));
-
- if (DEBUG) Log.d(TAG, "onUpdate(): index="+index);
- // 获取 example_appwidget.xml 对应的RemoteViews
- RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
-
- // 设置显示图片
- remoteView.setImageViewResource(R.id.iv_show, ARR_IMAGES[index]);
-
- // 设置点击按钮对应的PendingIntent:即点击按钮时,发送广播。
- remoteView.setOnClickPendingIntent(R.id.btn_show, getPendingIntent(context,
- BUTTON_SHOW));
-
- // 更新 widget
- appWidgetManager.updateAppWidget(appID, remoteView);
- }
- }
-
- private PendingIntent getPendingIntent(Context context, int buttonId) {
- Intent intent = new Intent();
- intent.setClass(context, ExampleAppWidgetProvider.class);
- intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
- intent.setData(Uri.parse("custom:" + buttonId));
- PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0 );
- return pi;
- }
-
- // 调试用:遍历set
- private void prtSet() {
- if (DEBUG) {
- int index = 0;
- int size = idsSet.size();
- Iterator it = idsSet.iterator();
- Log.d(TAG, "total:"+size);
- while (it.hasNext()) {
- Log.d(TAG, index + " -- " + ((Integer)it.next()).intValue());
- }
- }
- }
- }

AppWidgetProvider重要的几个重写方法,比如onDeleted、onEnabled、onDisabled、onReceive、onUpdate方法的作用已经在代码里面做了注释
5. 编辑ExampleAppWidgetService.java
- public class ExampleAppWidgetService extends Service {
-
- private static final String TAG="ExampleAppWidgetService";
-
- // 更新 widget 的广播对应的action
- private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL";
- // 周期性更新 widget 的周期
- private static final int UPDATE_TIME = 5000;
- // 周期性更新 widget 的线程
- private UpdateThread mUpdateThread;
- private Context mContext;
- // 更新周期的计数
- private int count=0;
-
- @Override
- public void onCreate() {
-
- // 创建并开启线程UpdateThread
- mUpdateThread = new UpdateThread();
- mUpdateThread.start();
-
- mContext = this.getApplicationContext();
-
- super.onCreate();
- }
-
- @Override
- public void onDestroy(){
- // 中断线程,即结束线程。
- if (mUpdateThread != null) {
- mUpdateThread.interrupt();
- }
-
- super.onDestroy();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- /*
- * 服务开始时,即调用startService()时,onStartCommand()被执行。
- * onStartCommand() 这里的主要作用:
- * (01) 将 appWidgetIds 添加到队列sAppWidgetIds中
- * (02) 启动线程
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.d(TAG, "onStartCommand");
- super.onStartCommand(intent, flags, startId);
-
- return START_STICKY;
- }
-
- private class UpdateThread extends Thread {
-
- @Override
- public void run() {
- super.run();
-
- try {
- count = 0;
- while (true) {
- Log.d(TAG, "run ... count:"+count);
- count++;
-
- Intent updateIntent=new Intent(ACTION_UPDATE_ALL);
- mContext.sendBroadcast(updateIntent);
-
- Thread.sleep(UPDATE_TIME);
- }
- } catch (InterruptedException e) {
- // 将 InterruptedException 定义在while循环之外,意味着抛出 InterruptedException 异常时,终止线程。
- e.printStackTrace();
- }
- }
- }
- }

(01) onCreate() 在创建服务时被执行。它的作用是创建并启动线程UpdateThread()。
(02) onDestroy() 在销毁服务时被执行。它的作用是注销线程UpdateThread()。
(03) 服务UpdateThread 每隔5秒,发送1个广播ACTION_UPDATE_ALL。广播ACTION_UPDATE_ALL在ExampleAppWidgetProvider被处理:用来更新widget中的图片。
6.编译代码生成apk文件,将其安装到手机
7.长按手机的桌面(不同品牌手机可能操作方式不一样)会弹出桌面设置界面,选择添加工具
widget在添加到桌面前的效果图:
widget在添加到桌面后的效果图:
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。