天天看點

Android Widget 開發詳解(二) +支援listView滑動的widget 一 建立AppWidgetProvider 二 建立RemoteViewsFactory 三  RemoteViewsService 四 增加widet基礎屬性配置

轉載請标明出處:http://blog.csdn.net/sk719887916/article/details/47027263

不少開發項目中都會有widget功能,别小瞧了它,他也是android的七大元件之一,對widget陌生的朋友可以閱讀下我的上篇文章< Android Widget工作原理詳解(一)>關于内部的介紹,還沒掌握的同學不要擔心,開發AppWidget套路很簡單,今天我們就實作一個可以加入listView滑動的widget,熟悉下一個普通widget的開發步驟。

一 建立AppWidgetProvider

此類是widget的控制核心,主要控制添加,删除,更新等。他是Broadcast的子類,可以擁有廣播的一切特性。 建立 MyAppListWidgetProvider類繼承AppWidgetProvider,實作其一下方法,onUpdate(),onReceive(), onEnabled(Context context) , onDeleted(), onDisabled()後面三方法可選而不可選。

1 onUpdate() 此方法一般處理widget的建立布局和更新UI操作,當widget添加到桌面會觸發onUpdate()方法,接下我們可以在此裡通過擷取remoteViews來給widget加載一個布局,遠端視圖前面也說過,它是widget的資源管理工具,我們可以用來給widget轉換一個它支援布局,僅支援特定的view,下面我為它加載一個listView,在widget上給某個控件設定點選事件采用PendingIntent,通過new一個延時意圖,然後 remoteViews.setOnClickPendingIntent( )來注冊點選事件。更新布局可以獲得用WidgetManager..updateAppWidget(thisWidget, remoteViews)來加載或更新widget布局,也可以通過onReceive() 收到一個自定義的廣播來調用此方法更新布局也可以。

[java]  view plain  copy

  1. @Override  

[java]  view plain  copy

  1. public void onUpdate(Context context, AppWidgetManager appWidgetManager,  
  2.         int[] appWidgetIds) {  
  3.     // 擷取Widget的元件名  
  4.     ComponentName thisWidget = new ComponentName(context,  
  5.             MyAppListWidgetProvider.class);  
  6.     // 建立一個RemoteView  
  7.     RemoteViews remoteViews = new RemoteViews(context.getPackageName(),  
  8.             R.layout.my_widget_layout);  
  9.     // 把這個Widget綁定到RemoteViewsService  
  10.     Intent intent = new Intent(context, MyRemoteViewsService.class);  
  11.     intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[0]);  
  12.     // 設定擴充卡  
  13.     remoteViews.setRemoteAdapter(R.id.widget_list, intent);  
  14.     // 設定當顯示的widget_list為空顯示的View  
  15.     remoteViews.setEmptyView(R.id.widget_list, R.layout.none_data);  
  16.     // 點選清單觸發事件  
  17.     Intent clickIntent = new Intent(context, MyAppListWidgetProvider.class);  
  18.     // 設定Action,友善在onReceive中差別點選事件  
  19.     clickIntent.setAction(clickAction);  
  20.     clickIntent.setData(Uri.parse(clickIntent.toUri(Intent.URI_INTENT_SCHEME)));  
  21.     PendingIntent pendingIntentTemplate = PendingIntent.getBroadcast(  
  22.             context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);  
  23.     remoteViews.setPendingIntentTemplate(R.id.widget_list,  
  24.             pendingIntentTemplate);  
  25.     // 重新整理按鈕  
  26.     final Intent refreshIntent = new Intent(context,  
  27.             MyAppListWidgetProvider.class);  
  28.     refreshIntent.setAction("refresh");  
  29.     final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(  
  30.             context, 0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);  
  31.     remoteViews.setOnClickPendingIntent(R.id.button_refresh,  
  32.             refreshPendingIntent);  
  33.     // 更新Wdiget  
  34.     appWidgetManager.updateAppWidget(thisWidget, remoteViews);  
  35. }  

            2  onReceive()

        此方功類似廣播的onReceive()用發,用開接收和處理廣播,如果我們在manifest.xml注冊了MyAppListWidgetProvider為一個appwidget,那麼不必須為此廣播加上widget标示,添加一action:<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />,下面的 <meta-data>标簽用來定義widget的屬性,指定一個widget描述資訊,具體釋義請閱讀 上篇widget原理詳解文章,

[html]  view plain  copy

  1. <!-- Widget必須添加到manifest檔案中,和Broadcaset Receiver一樣使用“receiver”标簽 -->  
  2.        <receiver android:name=".MyAppListWidgetProvider" >  
  3.            <!-- 此處設定Wdiget更新動作 -->  
  4.            <intent-filter>  
  5.                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
  6.            </intent-filter>  
  7.            <!-- 此處設定Widget的描述資源res/xml/my_widget.xml -->  
  8.            <meta-data  
  9.                android:name="android.appwidget.provider"  
  10.                android:resource="@xml/widget_info"   
  11.               >  
  12.            </meta-data>  
  13.        </receiver>  

  onReceive裡處理代碼邏輯,比如我這裡用來接收widget的用來更新我們在onUpdate()給重新整理按鈕定義的點選事件,處理重新整理界面需求,   [java]  view plain  copy

  1.     @Override  
  2.     public void onReceive(Context context, Intent intent) {  
  3.         super.onReceive(context, intent);  
  4.         String action = intent.getAction();  
  5.         if (action.equals("refresh")) {  
  6.             // 重新整理Widget  
  7.             final AppWidgetManager mgr = AppWidgetManager.getInstance(context);  
  8.             final ComponentName cn = new ComponentName(context,  
  9.                     MyAppListWidgetProvider.class);  
  10.             MyRemoteViewsFactory.mList.add("音樂"+i);  
  11.             // 這句話會調用RemoteViewSerivce中RemoteViewsFactory的onDataSetChanged()方法。  
  12.             mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn),  
  13.                     R.id.widget_list);  
  14.         } else if (action.equals(clickAction)) {  
  15.             // 單擊Wdiget中ListView的某一項會顯示一個Toast提示。  
  16.             Toast.makeText(context, intent.getStringExtra("content"),  
  17.                     Toast.LENGTH_SHORT).show();  
  18.         }  
  19.         i=i+1;  
  20.     }  

    3 onEnabled(Context context) 

    當wdiget可用正在拖進桌面時觸發,這裡我們一般可以進行一些變量初始的工作     [java]  view plain  copy

  1. @Override  
  2.    public void onEnabled(Context context) {  
  3.     // TODO Auto-generated method stub  
  4.     super.onEnabled(context);  
  5.        Toast.makeText(context, "使用者将widget添加桌面了",  
  6.                Toast.LENGTH_SHORT).show();  
  7.    }  

 4 onDeleted(),     widget被删除了,這裡我們通常用于釋放一些對象和視圖資源,便于防止記憶體洩露。

  [java]  view plain  copy

  1. @Override  
  2.  public void onDeleted(Context context, int[] appWidgetIds) {  
  3.     // TODO Auto-generated method stub。  
  4.      Toast.makeText(context, "使用者将widget從桌面移除了",  
  5.              Toast.LENGTH_SHORT).show();  
  6.     super.onDeleted(context, appWidgetIds);  
  7.  }  

5  onDisabled()    widget在被拖動的時候觸發,這是widget是無法點選的,當停止拖動操作時widget可用。

二 建立RemoteViewsFactory

   遠端視圖工廠,用來傳回Remoteviews,其通過adpter進行工作的,這裡我們調用  MyRemoteViewsFactory.mList.add()方法;給widget上的Listview加入資料。

[java]  view plain  copy

  1. public class MyRemoteViewsFactory implements RemoteViewsFactory {  
  2.      private final Context mContext;  
  3.         public static List<String> mList = new ArrayList<String>();  
  4.         public MyRemoteViewsFactory(Context context, Intent intent) {  
  5.             mContext = context;  
  6.         }  
  7.         @Override  
  8.         public void onCreate() {  
  9.             // 需要顯示的資料  
  10.             mList.add("");  
  11.             for (int i = 0; i < 5; i++) {  
  12.                   mList.add("item"+ i);  
  13.             }  
  14.         }  
  15.         @Override  
  16.         public void onDataSetChanged() {  
  17.         }  
  18.         @Override  
  19.         public void onDestroy() {  
  20.             mList.clear();  
  21.         }  
  22.         @Override  
  23.         public int getCount() {  
  24.             return mList.size();  
  25.         }  
  26.         @Override  
  27.         public RemoteViews getViewAt(int position) {  
  28.             if (position < 0 || position >= mList.size())  
  29.                 return null;  
  30.             String content = mList.get(position);  
  31.             // 建立在目前索引位置要顯示的View  
  32.             final RemoteViews rv = new RemoteViews(mContext.getPackageName(),  
  33.                     R.layout.my_widget_layout_item);  
  34.             // 設定要顯示的内容  
  35.             rv.setTextViewText(R.id.widget_list_item_tv, content);  
  36.             // 填充Intent,填充在AppWdigetProvider中建立的PendingIntent  
  37.             Intent intent = new Intent();  
  38.             // 傳入點選行的資料  
  39.             intent.putExtra("content", content);  
  40.             rv.setOnClickFillInIntent(R.id.widget_list_item_tv, intent);  
  41.             return rv;  
  42.         }  
  43.         @Override  
  44.         public RemoteViews getLoadingView() {  
  45.             return null;  
  46.         }  
  47.         @Override  
  48.         public int getViewTypeCount() {  
  49.             return 1;  
  50.         }  
  51.         @Override  
  52.         public long getItemId(int position) {  
  53.             return position;  
  54.         }  
  55.         @Override  
  56.         public boolean hasStableIds() {  
  57.             return true;  
  58.         }  

三  RemoteViewsService

RemoteViewsService子類提供了RemoteViewsFactory用于填充遠端集合視圖。

具體地說,其子類RemoteViewsService是一個遠端的服務擴充卡 可以請求RemoteViews,管理RemoteViews的服務。我們繼承RemoteViewsService來獲得一個視圖工廠,

[java]  view plain  copy

  1. @TargetApi(Build.VERSION_CODES.HONEYCOMB)  
  2. public class MyRemoteViewsService extends RemoteViewsService {  
  3.     @Override  
  4.     public RemoteViewsFactory onGetViewFactory(Intent intent) {  
  5.            return new MyRemoteViewsFactory(this.getApplicationContext(), intent);  
  6.     }  
  7. }  

四 增加widet基礎屬性配置

  1  添加widget描述檔案           我們為widget新增一個描述xml,在res/下建立一個xml檔案目錄,然後建立widget_info.xml檔案,具體如下    給widget指定了最小的寬高和浏覽的基礎視圖,包括其具體的布局檔案。具體介紹請看上篇文章 --- widget原理詳解。

   [java]  view plain  copy

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:initialLayout="@layout/my_widget_layout"  
  4.     android:minHeight="120dp"  
  5.     android:minWidth="280dp"  
  6.     android:previewImage="@drawable/ic_launcher"  
  7.     android:resizeMode="horizontal|vertical"  
  8.     android:updatePeriodMillis="0" >  
  9.     <!--  
  10.         sdk1.5之後updatePeriodMillis已失效,置為0,循環執行自行在代碼中實作。  
  11.         至于其他屬性可以查一下。在其他随筆中我也給出了  
  12.     -->  
  13. </appwidget-provider>  

    2  建立widget資源檔案xml          為widget建立一個實際要加載,也就是我直覺的看到的視圖布局。此布局通過widgetInfo的android:initialLayout="@layout/my_widget_layout"

屬性來指定。而widget描述資訊我們在 manifest.xml中 用<meta-data>标簽用來指定。

[html]  view plain  copy

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="200dp"  
  5.     android:background="@android:color/white"  
  6.     android:orientation="vertical" >  
  7.     <Button  
  8.         android:id="@+id/button_refresh"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_gravity="center_horizontal"  
  12.          android:background="@drawable/lite_widget_item_choosed_background_icon"      
  13.         android:textColor="@android:color/white"  
  14.         android:layout_marginTop="2dp"  
  15.         android:text="重新整理" />  
  16.     <ListView  
  17.         android:id="@+id/widget_list"  
  18.         android:layout_width="match_parent"  
  19.         android:layout_height="wrap_content"  
  20.         android:cacheColorHint="#00000000"  
  21.         android:scrollbars="none" />  
  22.     <!-- 此處的ListView 可以換成StackView或者GridView -->  
  23. </LinearLayout>  

    通過以上的步驟我們簡單的實作了一個widget,用于初學者學習和交流,比較複雜的widget邏輯我們還會加入網絡通路功能,和一些和sevice進行資料互動,如果想要widget實作自動加入到桌面,或者widget支援自定義控件的話,第一可以将我的app變成系統app,第二,采用重寫 Remoteviews來支援我們自定義的view,具體實作邏輯後面再介紹,謝謝閱讀。

繼續閱讀