android 中 AppWidget 的 ListView 的實現

http://www.cnblogs.com/debugman/archive/2012/06/18/android.html

3.0 以後系統直接支持了ListView. 關於ListView 的國內資料匱乏,大多數例子都是轉來轉去。由於初學android, 鄙人在搜索資料的時候遇到了不少麻煩~很是鬱悶和苦惱~深感國內學習氛圍確實怪異,學習方式需要改變。應該多去查看官方文檔。。。。

話不多說,現在開始listView 實現:

這是文檔列出的支持的佈局和widget控件:

A RemoteViews object (and, consequently, an App Widget) can support the following layout classes:

And the following widget classes:

Descendants of these classes are not supported

其中有ListView和 GridView 等控件。

android 中實現 appWidget 有自己的一套機制:

1.  widget 的支持,AppWidgetProvider 類的實現。

覆蓋在 AppWidgetProvider 的 OnReceive() 函數,從android的源碼中可以知道,AppWidgetProvider 的 OnUpdate() , OnEnable(), OnDelete() 等方法都是從 OnReceive() 方法中分配進去的。即所有的廣播先通過OnReceive()函數,再分配到OnUpdate()等函數去。

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_ENABLED.equals(action)) {
        this.onEnabled(context);
    }
    else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
        this.onDisabled(context);
    }
}
// END_INCLUDE(onReceive)

 

注意到:

String action = intent.getAction();

這裏 intent 先獲取 action, 通過action 來獲取到廣播並區分類型,所以自己定義 action 通過 PendingIntent 來實現各種跳轉~

到這裏先擺下基礎的文件吧:

這裏需要注意到的是:android中的xml 文件不能有大寫字母。區分單詞用最好用  _  符號。否則找不到文件名。

provider_info 文件,提供 appWidget 的一些基本控制信息。

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
  android:minWidth = "294dp"
  android:minHeight = "367dp"
  android:updatePeriodMillis = "1000"
  android:initialLayout = "@layout/listview"
  android:background="#0000ff"
  >
   
</appwidget-provider>

listview 文件: ListView 控件就在內部:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  android:layout_width="294dp"
  android:layout_height="400dp">
  <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="360dp"
    android:minHeight="100dp"
    android:id="@+id/listviewWrapper"
    >
      <ListView
        android:layout_height = "360dp"
        android:layout_width = "294dp"
        android:background="#000000"
        android:id = "@+id/myListView"
      />
  </LinearLayout>
  <Button
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/listviewWrapper"
     
    android:layout_alignParentLeft="true"
    android:id="@+id/refresh"
    android:text="refresh"
    >
  </Button>
</RelativeLayout>

下面是list_item 文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:textColor="#ff0000"
    android:layout_marginTop="5px"
    android:layout_marginBottom="5px"
    android:paddingBottom="25px"
    android:paddingTop="5px"
    android:textSize="60px"
    android:id="@+id/item"
  />
  <ImageView
    android:id="@+id/imageItem"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_alignRight="@id/item"
    />
</RelativeLayout>

 list 的 item 中也可以添加 ImageView 等appWidget 支持的控件。

這是manifest 問件:

<?xml version="1.0" encoding="utf-8"?>
    package="com.zgc.AppWidget6"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk android:minSdkVersion="15" />
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <receiver android:name=".MyWidgetProvider">
            <meta-data android:name="android.appwidget.provider"
                android:resource="@xml/provider_info" >
            </meta-data>
            <intent-filter >
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>   
            </intent-filter>
        </receiver>
         
        <service android:name=".MyWidgetService"
            android:permission="android.permission.BIND_REMOTEVIEWS"
            android:exported="false" ></service>
         
     </application>
      
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

其中 service 提供 name 是 MyWidgetService ,他是繼承RemoteViewsService 類。是我們需要爲 遠程 ListView 提供 數據源的服務。

RemoteViewsService的是個服務。其中:

public RemoteViewsFactory onGetViewFactory(Intent intent) {
      return new ListRemoteViewsFactory(this.getApplicationContext(), intent);
  }
