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