2025-05-23

最近要把之前做的2.3上面的一些程序移植到4.0上面來,  幾乎所有的AppWidget都是我一手操辦, 所以這個玩意都是我弄.

我把Android2.3的代碼直接拷到4.0的環境下面, 編譯然後Push,  直接可以跑, 這是木有問題的.  但是我發現4.0上面有一些新東東是之前2.3上面沒有的,

我也讀瞭下官方的文檔, 做瞭些demo, 這裡總結給大傢, 在以後需要做AppWidget的時候可以得心應手.

1: 應用列表中的預覽圖

如果你不想你的Widget在應用列表裡面顯示成那個醜機器人圖片的話, 就需要在<appwidget-provider>中設置previewImage屬性,例如:

[html]
<appwidget-provider  
    android:previewImage="@drawable/widget_preview" 
/> 

2. Widget可以resize
這個我先沒註意到, 玩開發板的時候不小心把系統中帶的日歷的Widget拖出來,想刪沒有刪掉, 發現邊上出來一圈藍邊,於是乎想到是不是可以resize大小呢.?結果一試還真是可以,就翻日歷源碼的Widget和相應的xml文件,發現在<appwidget-provider>中設置瞭resizeMode屬性, 可以設置讓用戶橫向拉, 縱向拉.  設置minResizeWidth和minResizeHeight可以根據需要指定每次縮放的大小(一般設成一格寬, 當然對於集合來說這個要根據你Widget每個元素的大小,一般遵循的規則是拉伸大小為Widget裡面每個元素的大小. 例如我看到BookMarket,他的每個元素是占一行兩列,所以此時你設置拉伸大小就要註意瞭, 最好也設置成每次橫向兩列, 縱向一行就行瞭 ).   例如

[java]
<!–需要兩個方向都可以拉的話,就把他們或起來,android裡面很多都是這麼做的 –> 
<appwidget-provider 
  android:resizeMode="horizontal|vertical" 
  android:minResizeWidth="146dip" 
  android:minResizeHeight="72dip" 
/> 
以上72和146是怎麼計算出來的這個不深說瞭, 文檔上是這麼說得.

3.支持很多集合控件

這個事非常讓我興奮的阿, 以前看到我Htc的機子上面Widget有集合控件,支持手勢, 但是如果不定制RemoteViews是沒辦法實現的.

Gallery2中的Widget就是拿StackView去做的.  於是我參照瞭下Gallery2的源碼和官方文檔,瞭解瞭Widget中使用集合控件的方法.

集合是通過一個RemoteViewService去做的, 然後要創建一個RemoteViewsFactory, 這個接口裡面的一些方法下面我會一一解釋.
不多說.直接上我寫的demo的代碼,拿ListView做的,其他集合控件都差不多的使用.

widget_provider.xml

[html]
<?xml version="1.0" encoding="utf-8" ?>  
  <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  
        android:minHeight="220dip"  
        android:minWidth="220dip"  
        android:updatePeriodMillis="0"  
        android:initialLayout="@layout/main" />  

main.xml
[html]
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 
    <ListView 
       android:layout_width="fill_parent" 
       android:layout_height="fill_parent"  
       android:id="@+id/list_data" 
         /> 
     
     
</LinearLayout> 
list_item.xml

[java]
<?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="horizontal" 
    android:id="@+id/item_layout" > 
    <TextView  
        android:layout_width="wrap_content" 
        android:layout_height="match_parent" 
        android:id="@+id/tv_key" 
        android:textSize="24dip"/> 
    <TextView  
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:id="@+id/tv_value" 
        android:textSize="24dip" 
        android:gravity="right"/> 
</LinearLayout> 

ListViewService.java

[java]
package cn.xuhui.pro; 
 
import java.util.ArrayList; 
import java.util.List; 
 
import android.content.Context; 
import android.content.Intent; 
import android.widget.RemoteViews; 
import android.widget.RemoteViewsService; 
 
public class ListViewService extends RemoteViewsService { 
 
    @Override 
    public RemoteViewsFactory onGetViewFactory(Intent intent) { 
        //這裡很簡單的給ListView一個List就好瞭 
        List<String> list = new ArrayList<String>(); 
        for(int i = 1; i <= 30; i++) { 
            list.add(i + "," + i); 
        }        
         
        return new ListRemoteViewsFactory(this, list); 
    } 
     
    private static class ListRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { 
        private List<String> mList; 
        private Context mContext; 
         
        //構造ListRemoteViewsFactory 
        public ListRemoteViewsFactory(Context context, List<String> list) { 
            mList = list; 
            mContext = context; 
        } 
         
         
        @Override 
        public int getCount() { 
            //返回count 
            return mList.size(); 
        } 
 
        @Override 
        public long getItemId(int position) { 
            //類似Adapter裡面的getItemId,不用處理,一般直接返回就夠瞭 
            return position; 
        } 
 
        @Override 
        public RemoteViews getLoadingView() { 
            //when ListView is scrolled, the loading view is not necessary 
            //如果是StackView之類需要顯示圖片什麼的,滑動的時候免得用戶看到白板,就設置個Loading的View 
            return null; 
        } 
 
        @Override 
        public RemoteViews getViewAt(int position) { 
                        //這個方法相當於返回ListView的一個Item(類似Adapter裡面的getView/getItem吧) 
                        RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.list_item); 
            String[] entry = mList.get(position).split(","); 
            rv.setTextViewText(R.id.tv_key, entry[0]); 
            rv.setTextViewText(R.id.tv_value, entry[1]); 
                        Intent fillInIntent = new Intent(mContext, WidgetClickHandlerService.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
            fillInIntent.putExtra("clicked_item", mList.get(position)); 
            //記住,這裡是不可以用setOnClickPendingIntent的,官方API上有說, PendingIntent對於CollectionViews是無效的. 
                        rv.setOnClickFillInIntent(R.id.item_layout, fillInIntent); 
            return rv; 
        } 
 
        @Override 
        public int getViewTypeCount() { 
            //視圖種類, 如果我們的集合裡面就隻有一種視圖,那麼返回1.附上官方的api doc 
            /*
            Returns the number of types of Views that will be created by getView(int, View, ViewGroup). 
            Each type represents a set of views that can be converted in getView(int, View, ViewGroup). 
            If the adapter always returns the same type of View for all items, this method should return 1
            */ 
            return 1; 
        } 
 
        @Override 
        public boolean hasStableIds() { 
            //return True if the same id always refers to the same object. 
            //如果返回true, 同一個id總是指向同一個對象 
            return true; 
        } 
 
        @Override 
        public void onCreate() { 
            // TODO ready for data source 
            //這個方法一般是準備或者處理數據源的,這裡不做處理. 可以參看Gallery2的 
        } 
 
        @Override 
        public void onDataSetChanged() { 
            // TODO demo only , no dataSet change, 可以參看Gallery2的 
        } 
 
        @Override 
        public void onDestroy() { 
            mList.clear(); 
            mList = null; 
        } 
    } 
 

我們點擊ListView的Item時就用一個服務簡單彈一個Toast就可以瞭
WidgetClickHandlerService .java
[java]
package cn.xuhui.pro; 
 
import android.app.Service; 
import android.content.Intent; 
import android.os.IBinder; 
import android.widget.Toast; 
 
public class WidgetClickHandlerService extends Service { 
 
    @Override 
    public IBinder onBind(Intent intent) { 
        return null; 
    } 
     
    @Override 
    public void onStart(Intent intent, int startId) { 
        super.onStart(intent, startId); 
        String data = intent.getStringExtra("clicked_item"); 
        Toast.makeText(this, data, Toast.LENGTH_LONG).show(); 
    } 
 

MyAppWidgetProvider.java
[java]
package cn.xuhui.pro; 
 
 
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.widget.RemoteViews; 
 
public class MyAppWidgetProvider extends AppWidgetProvider { 
    @Override 
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, 
            int[] appWidgetIds) { 
        RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.main); 
        Intent intent = new Intent(context, ListViewService.class); 
        rv.setRemoteAdapter(R.id.list_data, intent); 
        //註意,下面這段代碼不能少,否則點擊沒有效果 
                Intent clickIntent = new Intent(context, WidgetClickHandlerService.class); 
                PendingIntent pendingIntent = PendingIntent.getService( 
                context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT); 
        rv.setPendingIntentTemplate(R.id.list_data, pendingIntent); 
        appWidgetManager.updateAppWidget(new ComponentName(context, MyAppWidgetProvider.class), rv); 
    } 

最後AndroidManifest.xml
[html]
<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="cn.xuhui.pro" 
    android:versionCode="1" 
    android:versionName="1.0" > 
 
    <uses-sdk android:minSdkVersion="15" /> 
 
    <application 
        android:icon="@drawable/ic_launcher" 
        android:label="@string/app_name" > 
        <!–這裡必須加上這個權限, 否則報錯 –> 
        <service  
            android:name=".ListViewService" 
            android:permission="android.permission.BIND_REMOTEVIEWS" 
            /> 
         
        <service  
            android:name=".WidgetClickHandlerService" 
            /> 
         
        <receiver android:name=".MyAppWidgetProvider" 
            android:label="xh_demo"> 
            <intent-filter> 
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> 
            </intent-filter> 
            <meta-data android:name="android.appwidget.provider" 
                    android:resource="@xml/widget_provider" /> 
        </receiver> 
    </application> 
 
</manifest> 

好瞭, 今天就總結到這裡, 以後有時間再來研究一些Widget的東西吧

摘自  徐輝的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *