1.准备appwidget的初始布局和预览视图等资源
AppWidget集合应用时,需配置两种布局文件:一个用于集合view布局显示,一个用于集合view上条目的的布局显示
注意:要设置empty views,以便匹配集合view的空状态,且该View必须是集合View(一般可为ListView、GridView、StackView或AdapterViewFlipper)的同级View
2.在res/xml中创建AppWidgetProviderInfo文件
根标签为appwiget-provider,其属性要包括initialLayout、previewImage、autoAdvanceViewId
等,具体可参考后面的例子
3.创建AppWidgetProvider的实现子类,并在清单文件里面注册引用的元数据和监听的广播action
清单文件中,需给该子类配置如下信息:
action
android:name="android.appwidget.action.APPWIDGET_UPDATE"
元数据:android:name="android.appwidget.provider"
代码中,onUpdate方法中完成Widget布局到桌面上时的刷新;onReceive方法中完成接收到特定广播后的Widget刷新
(另外,如果配置了Configure
Activity还需要注册action,"android.appwidget.action.APPWIDGET_CONFIGURE")
4.因为是集合Widget,需要创建一个继承了RemoteViewsService的Service子类,并在清单文件中注册权限,阻止其它程序自由访问Widget中的数据
android:permission="android.permission.BIND_REMOTEVIEWS"
代码中通过onGetViewFactory方法返回一个RemoteViewsFactory的实现类,该实现类中的onCreate()用来创建及初始化设置Widget的单个item;getViewAt()方法用来同步数据,进行耗时的更新操作,并返回RemoteView对象。
getViewAt方法中可完成:
<1>将显示独立Widget条目的view
item数据以fill-in
intent方式填充到集合Widget的View集合中
View集合中Chilid
view不允许使用setOnClickPendingIntent的点击事件动态更新,只能通过setOnClickFillInIntent来实现,
即先设置好集合view的pending
intent template点击事件模版,然后,将独立的chilid
view条目的the
fill-in Intent意图事件填充进来。
<2>进行耗时的处理图片和获取网络数据操作,并同步刷新Widget的显示内容。
RemoteViewsFactory的实现类中还有一个onDataSetChanged方法,也可进行耗时的处理图片和获取网络数据操作,但他可以保持Widget的当前状态,直到我们显示调用AppWidgetManager
notifyAppWidgetViewDataChanged才会触发
参考:ExampleStackViewAppWidgets
本例主要参考http://developer.android.com/resources/samples/StackWidget/index.html
在其基础上略有改进
appwidget_providerinfo.xml
<?xml
version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:autoAdvanceViewId="@id/statck_view"
android:initialLayout="@layout/widget_layout"
android:minHeight="74dp"
android:minWidth="290dp"
android:previewImage="@drawable/doraemon"
android:updatePeriodMillis="3600000"
android:resizeMode="vertical">
</appwidget-provider>
StackViewAppWidgetService.java
/**
*
实现了给StackView上ImageView适配背景图片
*
@author wgyang
*
*/
public
class StackViewAppWidgetService extends RemoteViewsService {
@Override
public
RemoteViewsFactory onGetViewFactory(Intent intent) {
return
new ViewFlipperRemoteViewsFactory(this.getApplicationContext(),
intent);
}
}
class
ViewFlipperRemoteViewsFactory implements
RemoteViewsService.RemoteViewsFactory
{
int[]
images = { R.drawable.doraemon, R.drawable.baidu, R.drawable.bing,
R.drawable.google
};
//int[]
colors = { Color.BLUE, Color.RED, Color.GRAY, Color.GREEN };
private
static final int mCount = 4;
private
List<ImageView> mImageViews = new ArrayList<ImageView>();
private
Context mContext;
private
static ImageView mImageView;
public
ViewFlipperRemoteViewsFactory(Context context, Intent intent) {
mContext
= context;
}
public
void onCreate() {
//
In onCreate() you setup 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.
//
因不是很耗时,可放在这里
//在循环里控制显示独立Widget条目的界面信息
for
(int i = 0; i < mCount; ++i) {
mImageView
= new ImageView(mContext);
//mImageView.setBackgroundColor(colors[i]);
//mImageView.setImageResource(images[i]);
mImageViews.add(mImageView);
}
// We sleep for 3 seconds here to show how the empty view appears
in the interim.
//
The empty view is set in the StackWidgetProvider and should be a
sibling of the
//
collection view.
}
public
RemoteViews getViewAt(int position) {
// position will always range from 0 to getCount() - 1.
//
We construct a remote views item based on our widget item xml file,
and set the
//
text based on the position.
//通过Remoteview给view
item上设置要显示内容
RemoteViews
rv = new RemoteViews(mContext.getPackageName(),
R.layout.widget_item);
//
rv.setTextViewText(R.id.widget_item,
//
mWidgetItems.get(position).text);
//rv.setImageViewResource(R.id.widget_item,
mImageViews.get(position)
// .getBackground().getLevel());
//注意要将单个的widget条目信息设置进rv中,不然刷新界面不成功的
rv.setImageViewBitmap(R.id.widget_item,
BitmapFactory.decodeResource(
mContext.getResources(),
images[position]));
// Next, we set a fill-intent which will be used to fill-in the
pending intent template
//
which is set on the collection view in StackWidgetProvider.
//item的位置信息设置到fillintent中
Bundle
extras = new Bundle();
extras.putInt(StackViewAppWidgetProvider.EXTRA_ITEM,
position);
//设置fill-intent以填充Pending
intent 模版
Intent
fillInIntent = new Intent();
fillInIntent.putExtras(extras);
rv.setOnClickFillInIntent(R.id.widget_item,
fillInIntent);
// You can do heaving lifting in here, synchronously. For example,
if you need to
//
process an image, fetch something from the network, etc., it is ok to
do it here,
//
synchronously. A loading view will show up in lieu of the actual
contents in the
//
interim.
//其它耗时操作可在这里完成
return
rv;
}
public
int getCount() {
return
mCount;
}
public
RemoteViews getLoadingView() {
// You can create a custom loading view (for instance when
getViewAt() is slow.) If you
//
return null here, you will get the default loading view.
return
null;
}
public
int getViewTypeCount() {
return
1;//注意下,初始是0,改变与否不影响
}
public
long getItemId(int position) {
return
position;
}
public
boolean hasStableIds() {
return
true;//注意下,原来是false,改变与否不影响
}
public
void onDataSetChanged() {
// This is triggered when you call AppWidgetManager
notifyAppWidgetViewDataChanged
//
on the collection view corresponding to this factory. You can do
heaving lifting in
//
here, synchronously. For example, if you need to process an image,
fetch something
//
from the network, etc., it is ok to do it here, synchronously. The
widget will remain
//
in its current state while work is being done here, so you don‘t need
to worry about
//
locking up the widget.
//耗时操作可放在这里,可显式触发
}
public
void onDestroy() {
//
In onDestroy() you should tear down anything that was setup for your
data source,
//
eg. cursors, connections, etc.
mImageViews.clear();
}
}
StackViewAppWidgetProvider.java
/**
*
StackView更新图片
*
定时接收广播,自动刷新view,另外监听点击事件,弹土司消息反馈点击的是哪一个条目
*
@author wgyang
*
*/
public
class StackViewAppWidgetProvider extends AppWidgetProvider {
public
static final String TOAST_ACTION =
"com.lenove.widgetcollection.TOAST_ACTION";
public
static final String EXTRA_ITEM =
"com.lenove.widgetcollection.EXTRA_ITEM";
@Override
public
void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(TOAST_ACTION)) {
int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
Toast.makeText(context, "Touched view " +
viewIndex, Toast.LENGTH_SHORT).show();
}
super.onReceive(context,
intent);
}
//
完成Widget布局到桌面上时的刷新
@Override
public
void onUpdate(Context context, AppWidgetManager
appWidgetManager,int[] appWidgetIds) {
// update each of the widgets with the remote adapter
//遍历每个appWidgetId
for (int i = 0; i < appWidgetIds.length; ++i) {
//
Here we setup the intent which points to the StackViewService which
will
//
provide the views for this collection.
//指定启动到RemoteViewstService的Intent
Intent
intent = new Intent(context, StackViewAppWidgetService.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,以避免intent中的数据被忽略掉
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
//借助RemoteView传递信息
RemoteViews
rv = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
//注意,设置适配器
rv.setRemoteAdapter(appWidgetIds[i],
R.id.statck_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.statck_view,
R.id.empty_view);
//
Here we setup the a pending intent template. Individuals items of a
collection
//
cannot setup their own pending intents, instead, the collection as a
whole can
//
setup a pending intent template, and the individual items can set a
fillInIntent
//
to create unique before on an item to item basis.
//设置封装了操作信息的intnent--最终放到pendingIntentTemplate中
Intent
toastIntent = new Intent(context, StackViewAppWidgetProvider.class);
toastIntent.setAction(StackViewAppWidgetProvider.TOAST_ACTION);
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
//设置延迟意图模版PendingIntentTemplete,加载到RemoteViews,最后由AppWidgetManager更新
PendingIntent
toastPendingIntent = PendingIntent.getBroadcast(context, 0,
toastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
rv.setPendingIntentTemplate(R.id.statck_view,
toastPendingIntent);
//如果有耗时的操作应该启动应该服务Service去完成
appWidgetManager.updateAppWidget(appWidgetIds[i],
rv);
}
super.onUpdate(context,
appWidgetManager, appWidgetIds);
}
}
摘自:http://wenku.baidu.com/view/aab74fa4b0717fd5360cdc4a.html
android4.0中实现AppWidget集合