zoukankan      html  css  js  c++  java
  • HoneyComb3.0技术系列之AppWidget(RemoteViewService)

    1. 概述:

        在HoneyComb3.0中AppWidget上可以添加更多的组件,如:ListView,GridView,StackView和ViewFlipper等集合组件。它提供了一套新的

        集合组件渲染机制RemoteViewService,它继承自Service,向外提供渲染ListView,GridView或StackView等集合组件的Factory,这个Factory

        类似Adapter向这些集合组件提供数据。在AppWidgetProviderInfo类中(<appwidget-provider>标签)新增加了两个属性,一个是

        previewImage,这个属性指定一张图片,这个图片显示在添加AppWidget的Picker界面中,拖动这张图片到Launcher的WorkSpace后就向其中

        添加图片所对应的AppWidget,另一个是autoAdvanceViewId,这个属性指定自动更新的ViewID,我指定StackView的ID有效果(View之间不

        停的滑动),但是指定的ListView或GridView的ID没有任何效果。

    2. 整体效果图:

        (1)AppWidget的Picker效果图,如下:

                AppWidget1

        (2)AppWidget效果图,如下:

                AppWidget2

    3. AppWidget的代码实现,如下:

        (1)AndroidManifest.xml配置文件,如下:

    [java] view plaincopy
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     package="com.example.android.weatherlistwidget">  
    4.       
    5.     <application android:label="Weather Widget Sample">  
    6.       
    7.         <receiver android:name="WeatherWidgetProvider">  
    8.             <intent-filter>  
    9.                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
    10.             </intent-filter>  
    11.             <meta-data android:name="android.appwidget.provider"  
    12.                     android:resource="@xml/widgetinfo" />  
    13.         </receiver>  
    14.         <service android:name="WeatherWidgetService"  
    15.             android:permission="android.permission.BIND_REMOTEVIEWS"  
    16.             android:exported="false" />  
    17.         <provider android:name="WeatherDataProvider"  
    18.               android:authorities="com.example.android.weatherlistwidget.provider" />  
    19.                 
    20.     </application>  
    21.       
    22.     <uses-sdk android:minSdkVersion="11" />  
    23.       
    24. </manifest>  

        (2)res/xml/目录中AppWidget的配置文件,如下:

    [java] view plaincopy
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <appwidget-provider  
    3.   xmlns:android="http://schemas.android.com/apk/res/android"  
    4.   android:minWidth="222dip"  
    5.   android:minHeight="222dip"  
    6.   android:updatePeriodMillis="1800000"  
    7.   android:initialLayout="@layout/widget_layout"  
    8.   android:previewImage="@drawable/preview"  
    9.   >  
    10. </appwidget-provider>  

        (3)res/layout目录中AppWidget的总布局,如下:

    [java] view plaincopy
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="294dp"  
    4.     android:layout_height="match_parent"  
    5.     android:orientation="vertical">  
    6.       
    7.     <FrameLayout  
    8.         android:layout_width="match_parent"  
    9.         android:layout_height="wrap_content">  
    10.         <ImageView  
    11.             android:id="@+id/header"  
    12.             android:layout_width="match_parent"  
    13.             android:layout_height="wrap_content"  
    14.             android:src="@drawable/header" />  
    15.         <ImageButton  
    16.             android:id="@+id/refresh"  
    17.             android:layout_width="56dp"  
    18.             android:layout_height="39dp"  
    19.             android:layout_marginLeft="222dp"  
    20.             android:layout_marginTop="20dp"  
    21.             android:background="@drawable/refresh_button" />  
    22.     </FrameLayout>  
    23.       
    24.     <FrameLayout  
    25.         android:layout_width="match_parent"  
    26.         android:layout_height="match_parent"  
    27.         android:layout_weight="1"  
    28.         android:layout_gravity="center"  
    29.         android:background="@drawable/body">  
    30.         <ListView  
    31.             android:id="@+id/weather_list"  
    32.             android:layout_width="match_parent"  
    33.             android:layout_height="match_parent" />  
    34.         <TextView  
    35.             android:id="@+id/empty_view"  
    36.             android:layout_width="match_parent"  
    37.             android:layout_height="match_parent"  
    38.             android:gravity="center"  
    39.             android:visibility="gone"  
    40.             android:textColor="#ffffff"  
    41.             android:text="@string/empty_view_text"  
    42.             android:textSize="20sp" />  
    43.     </FrameLayout>  
    44.       
    45.     <ImageView  
    46.         android:id="@+id/footer"  
    47.         android:layout_width="match_parent"  
    48.         android:layout_height="wrap_content"  
    49.         android:src="@drawable/footer" />  
    50. </LinearLayout>   

        (4)ContentProvider类,如下:

    [java] view plaincopy
     
    1. package com.example.android.weatherlistwidget;  
    2. import java.util.ArrayList;  
    3. import android.content.ContentProvider;  
    4. import android.content.ContentValues;  
    5. import android.database.Cursor;  
    6. import android.database.MatrixCursor;  
    7. import android.net.Uri;  
    8. class WeatherDataPoint {  
    9.     String city;  
    10.     int degrees;  
    11.     WeatherDataPoint(String c, int d) {  
    12.         city = c;  
    13.         degrees = d;  
    14.     }  
    15. }  
    16. public class WeatherDataProvider extends ContentProvider {  
    17.       
    18.     public static final Uri CONTENT_URI = Uri.parse("content://com.example.android.weatherlistwidget.provider");  
    19.       
    20.     public static class Columns {  
    21.         public static final String ID = "_id";  
    22.         public static final String CITY = "city";  
    23.         public static final String TEMPERATURE = "temperature";  
    24.     }  
    25.     private static final ArrayList<WeatherDataPoint> sData = new ArrayList<WeatherDataPoint>();  
    26.     /** 
    27.      * 在onCreate()时初始化数据。 
    28.      */  
    29.     @Override  
    30.     public boolean onCreate() {  
    31.         sData.add(new WeatherDataPoint("San Francisco", 13));  
    32.         sData.add(new WeatherDataPoint("New York", 1));  
    33.         sData.add(new WeatherDataPoint("Seattle", 7));  
    34.         sData.add(new WeatherDataPoint("Boston", 4));  
    35.         sData.add(new WeatherDataPoint("Miami", 22));  
    36.         sData.add(new WeatherDataPoint("Toronto", -10));  
    37.         sData.add(new WeatherDataPoint("Calgary", -13));  
    38.         sData.add(new WeatherDataPoint("Tokyo", 8));  
    39.         sData.add(new WeatherDataPoint("Kyoto", 11));  
    40.         sData.add(new WeatherDataPoint("London", -1));  
    41.         sData.add(new WeatherDataPoint("Nomanisan", 27));  
    42.         return true;  
    43.     }  
    44.     @Override  
    45.     public synchronized Cursor query(Uri uri, String[] projection, String selection,  
    46.             String[] selectionArgs, String sortOrder) {  
    47.         assert(uri.getPathSegments().isEmpty());  
    48.         /** 
    49.          * 创建MatrixCursor,并向MatrixCursor中添加数据,返回。 
    50.          */  
    51.         final MatrixCursor c = new MatrixCursor(new String[]{ Columns.ID, Columns.CITY, Columns.TEMPERATURE });  
    52.         for (int i = 0; i < sData.size(); ++i) {  
    53.             final WeatherDataPoint data = sData.get(i);  
    54.             c.addRow(new Object[]{ new Integer(i), data.city, new Integer(data.degrees) });  
    55.         }  
    56.           
    57.         return c;  
    58.     }  
    59.     @Override  
    60.     public String getType(Uri uri) {  
    61.         return "vnd.android.cursor.dir/vnd.weatherlistwidget.citytemperature";  
    62.     }  
    63.     @Override  
    64.     public Uri insert(Uri uri, ContentValues values) {  
    65.         return null;  
    66.     }  
    67.     @Override  
    68.     public int delete(Uri uri, String selection, String[] selectionArgs) {  
    69.         return 0;  
    70.     }  
    71.     @Override  
    72.     public synchronized int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {  
    73.         assert(uri.getPathSegments().size() == 1);  
    74.           
    75.         final int index = Integer.parseInt(uri.getPathSegments().get(0));  
    76.         assert(0 <= index && index < sData.size());  
    77.         final WeatherDataPoint data = sData.get(index);  
    78.         data.degrees = values.getAsInteger(Columns.TEMPERATURE);  
    79.         /** 
    80.          * 提醒ContentObserver数据改变。 
    81.          */  
    82.         getContext().getContentResolver().notifyChange(uri, null);  
    83.           
    84.         return 1;  
    85.     }  
    86. }  

        (5)AppWidget类,如下:

    [java] view plaincopy
     
    1. package com.example.android.weatherlistwidget;  
    2. import java.util.Random;  
    3. import android.app.PendingIntent;  
    4. import android.appwidget.AppWidgetManager;  
    5. import android.appwidget.AppWidgetProvider;  
    6. import android.content.ComponentName;  
    7. import android.content.ContentResolver;  
    8. import android.content.ContentUris;  
    9. import android.content.ContentValues;  
    10. import android.content.Context;  
    11. import android.content.Intent;  
    12. import android.database.ContentObserver;  
    13. import android.database.Cursor;  
    14. import android.net.Uri;  
    15. import android.os.Handler;  
    16. import android.os.HandlerThread;  
    17. import android.widget.RemoteViews;  
    18. import android.widget.Toast;  
    19. class WeatherDataProviderObserver extends ContentObserver {  
    20.       
    21.     private AppWidgetManager mAppWidgetManager;  
    22.     private ComponentName mComponentName;  
    23.     WeatherDataProviderObserver(AppWidgetManager mgr, ComponentName cn, Handler h) {  
    24.         super(h);  
    25.         mAppWidgetManager = mgr;  
    26.         mComponentName = cn;  
    27.     }  
    28.     /** 
    29.      * ContentProvider的内容改变后会通过AppWidget组件中的ListView重新渲染数据。这里会调用RemoteViewService中RemoteViewsFactory的onDataSetChanged方法。 
    30.      */  
    31.     @Override  
    32.     public void onChange(boolean selfChange) {  
    33.         mAppWidgetManager.notifyAppWidgetViewDataChanged(mAppWidgetManager.getAppWidgetIds(mComponentName), R.id.weather_list);  
    34.     }  
    35.       
    36. }  
    37. public class WeatherWidgetProvider extends AppWidgetProvider {  
    38.     public static String CLICK_ACTION = "com.example.android.weatherlistwidget.CLICK";  
    39.     public static String REFRESH_ACTION = "com.example.android.weatherlistwidget.REFRESH";  
    40.     public static String EXTRA_CITY_ID = "com.example.android.weatherlistwidget.city";  
    41.     private static HandlerThread sWorkerThread;  
    42.     private static Handler sWorkerQueue;  
    43.     private static WeatherDataProviderObserver sDataObserver;  
    44.     public WeatherWidgetProvider() {  
    45.         sWorkerThread = new HandlerThread("WeatherWidgetProvider-worker");  
    46.         sWorkerThread.start();  
    47.         sWorkerQueue = new Handler(sWorkerThread.getLooper());  
    48.     }  
    49.     @Override  
    50.     public void onEnabled(Context context) {  
    51.         final ContentResolver r = context.getContentResolver();  
    52.           
    53.         /** 
    54.          * 在发出AppWidget的Enable广播后向WeatherDataProvider注册WeatherDataProviderObserver(ContentObserver)。 
    55.          */  
    56.         if (sDataObserver == null) {  
    57.             final AppWidgetManager mgr = AppWidgetManager.getInstance(context);  
    58.             final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);  
    59.             sDataObserver = new WeatherDataProviderObserver(mgr, cn, sWorkerQueue);  
    60.             r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver);  
    61.         }  
    62.     }  
    63.     @Override  
    64.     public void onReceive(Context ctx, Intent intent) {  
    65.         final String action = intent.getAction();  
    66.           
    67.         if (action.equals(REFRESH_ACTION)) {  
    68.             final Context context = ctx;  
    69.             sWorkerQueue.removeMessages(0);  
    70.             sWorkerQueue.post(new Runnable() {  
    71.                 /** 
    72.                  * 单击刷新按钮后会更新数据。 
    73.                  */  
    74.                 @Override  
    75.                 public void run() {  
    76.                     final ContentResolver r = context.getContentResolver();  
    77.                     final Cursor c = r.query(WeatherDataProvider.CONTENT_URI, null, null, null,   
    78.                             null);  
    79.                     final int count = c.getCount();  
    80.                     final int maxDegrees = 96;  
    81.                     r.unregisterContentObserver(sDataObserver);  
    82.                     for (int i = 0; i < count; ++i) {  
    83.                         final Uri uri = ContentUris.withAppendedId(WeatherDataProvider.CONTENT_URI, i);  
    84.                         final ContentValues values = new ContentValues();  
    85.                         values.put(WeatherDataProvider.Columns.TEMPERATURE,  
    86.                                 new Random().nextInt(maxDegrees));  
    87.                         r.update(uri, values, null, null);  
    88.                     }  
    89.                     r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver);  
    90.                     final AppWidgetManager mgr = AppWidgetManager.getInstance(context);  
    91.                     final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);  
    92.                       
    93.                     /** 
    94.                      * 这句话会调用RemoteViewSerivce中RemoteViewsFactory的onDataSetChanged()方法。 
    95.                      */  
    96.                     mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn), R.id.weather_list);  
    97.                 }  
    98.             });  
    99.         } else if (action.equals(CLICK_ACTION)) {  
    100.             /** 
    101.              * 单击ListView中的某一项会显示一个Toast提示。 
    102.              */  
    103.             final String city = intent.getStringExtra(EXTRA_CITY_ID);  
    104.             final String formatStr = ctx.getResources().getString(R.string.toast_format_string);  
    105.             Toast.makeText(ctx, String.format(formatStr, city), Toast.LENGTH_SHORT).show();  
    106.         }  
    107.         super.onReceive(ctx, intent);  
    108.     }  
    109.     @Override  
    110.     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {  
    111.         for (int i = 0; i < appWidgetIds.length; ++i) {  
    112.               
    113.             /** 
    114.              * 创建请求Service的Intent对象。 
    115.              */  
    116.             final Intent intent = new Intent(context, WeatherWidgetService.class);  
    117.             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);  
    118.             intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));  
    119.               
    120.             /** 
    121.              * 创建RemoteViews对象,在其中的布局中存在一个ListView组件widget_layout。 
    122.              */  
    123.             final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);  
    124.               
    125.             /** 
    126.              * 组件ListView组件weather_list设置RemoteViewService渲染ListView. 
    127.              */  
    128.             rv.setRemoteAdapter(appWidgetIds[i], R.id.weather_list, intent);  
    129.               
    130.             /** 
    131.              * 如果weather_list为空则显示empty_view。 
    132.              */  
    133.             rv.setEmptyView(R.id.weather_list, R.id.empty_view);  
    134.               
    135.             /** 
    136.              * 点击ListView某一项时会显示一个Toast提示。 
    137.              */  
    138.             final Intent onClickIntent = new Intent(context, WeatherWidgetProvider.class);  
    139.             onClickIntent.setAction(WeatherWidgetProvider.CLICK_ACTION);  
    140.             onClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);  
    141.             onClickIntent.setData(Uri.parse(onClickIntent.toUri(Intent.URI_INTENT_SCHEME)));  
    142.             final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0,  
    143.                     onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);  
    144.             rv.setPendingIntentTemplate(R.id.weather_list, onClickPendingIntent);  
    145.             /** 
    146.              * 点击刷新按钮时刷新ListView数据。 
    147.              */  
    148.             final Intent refreshIntent = new Intent(context, WeatherWidgetProvider.class);  
    149.             refreshIntent.setAction(WeatherWidgetProvider.REFRESH_ACTION);  
    150.             final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0,  
    151.                     refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);  
    152.             rv.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);  
    153.             /** 
    154.              * 更新AppWidget组件。 
    155.              */  
    156.             appWidgetManager.updateAppWidget(appWidgetIds[i], rv);  
    157.         }  
    158.           
    159.         super.onUpdate(context, appWidgetManager, appWidgetIds);  
    160.     }  
    161. }  

        (6)RemoteViewsService渲染器,如下:

    [java] view plaincopy
     
    1. package com.example.android.weatherlistwidget;  
    2. import android.content.Context;  
    3. import android.content.Intent;  
    4. import android.database.Cursor;  
    5. import android.os.Bundle;  
    6. import android.widget.RemoteViews;  
    7. import android.widget.RemoteViewsService;  
    8. public class WeatherWidgetService extends RemoteViewsService {  
    9.     @Override  
    10.     public RemoteViewsFactory onGetViewFactory(Intent intent) {  
    11.         return new StackRemoteViewsFactory(this.getApplicationContext(), intent);  
    12.     }  
    13. }  
    14. /** 
    15.  * 这个Factory就类拟一个Adapter,可以为AppWidget中的集合组件设置数据。 
    16.  */  
    17. class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {  
    18.     private Context mContext;  
    19.     private Cursor mCursor;  
    20.     public StackRemoteViewsFactory(Context context, Intent intent) {  
    21.         mContext = context;  
    22.     }  
    23.     public void onCreate() {  
    24.           
    25.     }  
    26.     public void onDestroy() {  
    27.         if (mCursor != null) {  
    28.             mCursor.close();  
    29.         }  
    30.     }  
    31.     /** 
    32.      * 获取Cursor中的数据个数。 
    33.      */  
    34.     public int getCount() {  
    35.         return mCursor.getCount();  
    36.     }  
    37.     /** 
    38.      * 获取ListView中的每一个条目View. 
    39.      */  
    40.     public RemoteViews getViewAt(int position) {  
    41.         String city = "Unknown City";  
    42.         int temp = 0;  
    43.         if (mCursor.moveToPosition(position)) {  
    44.             final int cityColIndex = mCursor.getColumnIndex(WeatherDataProvider.Columns.CITY);  
    45.             final int tempColIndex = mCursor.getColumnIndex(  
    46.                     WeatherDataProvider.Columns.TEMPERATURE);  
    47.             city = mCursor.getString(cityColIndex);  
    48.             temp = mCursor.getInt(tempColIndex);  
    49.         }  
    50.         final String formatStr = mContext.getResources().getString(R.string.item_format_string);  
    51.         final int itemId = (position % 2 == 0 ? R.layout.light_widget_item  
    52.                 : R.layout.dark_widget_item);  
    53.         RemoteViews rv = new RemoteViews(mContext.getPackageName(), itemId);  
    54.         rv.setTextViewText(R.id.widget_item, String.format(formatStr, temp, city));  
    55.         final Intent fillInIntent = new Intent();  
    56.         final Bundle extras = new Bundle();  
    57.         extras.putString(WeatherWidgetProvider.EXTRA_CITY_ID, city);  
    58.         fillInIntent.putExtras(extras);  
    59.         rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);  
    60.         return rv;  
    61.     }  
    62.     public RemoteViews getLoadingView() {  
    63.         return null;  
    64.     }  
    65.     public int getViewTypeCount() {  
    66.         return 2;  
    67.     }  
    68.     public long getItemId(int position) {  
    69.         return position;  
    70.     }  
    71.     public boolean hasStableIds() {  
    72.         return true;  
    73.     }  
    74.     /** 
    75.      * 在AppWidget被第一次加载到屏幕时会自动调用此方法或者调用AppWidgetManager.notifyAppWidgetViewDataChanged()方法也会调用此方法。 
    76.      */  
    77.     public void onDataSetChanged() {  
    78.         if (mCursor != null) {  
    79.             mCursor.close();  
    80.         }  
    81.           
    82.         mCursor = mContext.getContentResolver().query(WeatherDataProvider.CONTENT_URI, null, null, null, null);  
    83.     }  
    84.       
    85. }  

     原文:http://blog.csdn.net/mayingcai1987/article/details/6264213

  • 相关阅读:
    ubuntu防火墙设置通过某端口
    pandas入门
    pyplot入门
    numpy教程
    跨域请求 spring boot
    spring boot 启动流程
    代理配置访问
    AOP,拦截器
    spring boot 启动不连接数据库
    Python 3.x 连接数据库(pymysql 方式)
  • 原文地址:https://www.cnblogs.com/veins/p/3964265.html
Copyright © 2011-2022 走看看