天天看點

AppWidget應用小部件詳解(二)

AppWidget應用小部件詳解(二)

在上一篇的 AppWidget應用小部件詳解(一)中,介紹了如何自定義實作一個簡單的App Widget應用小部件的步驟,而在本篇中将繼續介紹自定義App Widget的建立,這次将介紹在應用小部件中顯示一個清單容器視圖,比如ListView、GridView、StackView、AdapterViewFlipper。接下來就實作一個基于該模式的 圖檔浏覽的App Widget 。                   其實建立應用小部件的步驟都一樣,隻是在呈現小部件内容的處理方式上面不同罷了,現在就開始實作該圖檔浏覽的小部件 (基于android系統4.1)。         1、首先建立AppWidgetProviderInfo對象,即在res/xml目錄下建立一個appwidget_provider_image.xml,該檔案的内容對于所有的App Widget基本上大體相同,其代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
		android:minWidth="93dp"
		android:minHeight="93dp"
		android:updatePeriodMillis="86400000"
		android:initialLayout="@layout/appwidget_provider_image" />
           

2、接着建立App Widget的布局檔案,用于顯示App Widget,該檔案中有兩個顯示的view,StackView就是圖檔浏覽的主要view,ImageView隻是當StackView中沒有内容時才會顯示出來,其檔案如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <StackView 
        android:id="@+id/stack_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:loopViews="true" />
    
    <ImageView 
        android:id="@+id/empty_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/stack_img_empty"
        android:contentDescription="@string/app_name"/>

</FrameLayout>
           

  3、然後建立AppWidgetProvider的實作類StackViewAppWidgetProvider,用來接收App Widget釋出的廣播,根據廣播進行相關的建立、更新App Widget等操作,與之前數字時鐘的AppWidgetProvider實作類有所不同,StackView中顯示的資料不能在目前的StackViewAppWidgetProvider類中去設定,必須通過設定RemoteAdapter來啟動一個提供資料的StackRemoteViewsService服務;并且如果需要使StackView中的每一個子view都有自己獨立的行為動作,就必須首先在承載該StackView的RemoteViews中設定一個PenddingIntentTemplate,對于觸發的子view就可以通過這個PenddingIntentTemplate去執行其中的Intent,然後在StackRemoteViewsFactory類的getViewAt方法中為每一個子view設定一個FillInIntent,進而攜帶不同的資料,這樣就可以在每一個子view上觸發事件,通過相應的處理類進行處理,其核心代碼如下:

public static final String TOAST_ACTION = "com.android.stackviewappwidget.TOAST_ACTION";
	public static final String EXTRA_ITEM = "com.android.stackviewappwidget.EXTRA_ITEM";

	 /**
	  * 廣播接收處理
	  * 當intent的 action為TOAST_ACTION時,就進行相應的界面更新處理?
	  * (點選stackview的哪一個子view就将其顯示在最前面)
	  */
	@Override
	public void onReceive(Context context, Intent intent) {
		super.onReceive(context, intent);
		if(intent.getAction().equals(TOAST_ACTION))
		{
			// 建立AppWidgetManager對象
			AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
			
			// 獲得應用小部件的id
			int appWidgetId = intent.getIntExtra(
					AppWidgetManager.EXTRA_APPWIDGET_ID,
					AppWidgetManager.INVALID_APPWIDGET_ID);


			// 擷取目前點選的stackview子view的位置
			int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);


			// 根據view的位置進行界面的更新,将該view顯示在stackview的最前面
			// 建立RemoteViews對象
			RemoteViews views = createRemoteViews(context, appWidgetId);
			// 顯示目前點選的子view
			views.setDisplayedChild(R.id.stack_image, viewIndex);


			// 更新AppWidget
			appWidgetManager.updateAppWidget(appWidgetId, views);
		}
	}


	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {
		super.onUpdate(context, appWidgetManager, appWidgetIds);
		
		for(int i=0;i<appWidgetIds.length;i++)
		{
			// 構造展示圖檔浏覽的RemoteViews對象
			RemoteViews views = createRemoteViews(context, appWidgetIds[i]);
			// 更新目前的App Widget
			appWidgetManager.updateAppWidget(appWidgetIds[i], views);
		}
		
	}
	
	/**
	 * 構造展示圖檔浏覽的RemoteViews對象
	 * @return
	 */
	private RemoteViews createRemoteViews(Context context, int appWidgetId)
	{
		// 建立RemoteViews對象,用于widget布局
		RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_image);
		
		// 建立啟動v
		Intent intent = new Intent(context, StackRemoteViewsService.class);
		// 将app widget id 添加到intent extras中
		intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
		intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
		
		// 給views設定RemoteViews擴充卡
		// 該擴充卡通過intent來連接配接RemoteViewsService,獲得展示的資料
		views.setRemoteAdapter(R.id.stack_image, intent);
		
		// 如果StackView的資料為空,則顯示empty_image視圖
		views.setEmptyView(R.id.stack_image, R.id.empty_image);
		
		/**
		 * 建立PendingIntent模版:給子view添加獨立的觸發事件
		 * 一旦點選子view之後就會發送一個帶TOAST_ACTION的廣播
		 */
		// 建立Intent對象,用來啟動StackViewAppWidgetProvider
		Intent toastIntent = new Intent(context, StackViewAppWidgetProvider.class);
		
		// 設定Intent的Action,啟動相應元件的辨別
		toastIntent.setAction(TOAST_ACTION);
		// 将app widget id 添加到intent extras中
		toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
		toastIntent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
		// 根據toastIntent建立一個啟動廣播接收器的PendingIntent模版
		PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
		// 将PendingIntent模版添加到RemoteViews中
		views.setPendingIntentTemplate(R.id.stack_image, toastPendingIntent);
		
		return views;
	}
           

4、建立為RemoteViewsAdapter提供資料的StackRemoteViewsService服務,該服務中的核心就是RemoteViewsFactory的實作類StackRemoteViewsFactory,該類其實類似于一個自定義的BaseAdapter,為了讓StackRemoteViewsFactory能夠生成顯示一個view就必須實作幾個核心的方法getCount、getViewAt、onCreate等。其核心代碼如下:

public class StackRemoteViewsService extends RemoteViewsService {
	
	// 建立RemoteViewsFactory對象
	@Override
	public RemoteViewsFactory onGetViewFactory(Intent intent) {
		return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
	}

	/**
	 * StackView中RemoteViews構造器
	 * @author Administrator
	 *
	 */
	class StackRemoteViewsFactory implements RemoteViewsFactory
	{
		private Context mContext;
		// stack圖檔
		private int[] stackImages;
		
		public StackRemoteViewsFactory(Context applicationContext, Intent intent) {
			this.mContext = applicationContext;
		}

		@Override
		public int getCount() {
			return stackImages.length;
		}

		@Override
		public long getItemId(int position) {
			return 0;
		}

		@Override
		public RemoteViews getLoadingView() {
			return null;
		}

		@Override
		public RemoteViews getViewAt(int position) {
			// 建立RemoteViews對象
			RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.appwidget_provider_item);
			
			// 設定RemoteViews中顯示的圖檔資訊
			views.setImageViewResource(R.id.image_item, stackImages[position]);
			
			// 建立Intent對象,用來為每一個子view攜帶相關的資料
			Intent fillInIntent = new Intent();
			fillInIntent.putExtra(StackViewAppWidgetProvider.EXTRA_ITEM, position);
			// 通過設定setOnClickFillInIntent方法,結合PengdingIntentTemplate就可以實作子view的觸發事件
			views.setOnClickFillInIntent(R.id.image_item, fillInIntent);
			
			return views;
		}

		@Override
		public int getViewTypeCount() {
			return 0;
		}

		@Override
		public boolean hasStableIds() {
			return false;
		}

		@Override
		public void onCreate() {
			// 初始化stack圖檔
			stackImages = new int[]{R.drawable.stack_img_1, R.drawable.stack_img_2, R.drawable.stack_img_3,
					 				R.drawable.stack_img_4, R.drawable.stack_img_5, R.drawable.stack_img_6,
					 				R.drawable.stack_img_7};
		}

		@Override
		public void onDataSetChanged() {
			
		}

		@Override
		public void onDestroy() {
			
		}
		
	}
}
           

5、最後将StackViewAppWidgetProvider類以及StackRemoteViewsService注冊到AndroidManifest.xml配置檔案中,其檔案如下:

<!-- 處理StackViewAppWidget的廣播接收器 -->
<receiver android:name=".StackViewAppWidgetProvider"
            android:label="@string/image_view"
            android:icon="@drawable/image_view">
            
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="com.android.stackviewappwidget.TOAST_ACTION" />
            </intent-filter>
            
            <meta-data android:name="android.appwidget.provider"
                android:resource="@xml/appwidget_provider_image"/>
        </receiver>
        
        <!-- 提供集合資料的服務:用于綁定remoteviews視圖 -->
 <service android:name=".StackRemoteViewsService" 
            android:permission="android.permission.BIND_REMOTEVIEWS"/>
           

6、通過以上幾步,自定義圖檔浏覽的App Widget就建立成功了,由于是運作在android4.1系統上面的,将應用小部件添加到手機桌面的方式和将應用快捷鍵添加到手機桌面的方式一樣,其步驟以及運作結果如下所示:         點選手機螢幕下方的菜單按鈕,進入所有顯示應用界面:        

AppWidget應用小部件詳解(二)

        通過向右滑動螢幕,一般到最後一個螢幕的界面就可以找到我們自定義的圖檔浏覽的App Widget:         

AppWidget應用小部件詳解(二)

        通過長按該自定義的圖檔浏覽的App Widget的圖示,就可以将其拖到手機桌面進行顯示:

AppWidget應用小部件詳解(二)