一,寫在前面
在文章RemoteViews的基本使用(上)之通知欄 中講述了的RemoteViews使用場景之通知欄,這篇文章主要講述RemoteViews在窗口小部件中的使用。在寫好了一個窗口小部件之後,如果需要對小部件的界面進行更新,由於在本應用中無法調用findViewbyid(id)方法獲取控件引用(需要跨進程訪問界面),這個時候RemoteViews就派上用場了。在上篇文章中講到,RemoteViews可以實現跨進程更新界面,內部實現原理是Binder機制,後面會單獨更新一篇從源碼角度分析RemoteViews。這篇文章主要介紹RemoteViews在窗口小部件的使用,並介紹窗口小部件的簡單實現。
二,窗口小部件
AppWidgetProvider的註冊
<receiver android:name="com.example.appwidgetdemo.MyAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!-- 註冊接受點擊Button發送的廣播 -->
<action android:name="com.widget.wang"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/my_appwidget_info" />
</receiver>
AppWidgetProviderInfo對應資源文件
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp"
android:minHeight="110dp"
android:previewImage="@drawable/ic_launcher"
android:initialLayout="@layout/my_appwidget">
</appwidget-provider>
分析:在創建該資源文件時,選擇Resource Type爲AppWidget Provider,這樣會自動生成一個帶有appwidget-provider標籤的文件,手寫亦可。layout佈局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="#f00"
android:layout_centerHorizontal="true"
android:text="TextView"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ttnkh"/>
<Button
android:id="@+id/btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:text="Button"/>
</LinearLayout>
</RelativeLayout>
AppWidgetProvider的生命週期
public void onReceive(Context context, Intent intent) {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to crash).
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
}
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
this.onDeleted(context, new int[] { appWidgetId });
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
&& extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
Bundle widgetExtras = extras.getBundle(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS);
this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
appWidgetId, widgetExtras);
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
this.onEnabled(context);
}
else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
this.onDisabled(context);
}
}
public class MyAppWidgetProvider extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if ("com.widget.wang".equals(intent.getAction())) {
//接受點擊按鈕後發送的廣播,將RemoteView的圖片設置爲R.drawable.ic_launcher
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.my_appwidget);
rv.setImageViewResource(R.id.iv, R.drawable.ic_launcher);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName provider = new ComponentName(context, MyAppWidgetProvider.class);
appWidgetManager.updateAppWidget(provider, rv);
}
}
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
Log.e("MyAppWidgetProvider", "call onEnabled");
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
Log.e("MyAppWidgetProvider", "call onUpdate");
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.my_appwidget);
rv.setTextViewText(R.id.tv, "更新窗口小部件界面");
rv.setTextColor(R.id.tv, Color.WHITE);
Intent intent = new Intent();
intent.setAction("com.widget.wang");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// 給Button設置點擊事件,觸發一個Intent,這裏是發送廣播
rv.setOnClickPendingIntent(R.id.btn, pi);
appWidgetManager.updateAppWidget(appWidgetIds, rv);
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
Log.e("MyAppWidgetProvider", "call onDeleted");
}
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
Log.e("MyAppWidgetProvider", "call onDisabled");
}
}
驗證窗口小部件的生命週期方法的調用,執行這樣一些操作:添加小部件到桌面 -> 添加小部件到桌面->刪除小部件->刪除小部件。查看日誌如下: