本文翻译整理自:
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; } ... }