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:
- AnalogClock
- Button
- Chronometer
- ImageButton
- ImageView
- ProgressBar
- TextView
- ViewFlipper
- ListView
- GridView
- StackView
- AdapterViewFlipper
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 xmlns:android="http://schemas.android.com/apk/res/android" 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
xmlns:android="http://schemas.android.com/apk/res/android"
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
xmlns:android="http://schemas.android.com/apk/res/android"
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"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
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);
}
}
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();
}
}
注意到幾個方法:
public void onDataSetChanged(){.....}
public RemoteViews getViewAt(int position) {....}
public int getCount() {....}
onDateSetChanged(){...} 方法在你使用的 MyWidgetProvider 的 onReceive() 和 onUpdate() 方法中調用
AppWidgetManager 的實例的方法: mgr.notifyAppWidgetViewDataChanged(appIds, R.id.myListView); 來更新要求更新數據源:
首先就會調用 :
onDataSetChanged(){.....}
然后在調用 getViewAt(int position){....}
其中getViewAt 的參數 position 就是你的ListView中每一項 item 的位置。從 0 計數。
其中你必須 override 的 getCount()方法是返回你的ListView item 的總數。這個自己必須返回自己的數據才能讓getViewAt的postion能夠計數。
你獲取數據的方式比如http從服務器獲取數據的話,就需要放在onDateSetChagged()方法里。
當然 public RemoteViews getLoadingView(){...} 也可以。不過要注意放回的是RemoteViews 就說明這是要更新界面的,這個函數的作用就是在你更新界面的時候如果耗時就會顯示 正在加載... 的默認字樣,但是你可以更改這個界面。需要返回一個 RemoteViews 類型。其中你可以使用RemoteViews 去切換自己定義的 Layout 。
關於 remoteViews 的實例的方法:
rv.setOnClickFillInIntent(R.id.item, fillInIntent);
在下面會解釋。
下面是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)。還可以實現回到第一個加載的頁面。其實可以做到回到上一個頁面。
博客園不能奉上源代碼,可惜。。。有需要的留言~