ListRemoteViewsFactory 這裏就充當 ListView 的數據源。

就好比在activity 中使用ListView 一樣。也需要通過AdapterView 來爲ListView 提供數據源。不過AdatperView中提供了每一Item的方法。

具體邏輯如圖:

    

 

下面是 service 的源代碼:

public class MyWidgetService  extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new ListRemoteViewsFactory(this.getApplicationContext(), intent);
    }
 
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        System.out.println("service in onCreate");
        super.onCreate();
    }
     
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        System.out.println("service in onDestory");
        super.onDestroy();
    }
 
    @Override
    public boolean onUnbind(Intent intent) {
        // TODO Auto-generated method stub
        System.out.println("service in onUnbind");
        return super.onUnbind(intent);
    }
 
    @Override
    public void onRebind(Intent intent) {
        // TODO Auto-generated method stub
        System.out.println("service in onRebind");
        super.onRebind(intent);
    }
 
    @Override
    public void onStart(Intent intent, int startId) {
        // TODO Auto-generated method stub
        System.out.println("service in onStart");
        super.onStart(intent, startId);
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        return super.onStartCommand(intent, flags, startId);
    }
}<br><br>
class ListRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
     
    private static int mCount = 0;
    private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
    private List<String> mWidgetItemsAttr= new ArrayList<String>();
    private Context mContext;
    private int mAppWidgetId;
    private String url = "http://10.40.73.77/php/getData.php";
     
    public static int whichPage = 0;
    public static int mainPageId = -1;
    public static int secPageId = -1;
    public static final int mainPage = 0;
    public static final int secPage = 1;
    public static List<Integer> checkPos = new ArrayList<Integer>();
    //public static int[] checkPosArr = new int[100];
     
     
    public ListRemoteViewsFactory(Context context, Intent intent){
        mContext = context;
        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }
     
    @Override
    public void onCreate() {
        System.out.println("onCreate in factory");
        // TODO Auto-generated method stub
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        if(whichPage == mainPage){
            mCount = mWidgetItems.size();
        }
        else if(whichPage == secPage){
            mCount = mWidgetItemsAttr.size();
        }
         
        return mCount;
    }
 
    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }
 
    @Override
    public RemoteViews getLoadingView() {
        // TODO Auto-generated method stub
        System.out.println("getLoadingView");
        return null;
    }
 
    @Override
    public RemoteViews getViewAt(int position) {
        System.out.println("getViewAt");
        // TODO Auto-generated method stub
        RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.list_item);
        switch(whichPage){
        case mainPage :
            if(-1 == mainPageId){ //refresh main page
                System.out.println("getViewAt mainPage refresh");
                 
                rv.setTextViewText(R.id.item, mWidgetItems.get(position).text);
                Bundle extras = new Bundle();
                extras.putInt("page", 0);
                extras.putInt(MyWidgetProvider.EXTRA_ITEM, position);
                extras.putString("name", mWidgetItems.get(position).text);
                Intent fillInIntent = new Intent();
            fillInIntent.putExtras(extras);
            rv.setOnClickFillInIntent(R.id.item, fillInIntent);
            }
            else{ //refresh to secPage list content
                System.out.println("getViewAt mainPage item click");
                 
                mainPageId = -1;
            }
            break;
             
        case secPage:
            if(-1 == secPageId){ //refresh when click back button, but I only have one home button
                //refresh second list page
                System.out.println("getViewAt secPage refresh");
                 
                rv.setTextViewText(R.id.item, mWidgetItemsAttr.get(position));
                rv.setImageViewResource(R.id.imageItem, R.drawable.checkbox);
                 
                Bundle extras = new Bundle();
                extras.putInt("page", 1);
                extras.putInt(MyWidgetProvider.EXTRA_ITEM, position);
                Intent fillInIntent = new Intent();
                fillInIntent.putExtras(extras);
                rv.setOnClickFillInIntent(R.id.item, fillInIntent);
                rv.setOnClickFillInIntent(R.id.imageItem, fillInIntent);
            }
            else{ //change positon
                rv.setTextViewText(R.id.item, mWidgetItemsAttr.get(position));
                 
                if(-1 != checkPos.indexOf(position)){
                    //change list item picture to be checked
                    rv.setImageViewResource(R.id.imageItem, R.drawable.checkedbox);
                }
                else{
                    rv.setImageViewResource(R.id.imageItem, R.drawable.checkbox);
                }
                 
                //每一個 item 都需要從新賦值。否則會出錯!!具體原因沒有查明
                Bundle extras = new Bundle();
                extras.putInt("page", 1);
                extras.putInt(MyWidgetProvider.EXTRA_ITEM, position);
                Intent fillInIntent = new Intent();
                fillInIntent.putExtras(extras);
                rv.setOnClickFillInIntent(R.id.item, fillInIntent);
                rv.setOnClickFillInIntent(R.id.imageItem, fillInIntent);
            }
            break;
            default: ;
        }
         
        return rv;
    }
 
    @Override
    public int getViewTypeCount() {
        // TODO Auto-generated method stub
        return 1;
    }
 
    @Override
    public boolean hasStableIds() {
        // TODO Auto-generated method stub
        return true;
    }
 
 
    @Override
    public void onDataSetChanged() {
        // TODO Auto-generated method stub
        System.out.println("onDataSetChanged");
 
            //this func is get data
        mWidgetItems.clear();
         
         
        switch(whichPage){
        case mainPage :
            System.out.println("onDataSetChanged_mainPage");
            if(-1 == mainPageId){ //refresh main page
                 
                try{
                    URL reqURL = new URL(url);
                    BufferedReader br = new BufferedReader(new InputStreamReader(reqURL.openStream(), "gbk"));
                    StringBuffer sb = new StringBuffer();
                    String line;
                    while(null != (line = br.readLine())){
                        sb.append(line);
                    }  
                    br.close();
             
                    ArrayList <Map<String, Object>> mList= new ArrayList<Map<String, Object>>();
                        
                    JSONArray arr_json = new JSONArray(sb.toString());
                    for(int i = 0, len = arr_json.length(); i < len; i++){
                           String strName = arr_json.getJSONObject(i).getString("name");
                           String strUrl = arr_json.getJSONObject(i).getString("url");
                           int id = arr_json.getJSONObject(i).getInt("id");
                            
                           Map<String, Object> map = new HashMap<String, Object>();
                            
                           mWidgetItems.add(new WidgetItem(strName, id));
                            
                           map.put("name", strName);
                           map.put("url", strUrl);
                           mList.add(map);            
                    }
                         
                            mCount = mWidgetItems.size();
                    }catch(Exception e){
                        Toast.makeText(mContext, "can't connect server", Toast.LENGTH_LONG).show();
                    }
            }
            else{ //
                System.out.println("onDataSetChanged_mainPage else");
                 
                WidgetItem item = mWidgetItems.get(mainPageId);
                 
            }
             
            System.out.println("onDataSetChanged_-1");
            break;
             
        case secPage: //here can get more info from server, but no need to get more infomation, 
            if(-1 == secPageId){
                mWidgetItemsAttr.clear();
                 
                System.out.println("onDataSetChanged_secPage -1");
                mWidgetItemsAttr.add("zhang");
                mWidgetItemsAttr.add("gui");
                mWidgetItemsAttr.add("chuang");
                mWidgetItemsAttr.add("hui");
                mWidgetItemsAttr.add("cong");
                mWidgetItemsAttr.add("gui");
                mWidgetItemsAttr.add("chuang");
                mWidgetItemsAttr.add("hui");
                mWidgetItemsAttr.add("cong");
                mWidgetItemsAttr.add("gui");
                mWidgetItemsAttr.add("chuang");
                mWidgetItemsAttr.add("hui");
                mWidgetItemsAttr.add("cong");
                mWidgetItemsAttr.add("cong");
            }
            else{
                System.out.println("onDataSetChanged_secPage else");
            }
            break;
             
            default: return ;
        }
         
 
         
    }
 
    @Override
    public void onDestroy() {
        System.out.println("onDestory in factory");
        // TODO Auto-generated method stub
        mWidgetItems.clear();  
    }
 
}

  

<br>
<br>注意到幾個方法:<br>public void onDataSetChanged(){.....}<br>public RemoteViews getViewAt(int position) {....}<br>public int getCount() {....}<br><br>onDateSetChanged(){...} 方法在你使用的 MyWidgetProvider 的 onReceive() 和 onUpdate() 方法中調用
AppWidgetManager 的實例的方法: mgr.notifyAppWidgetViewDataChanged(appIds, R.id.myListView); 來更新要求更新數據源:<br>首先就會調用 :
onDataSetChanged(){.....}
然後在調用 getViewAt(int position){....}<br>其中getViewAt 的參數 position 就是你的ListView中每一項 item 的位置。從 0 計數。<br>其中你必須 override 的 getCount()方法是返回你的ListView item 的總數。這個自己必須返回自己的數據才能讓getViewAt的postion能夠計數。<br><br>你獲取數據的方式比如http從服務器獲取數據的話,就需要放在onDateSetChagged()方法裏。<br>當然 public RemoteViews getLoadingView(){...} 也可以。不過要注意放回的是RemoteViews 就說明這是要更新界面的,這個函數的作用就是在你更新界面的時候如果耗時就會顯示 正在加載... 的默認字樣,但是你可以更改這個界面。需要返回一個 RemoteViews 類型。其中你可以使用RemoteViews 去切換自己定義的 Layout 。<br><br>關於 remoteViews 的實例的方法:
rv.setOnClickFillInIntent(R.id.item, fillInIntent);
在下面會解釋。<br><br><br>下面是provider:
public class MyWidgetProvider extends AppWidgetProvider{
    public static final String TOAST_ACTION = "com.zgc.listwidget.TOAST_ACTION";
    public static final String EXTRA_ITEM = "com.zgc.listwidget.EXTRA_ITEM";
    public static final String TO_SITE = "com.zgc.listwidget.TO_SITE";
    public static final String SITE = "com.zgc.listwidget.SITE";
    public static final String BACK_HOME = "com.zgc.listwidget.BACK_HOME";
    public static String PicName = "";
    public static final String REFRESH = "com.zgc.listwidget.REFRESH";
     
