Widgets高级篇(三)

来源:互联网 发布:技校软件开发 编辑:程序博客网 时间:2024/04/29 17:10
本文翻译整理自:http://developer.android.com/guide/topics/appwidgets/index.html#implementing_collections
3.6、设置数据项View的行为
前文已经告诉你如何绑定您的数据到你的app widget collection。但是如何为数据项View添加动态的行为呢?本部分将讲述关于次的详细内容。
在普通的App Widgets中,我们可以通过setOnClickPendingIntent()来设置View控件Click时,发送的Intent.通过次方法,我们就可以实现当用户点击一个按钮时,就启动一个Activity.但是这种方法对app widget collection的数据集的数据项View并不适用(你可以像Gmail app widget中一样通过setOnClickPendingIntent()来设置global button的click行为来启动一个应用程序, 但是对数据集的数据项View不能使用这种方法)。替代的是首先使用setOnClickFillInIntent()方法为数据集的数据项View统一设置其Pending的Intent模板,然后在RemoteViewsFactory中为数据集的数据项View的Pending的Intent模板的设置填充Intent。这样当数据项View在Click时,发送的Intent将是其Pending的Intent模板和其Pending的Intent模板填充Intent合成的Intent。

在这里我们将使用StackView Widget示例程序来说明如何为数据项View添加Click行为。在StackView Widget示例程序中,当你单击一个数据项View,它将显示一个Toast消息 "Touched view n,"n是它在touched view中的 index (position)。其主要执行流程如下:
第一步:首先,在StackWidgetProvider(an AppWidgetProvider subclass)中使用setOnClickFillInIntent()方法为数据集的数据项View统一设置其Pending的Intent模板,该intent有一个自定义的名叫TOAST_ACTION的action.在RemoteViewsFactory中为数据集的数据项View的Pending的Intent模板的设置填充Intent,填充Intent通过Extras数据项View的index (position)携带在其中
第二步、用户点击数据项View,其Pending的Intent模板和其Pending的Intent模板填充Intent被合成一个Intent,该Intent被激活并被发送。
第三步、数据项View发送的Intent广播被StackWidgetProvider接收,被传递到它的onReceive()中。
onReceive()方法中,我们从Intent的Extra中提取其数据项view的index (position),然后发送Toast消息。
注意StackView Widget示例程序中使用的是把Intent发送到广播StackWidgetProvider),但是一般的app widget只是简单的启动一
个Activity
3.6.1、设置数据项View的pending intent模板
在StackWidgetProvider (AppWidgetProvider subclass),数据项View的pending intent模板被统一进行设置.
不能对单个的数据项View的进行pending intent模板.你必须对数据集的数据项View的Pending的Intent模板进行统一设置,
对于数据集的单个数据项View只能进行填充Intent设置,以便让每个数据项View都有个性化的Click行为。
在StackWidgetProvider中,它接受了用户click数据项View所发送的Intent,并在onReceive()对其进行了处理。
如果发现intent的action是TOAST_ACTION(即为用户click数据项View所发送的Intent), app widget 将显示一个关于当前数据项View的Toast消息.
示例7
public class StackWidgetProvider extends AppWidgetProvider {
   
public static final String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION";
   
public static final String EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM";

   
...

   
// Called when the BroadcastReceiver receives an Intent broadcast.
   
// Checks to see whether the intent's action is TOAST_ACTION. If it is, the app widget
   
// displays a Toast message for the current item.
   
@Override
   
public void onReceive(Context context, Intent intent) {
       
AppWidgetManager mgr = AppWidgetManager.getInstance(context);
       
if (intent.getAction().equals(TOAST_ACTION)) {
           
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
               
AppWidgetManager.INVALID_APPWIDGET_ID);
           
int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
           
Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
       
}
       
super.onReceive(context, intent);
   
}
   
   
@Override
   
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
       
// update each of the app widgets with the remote adapter
       
for (int i = 0; i < appWidgetIds.length; ++i) {
   
           
// Sets up the intent that points to the StackViewService that will
           
// provide the views for this collection.
           
Intent intent = new Intent(context, StackWidgetService.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.widget_layout);
            rv
.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
   
           
// The empty view is displayed when the collection has no items. It should be a sibling
           
// of the collection view.
            rv
.setEmptyView(R.id.stack_view, R.id.empty_view);

           
// This section makes it possible for items to have individualized behavior.
           
// It does this by setting up a pending intent template. Individuals items of a collection
           
// cannot set up their own pending intents. Instead, the collection as a whole sets
           
// up a pending intent template, and the individual items set a fillInIntent
           
// to create unique behavior on an item-by-item basis.
           
Intent toastIntent = new Intent(context, StackWidgetProvider.class);
           
// Set the action for the intent.
           
// When the user touches a particular view, it will have the effect of
           
// broadcasting TOAST_ACTION.
            toastIntent
.setAction(StackWidgetProvider.TOAST_ACTION);
            toastIntent
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
            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.stack_view, toastPendingIntent);
           
            appWidgetManager
.updateAppWidget(appWidgetIds[i], rv);
       
}
   
super.onUpdate(context, appWidgetManager, appWidgetIds);
   
}
}
3.6.2、设置数据项View的填充intent
你必须在RemoteViewsFactory中对数据集的每个数据项View设置填充intent,以用于区分是哪个数据项View.
当用户点击数据项View,其Pending的Intent模板和其填充Intent被合成一个最终的Intent,并进行广播发送。
示例8
public class StackWidgetService extends RemoteViewsService {
   
@Override
   
public RemoteViewsFactory onGetViewFactory(Intent intent) {
       
return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
   
}
}

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
   
private static final int mCount = 10;
   
private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
   
private Context mContext;
   
private int mAppWidgetId;

   
public StackRemoteViewsFactory(Context context, Intent intent) {
        mContext
= context;
        mAppWidgetId
= intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
               
AppWidgetManager.INVALID_APPWIDGET_ID);
   
}

   
// Initialize the data set.
       
public void onCreate() {
           
// In onCreate() you set up any connections / cursors to your data source. Heavy lifting,
           
// for example downloading or creating content etc, should be deferred to onDataSetChanged()
           
// or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
           
for (int i = 0; i < mCount; i++) {
                mWidgetItems
.add(new WidgetItem(i + "!"));
           
}
           
...
       
}
       
...
   
       
// Given the position (index) of a WidgetItem in the array, use the item's text value in
       
// combination with the app widget item XML file to construct a RemoteViews object.
       
public RemoteViews getViewAt(int position) {
           
// position will always range from 0 to getCount() - 1.
   
           
// Construct a RemoteViews item based on the app widget item XML file, and set the
           
// text based on the position.
           
RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
            rv
.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);
   
           
// Next, set a fill-intent, which will be used to fill in the pending intent template
           
// that is set on the collection view in StackWidgetProvider.
           
Bundle extras = new Bundle();
            extras
.putInt(StackWidgetProvider.EXTRA_ITEM, position);
           
Intent fillInIntent = new Intent();
            fillInIntent
.putExtras(extras);
           
// Make it possible to distinguish the individual on-click
           
// action of a given item
            rv
.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
       
           
...
       
           
// Return the RemoteViews object.
           
return rv;
       
}
   
...
   
}