Android桌面小部件AppWidget(2)
在附錄文章1的基礎上,我再寫一篇關于Android AppWidget的文章,本篇文章實作一個簡單功能,此功能亦是對附錄文章1所實作功能的增強和改進,本文的代碼實作的功能:假設桌面小部件隻包含一個Button和一個TextView,當點選Button後,背景啟動一個服務(IntentService實作),該服務每個一秒發送一個簡單的字元串消息資料data,然後将此消息資料更新到桌面小部件的TextView裡面實時顯示。
這次,在Androidmanifest.xml有關receiver的定義中,與附錄文章1相比,将增加一個action:action_update。本例中,Button的按擊事件将觸發背景啟動服務,背景服務Service每隔一秒制造一個簡單字元串資料,然後将此資料實時的以廣播形式發給AppWidge,AppWidge收到後,就更新到桌面小部件的TextView裡面。
(1)定義AppWidget。
先在Androidmanifest.xml裡面定義APP widget的,以Android廣播形式:
<receiver android:name="zhangphil.widget.AppWidget" >
<intent-filter>
<action android:name="action_button" />
</intent-filter>
<intent-filter>
<action android:name="action_update" />
</intent-filter>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget" />
</receiver>
兩個用于廣播接收的action:action_button和action_update,其中action_button用于在桌面小部件接收使用者的點選事件,此action_button将随即啟動背景服務,而背景啟動的服務将發送廣播資料,資料中的廣播過濾器即是:action_update。
涉及到的res/xml目錄下的appwidget.xml代碼檔案:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/appwidget_layout"
android:minHeight="20dip"
android:minWidth="300dip"
android:previewImage="@drawable/ic_launcher"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="0"
android:widgetCategory="home_screen" >
</appwidget-provider>
(2)上次Java代碼實作視窗小部件。
核心的AppWidget.java代碼:
package zhangphil.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;
public class AppWidget extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.d(this.getClass().getName(), "onReceive");
if (intent == null)
return;
String action = intent.getAction();
if (action.equals(Constants.ACTION_UPDATE)) {
String data = intent.getStringExtra(Constants.KEY_DATA);
Log.d(Constants.KEY_DATA, data);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);
remoteViews.setTextViewText(R.id.text, data);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName componentName = new ComponentName(context, AppWidget.class);
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
// 點選了按鈕,開始啟動一個背景服務
if (action.equals(Constants.ACTION_BUTTON)) {
Intent serviceIntent = new Intent(context, MyService.class);
context.startService(serviceIntent);
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(this.getClass().getName(), "onUpdate");
Intent intent = new Intent(Constants.ACTION_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
// 小部件在Launcher桌面的布局
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);
// 事件
remoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent);
// 更新AppWidget
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
/**
* 删除AppWidget
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
Log.d(this.getClass().getName(), "onDeleted");
}
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
Log.d(this.getClass().getName(), "onDisabled");
}
/**
* AppWidget首次建立調用
*/
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
Log.d(this.getClass().getName(), "onEnabled");
}
}
RemoteViews用到的appwidget_layout.xml,appwidget_layout.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="wrap_content"
android:orientation="horizontal"
android:background="#33000000" >
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="啟動背景服務" >
</Button>
<TextView
android:id="@+id/text"
android:text="text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
(3)背景服務Service。
MyService.java代碼檔案,此service是一個Android IntentService,具體實作也可以是Service。此service功能簡單,是有桌面小部件的button按鈕觸發,然後在背景啟動,啟動後在一個for循環裡面循環産生一個簡單的字元串資料通過廣播形式廣播出去,注意打進去的廣播過濾器是:action_update。
package zhangphil.widget;
import android.app.IntentService;
import android.content.Intent;
public class MyService extends IntentService {
private static int ID = 0;
public MyService() {
super("ZhangPhilService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(Intent intent) {
myLongTimeTask(ID++);
}
private void myLongTimeTask(int id) {
for (int i = 0; i < 5; i++) {
Intent intent = new Intent(Constants.ACTION_UPDATE);
intent.putExtra(Constants.KEY_DATA, "Zhang Phil @ CSDN " + id + ":" + i);
sendBroadcast(intent);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
記得要将此service注冊到Androidmanifest.xml裡面:
<service android:name="zhangphil.widget.MyService" >
</service>
(4)公共變量的定義(次要)。
因為涉及到衆多公共變量的寫入和讀出,是以定義了一個單獨的Constants.java代碼類,專門定義公共的變量定義:
package zhangphil.widget;
public class Constants {
public static final String ACTION_BUTTON = "action_button";
public static final String ACTION_UPDATE = "action_update";
public static final String KEY_DATA = "data";
}
(5)完整的代碼結構。
如圖所示:
最終,代碼運作結果如圖所示:
附錄文章:
1,《Android桌面小部件AppWidget(1)》連結位址:
http://blog.csdn.net/zhangphil/article/details/50457355