    public static final String ITEM = "com.zgc.AppWidget6.ITEM";
     
     
 
    @Override
    public IBinder peekService(Context myContext, Intent service) {
        // TODO Auto-generated method stub
        System.out.println("peekService in provider");
        return super.peekService(myContext, service);
    }
 
    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        // TODO Auto-generated method stub
        System.out.println("onDeleted in Provider");
        super.onDeleted(context, appWidgetIds);
    }
 
    @Override
    public void onDisabled(Context context) {
        // TODO Auto-generated method stub
        System.out.println("onDisabled in Provider");
        super.onDisabled(context);
    }
 
    @Override
    public void onEnabled(Context context) {
        // TODO Auto-generated method stub
        System.out.println("onEnabled in Provider");
        super.onEnabled(context);
    }
 
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        ComponentName cmpName = new ComponentName(context, MyWidgetProvider.class);
         
        if (intent.getAction().equals(ITEM)) {
            System.out.println("item action");
            int pageNum = intent.getIntExtra("page", 1);
            int itemPos = intent.getIntExtra(EXTRA_ITEM, 0);
            if(0 == pageNum){
                System.out.println("item action 0 page");
                ListRemoteViewsFactory.secPageId = -1;
                ListRemoteViewsFactory.whichPage = ListRemoteViewsFactory.secPage;
                 
                int[] appIds = mgr.getAppWidgetIds(cmpName);
                mgr.notifyAppWidgetViewDataChanged(appIds, R.id.myListView);
                 
                //change refresh to commit button ,here no need to reload listview
                RemoteViews rv2 = new RemoteViews(context.getPackageName(), R.layout.listview);
                rv2.setTextViewText(R.id.refresh, "commit");
                Intent commitIntent = new Intent(context, MyWidgetProvider.class);
                commitIntent.setData(Uri.parse(commitIntent.toUri(Intent.URI_INTENT_SCHEME)));
                commitIntent.setAction(SITE);
                PendingIntent commitPendingIntent = PendingIntent.getBroadcast(context, 0,
                        commitIntent, PendingIntent.FLAG_UPDATE_CURRENT);
                rv2.setOnClickPendingIntent(R.id.refresh, commitPendingIntent);
                 
                mgr.updateAppWidget(appIds, rv2);
                 
            }
            else if(1 == pageNum){
                System.out.println("item action 1 page");
                ListRemoteViewsFactory.secPageId = itemPos;
                 
                if(-1 == ListRemoteViewsFactory.checkPos.indexOf(itemPos)){
                    ListRemoteViewsFactory.checkPos.add(itemPos);
                }
                else{
                    ListRemoteViewsFactory.checkPos.remove(ListRemoteViewsFactory.checkPos.indexOf(itemPos));
                }
 
                int[] appIds = mgr.getAppWidgetIds(cmpName);
                mgr.notifyAppWidgetViewDataChanged(appIds, R.id.myListView);
            }
 
        }
        else if(intent.getAction().equals(SITE)){
            System.out.println("in receive commit SITE action");
            RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.img);
            int id = R.drawable.uliuli;
            if(PicName.equals("google")){
                id = R.drawable.uliuli;
            }
            else if(PicName.equals("douban")){
                id = R.drawable.uliuli;
            }
            rv.setImageViewResource(R.id.displayImage, id);
             
            Intent homeIntent = new Intent(context, MyWidgetProvider.class);
            homeIntent.setAction(BACK_HOME);
            //homeIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
            homeIntent.setData(Uri.parse(homeIntent.toUri(Intent.URI_INTENT_SCHEME)));
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, homeIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            rv.setOnClickPendingIntent(R.id.backHome, pendingIntent);
             
            mgr.updateAppWidget(cmpName, rv);
            //Toast.makeText(context, "Touched view zhang", Toast.LENGTH_SHORT).show();
        }
        else if(intent.getAction().equals(BACK_HOME)){
            System.out.println("back_home ");
        
            int[] appWidgetIds = mgr.getAppWidgetIds(cmpName);
             
            //mgr.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myListView);
                 
            Intent serviceIntent = new Intent(context, MyWidgetService.class);  //
            //intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
            serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));
            RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.listview);
            rv.setRemoteAdapter(R.id.myListView, serviceIntent);
                 
            Intent toastIntent = new Intent(context, MyWidgetProvider.class);
            toastIntent.setAction(MyWidgetProvider.ITEM);
 
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
            PendingIntent.FLAG_UPDATE_CURRENT);
            rv.setPendingIntentTemplate(R.id.myListView, toastPendingIntent);
                 
            mgr.updateAppWidget(appWidgetIds, rv);
             
            Intent refreshIntent = new Intent(context, MyWidgetProvider.class);
            refreshIntent.setAction(REFRESH);
            PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0,
                    refreshIntent, 0);
            rv.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);
             
            ListRemoteViewsFactory.whichPage = ListRemoteViewsFactory.mainPage;
            ListRemoteViewsFactory.mainPageId = -1;
            mgr.updateAppWidget(cmpName, rv);
            mgr.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myListView);
            System.out.println("zhanggui");
            //Toast.makeText(context, "Touched view back home", Toast.LENGTH_SHORT).show();
        }
        else if(intent.getAction().equals(REFRESH)){
            System.out.println("refresh button begin");
            int[] appWidgetIds = mgr.getAppWidgetIds(cmpName);
            mgr.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myListView);
            System.out.println("refresh button end");
        }
        super.onReceive(context, intent);
    }
 
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {
        System.out.println("onUpdate");
        // TODO Auto-generated method stub
         
        for(int i = 0; i < appWidgetIds.length; i++){
            Intent intent = new Intent(context, MyWidgetService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
            // When intents are compared, the extras are ignored, so we need to embed the extras
            // into the data so that the extras will not be ignored.
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.listview);
            rv.setRemoteAdapter(R.id.myListView, intent);
            //rv.setEmptyView(R.id.myListView, R.id.empty);
             
            Intent refreshIntent = new Intent(context, MyWidgetProvider.class);
            refreshIntent.setAction(REFRESH);
            PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0,
                    refreshIntent, 0);
            rv.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);
 
            Intent toastIntent = new Intent(context, MyWidgetProvider.class);
            toastIntent.setAction(MyWidgetProvider.ITEM);
            //toastIntent.putExtra("page", 0); // main page
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            rv.setPendingIntentTemplate(R.id.myListView, toastPendingIntent);
 
            appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }
 
 
}

首先看onUpdate() 函數,默認是從這裏先進去的:

申明intent 後使用:

intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));

用:這是爲了讓 intent 能夠帶上 extras 數據一起傳遞,否則在intent的比較的過程中會被忽略掉。這裏的比較應該是在同一個代碼塊中有多個intent的時候會發生比較吧(猜測:因爲當一條邏輯執行路徑上代碼塊中只有一個Intent發送的時候能夠帶數據(不適用intent.setData函數),但是有多個Intent的話不行)

 

PendingIntent 的使用不再解釋。不過這裏的 remoteView 實例使用:

rv.setPendingIntentTemplate(R.id.myListView, toastPendingIntent);

listView 使用 setPendingIntentTemplate 方法,當你點擊 ListView 中的任何一個item 時都會發送 toastPendingIntent ,在我們的 service 中的getViewAt() 方法中,爲每一個item 都設置了一個 intent :

rv.setOnClickFillInIntent(R.id.item, fillInIntent);

其中 remoteViews 的setOnClickFillInIntent() 是將 fillIntent 合併到 toastPendingIntent 中去。就是兩個 intent 合併了。

具體的合併方法是Intent中的fillIn(..) 方法:

Intent A : {data="foo", categories="bar"}

Intent B : {action="gotit", data-type="some/thing", categories="one","two"}.

調用 fillIn(): A.fillIn(B, Intent.FILL_IN_DATA)

結果:  A : {action="gotit", data-type="some/thing", categories="bar"}.

 

注意到 action 還是你在 onUpdate()中設置的 action,當然你也可以在 item 中的 fillInIntent 設置action.但是不好。

這裏可以在每個 item 的 fillInIntent 中 使用 putExtras() 方法讓Intent 帶每個item 的 位置號碼 來區分每個 item.

 

機制基本上已經說完,還有一點就是:

RemoteViewsService 中每次獲取數據都會重新創建 service 和 銷燬 service ,但是 RemoteViewsService.RemoteViewsFactory

的銷燬則是在 機器上把 appWidget 刪除後發生。就好像 RemoteViewsService.RemoteViewsFactory 是個數據庫。你每次去訪問這個數據源都會創建 一個 RemoteViewsService 然後銷燬。這個源於我android學習1月,也不清楚內部機制。沒仔細研究。等研究了再發文表上。

還有 AppWidgetProvider 類的實例是每發送一次 intent (即每一次 boardcast) 就新建一次,所以如果需要做 flag 來表示按鈕是否check的話,最好就先聲明爲 static 類型或者放到其他類中。

其餘的就是自己邏輯的實現:

我這裏實現了 兩個 ListView 的數據加載(通過http從服務器獲取數據)和點入每條item進入另外的頁面(這裏也是在listView上從新加載數據,但是可以區分每個Item。也可以自己加載其他的layout)。還可以實現回到第一個加載的頁面。其實可以做到回到上一個頁面。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章