標籤:android widget
官方有話這樣說:
A RemoteViews object (and, consequently, an App Widget) can support the following layout classes:
FrameLayout
LinearLayout
RelativeLayout
And the following widget classes:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
Descendants of these classes are not supported.不支援這些類的後代
接下來的樣本說明怎麼樣實現 使用ListView、GridView、StackView、ViewFlipper建立AppWidget
menifest
<receiver android:name="com.stone.receiver.WidgetSetProvider"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="com.stone.action.clickset"/> <action android:name="com.stone.action.clickset.item"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/set_widget_provider"/></receiver>
set_widget_provider.xml
<?xml version="1.0" encoding="utf-8"?><appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="250dp" android:minHeight="180dp" android:updatePeriodMillis="5000" android:previewImage="@drawable/ic_launcher" android:initialLayout="@layout/collections_view_widget" android:resizeMode="horizontal|vertical" android:autoAdvanceViewId="@id/viewflipper" ><!-- 計算size的公式: (70*n) -30 n為組件所需的大小(占幾格) 當前的就是 4x4minResizeWidth minResizeHeight 能被調整的最小寬高,若大於minWidth minHeight 則忽略 label選擇組件時看到標籤 icon選擇組件時看到表徵圖 updatePeriodMillis更新時間間隔 previewImage選擇組件時 展示的映像 3.0以上使用 initialLayout布局檔案 resizeMode調整size模式 configure如果需要在啟動前先啟動一個Activity進行設定,在這裡給出Activity的完整類名 [email protected]/xx與集合組件一起使用,指定該集合item自動推進 暫只發現對stackview有效,會自動一段時間推進到下一個 集合組件:3.0後才有。set view:ListView、GridView、StackView、AdapterViewFlipper ViewFlipper 為非集合組件 --></appwidget-provider>
widget使用的布局檔案 collections_view_widget.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="fill_parent" android:layout_height="wrap_content" > <Button android:id="@+id/btn_listview" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="listview" /> <Button android:id="@+id/btn_gridview" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="gridview" /> <Button android:id="@+id/btn_stackview" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="stackview" /> <Button android:id="@+id/btn_viewflipper" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="viewflipper" /> </LinearLayout> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#80000000" > /> <ListView android:id="@+id/listview" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <GridView android:id="@+id/gridview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:numColumns="2" android:visibility="gone" /> <StackView android:id="@+id/stackview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:visibility="gone" /> <ViewFlipper android:id="@+id/viewflipper" android:layout_width="fill_parent" android:layout_height="fill_parent" android:autoStart="true" android:flipInterval="2000" android:visibility="gone" ><!-- autoStart=true <==>startFlipping()startFlipping() <==> How long to wait before flipping to the next view --> <ImageView android:id="@+id/iv1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/a11"/> <ImageView android:id="@+id/iv2" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/a2"/> <ImageView android:id="@+id/iv3" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/a3" /> <ImageView android:id="@+id/iv4" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/a4" /> </ViewFlipper> <TextView android:id="@+id/tv_empty" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="Empty List" android:visibility="gone" /> </FrameLayout></LinearLayout>
集合的資料來源 需要 繼承 RemoteViewsService
package com.stone.service;import java.util.ArrayList;import java.util.List;import android.app.PendingIntent;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.widget.RemoteViews;import android.widget.RemoteViewsService;import com.stone.R;import com.stone.receiver.WidgetSetProvider;/** * 繼承自RemoteViewsService 必須重寫onGetViewFactory * 該服務只是用來 建立 集合widget使用的資料來源 * @author stone */public class WidgetSetService extends RemoteViewsService {public WidgetSetService() {}@Overridepublic RemoteViewsFactory onGetViewFactory(Intent intent) {return new WidgetFactory(this.getApplicationContext(), intent);}public class WidgetFactory implements RemoteViewsService.RemoteViewsFactory {private static final int mCount = 10;private Context mContext;private List<String> mWidgetItems = new ArrayList<String>();public WidgetFactory(Context context, Intent intent) { mContext = context;// mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,// AppWidgetManager.INVALID_APPWIDGET_ID); }@Overridepublic void onCreate() {for (int i = 0; i < mCount; i++) { mWidgetItems.add("item:" + i + "!"); }}@Overridepublic void onDataSetChanged() {/* * appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listview); * 使用該通知更新資料來源,會調用onDataSetChanged */System.out.println("----onDataSetChanged----");}@Overridepublic void onDestroy() {mWidgetItems.clear();}@Overridepublic int getCount() {return mCount;}@Overridepublic RemoteViews getViewAt(int position) {RemoteViews views = new RemoteViews(mContext.getPackageName(), android.R.layout.simple_list_item_1);views.setTextViewText(android.R.id.text1, "item:" + position);System.out.println("RemoteViewsService----getViewAt" + position);Bundle extras = new Bundle(); extras.putInt(WidgetSetProvider.EXTRA_ITEM, position); Intent fillInIntent = new Intent(); fillInIntent.putExtras(extras); /* * android.R.layout.simple_list_item_1 --- id --- text1 * listview的item click:將fillInIntent發送, * fillInIntent它預設的就有action 是provider中使用 setPendingIntentTemplate 設定的action */views.setOnClickFillInIntent(android.R.id.text1, fillInIntent);return views;}@Overridepublic RemoteViews getLoadingView() {/* 在更新介面的時候如果耗時就會顯示 正在載入... 的預設字樣,但是你可以更改這個介面 * 如果返回null 顯示預設介面 * 否則 載入自訂的,返回RemoteViews */return null;}@Overridepublic int getViewTypeCount() {return 1;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic boolean hasStableIds() {return false;}}}
widgetprovider
package com.stone.receiver;import com.stone.R;import com.stone.service.WidgetSetService;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.os.Bundle;import android.text.TextUtils;import android.text.format.DateUtils;import android.view.View;import android.widget.RemoteViews;import android.widget.Toast;import android.widget.ViewFlipper;/** * 使用了集合展示AppWidget * ListView、GridView、StackView 設定adapter,處理item點擊 * ViewFlipper 在RemoteViews中缺少支援,暫只能在它的布局檔案中設定 輪換效果 * 對於切換到哪一個子view的item事件不好處理,只能設定一個整體setPendingIntent * @author stone */public class WidgetSetProvider extends AppWidgetProvider {public final static String CLICK_ACTION = "com.stone.action.clickset";public final static String CLICK_ITEM_ACTION = "com.stone.action.clickset.item";public final static String EXTRA_ITEM = "extra_item";@Overridepublic void onReceive(Context context, Intent intent) {super.onReceive(context, intent);System.out.println(intent.getAction());if (TextUtils.equals(CLICK_ACTION, intent.getAction())) {int extraType = intent.getIntExtra("view_tag", 0);if (extraType > 0) {System.out.println("extra:::" + extraType);switch (extraType) {case 1:updateWidget(context, R.id.listview, R.id.gridview, R.id.stackview, R.id.viewflipper);break;case 2:updateWidget(context, R.id.gridview, R.id.listview, R.id.stackview, R.id.viewflipper);break;case 3:updateWidget(context, R.id.stackview, R.id.gridview, R.id.listview, R.id.viewflipper);break;case 4:updateWidget(context, R.id.viewflipper, R.id.gridview, R.id.stackview, R.id.listview);break;default:break;}} } else if (TextUtils.equals(CLICK_ITEM_ACTION, intent.getAction())) {Bundle extras = intent.getExtras();int position = extras.getInt(WidgetSetProvider.EXTRA_ITEM, -1);if (position != -1) {System.out.println("--點擊了item---" + position);System.out.println("");//Toast.makeText(context, "click item:" + position, 0).show();}}}@Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.collections_view_widget);Intent intent1 = new Intent(CLICK_ACTION);intent1.putExtra("view_tag", 1);PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 101, intent1, 0);views.setOnClickPendingIntent(R.id.btn_listview, pendingIntent1);Intent intent2 = new Intent(CLICK_ACTION);intent2.putExtra("view_tag", 2);PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 102, intent2, 0);views.setOnClickPendingIntent(R.id.btn_gridview, pendingIntent2);Intent intent3 = new Intent(CLICK_ACTION);intent3.putExtra("view_tag", 3);PendingIntent pendingIntent3 = PendingIntent.getBroadcast(context, 103, intent3, 0);views.setOnClickPendingIntent(R.id.btn_stackview, pendingIntent3);Intent intent4 = new Intent(CLICK_ACTION);intent4.putExtra("view_tag", 4);PendingIntent pendingIntent4 = PendingIntent.getBroadcast(context, 104, intent4, 0);views.setOnClickPendingIntent(R.id.btn_viewflipper, pendingIntent4);appWidgetManager.updateAppWidget(appWidgetIds, views);System.out.println("setwidget update");super.onUpdate(context, appWidgetManager, appWidgetIds);}@Overridepublic void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId,Bundle newOptions) {super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId,newOptions);}@Overridepublic void onDeleted(Context context, int[] appWidgetIds) {super.onDeleted(context, appWidgetIds);}@Overridepublic void onEnabled(Context context) {super.onEnabled(context);}@Overridepublic void onDisabled(Context context) {super.onDisabled(context);}private void updateWidget(Context context, int visible, int gone1, int gone2, int gone3) {//RemoteViews處理異進程中的ViewRemoteViews views = new RemoteViews(context.getPackageName(), R.layout.collections_view_widget);views.setViewVisibility(visible, View.VISIBLE);views.setViewVisibility(gone1, View.GONE);views.setViewVisibility(gone2, View.GONE);views.setViewVisibility(gone3, View.GONE);if (visible != R.id.viewflipper) {//viewflipper 不是 繼承自AbsListView or AdapterViewAnimator 的viewIntent intent = new Intent(context, WidgetSetService.class);views.setRemoteAdapter(visible, intent);//設定集合的adapter為intent指定的serviceviews.setEmptyView(visible, R.id.tv_empty);//指定集合view為空白時顯示的viewIntent toIntent = new Intent(CLICK_ITEM_ACTION);PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 200, toIntent, PendingIntent.FLAG_UPDATE_CURRENT);/* * setPendingIntentTemplate 設定pendingIntent 模板 * setOnClickFillInIntent 可以將fillInIntent 添加到pendingIntent中 */views.setPendingIntentTemplate(visible, pendingIntent);} else if (visible == R.id.viewflipper) {//views.setPendingIntentTemplate(R.id.viewflipper, pendingIntentTemplate);}AppWidgetManager am = AppWidgetManager.getInstance(context);int[] appWidgetIds = am.getAppWidgetIds(new ComponentName(context, WidgetSetProvider.class));for (int i = 0; i < appWidgetIds.length; i++) {am.updateAppWidget(appWidgetIds[i], views); //更新 執行個體}}}
啟動並執行周期函數
點擊stackview的