一:实现一个Activity配置widget
(1):在widget配置文件中加入如下代码:
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="250dp"
android:minHeight="180dp"
android:updatePeriodMillis="0"
android:initialLayout="@layout/widget_layout"
android:resizeMode="vertical"
android:configure="xx.widgets.ExampleAppWidgetConfigure"
android:previewImage="@drawable/ic_launcher">
</appwidget-provider>
(2):在Activity中加入如下代码:
Intent launchIntent = getIntent();
Bundle extras = launchIntent.getExtras();
if (extras != null) {
appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);
Intent cancelResultValue = new Intent();
cancelResultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
//未配置完成返回结果
setResult(RESULT_CANCELED, cancelResultValue);
} else {
// only launch if it's for configuration
// Note: when you launch for debugging, this does prevent this
// activity from running. We could also turn off the intent
// filtering for main activity.
// But, to debug this activity, we can also just comment the
// following line out.
finish();
}
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID)
{
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
//配置完成返回结果
setResult(RESULT_OK, resultValue);
}
二:如何在RemoteViews中使用ListView
(1):通过RemoteViewsService 渲染器实现
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.IBinder;
import android.os.Looper;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
public class UpdateService extends RemoteViewsService {
@Override
public void onStart(Intent intent, int startId) {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return super.onBind(intent);
}
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new ListRemoteViewsFactory(this.getApplicationContext(), intent);
}
class ListRemoteViewsFactory implements
RemoteViewsService.RemoteViewsFactory {
private final Context mContext;
private final List<String> mList;
public ListRemoteViewsFactory(Context context, Intent intent) {
mContext = context;
mList = getDataSources();//获取数据源
if (Looper.myLooper() == null) {
Looper.prepare();
}
}
@Override
public void onCreate() {
}
@Override
public void onDataSetChanged() {
}
@Override
public void onDestroy() {
mList.clear();
}
@Override
public int getCount() {
return mList.size();
}
@Override
public RemoteViews getViewAt(int position) {
if (position < 0 || position >= mList.size())
return null;
String content = mList.get(position);
final RemoteViews rv = new RemoteViews(mContext.getPackageName(),
R.layout.row);
Intent intent = new Intent();
// TODO
// intent.setComponent(new ComponentName("包名", "类名"));
// 与CustomWidget中remoteViews.setPendingIntentTemplate配对使用实现ListView中的Item的点击事件
rv.setOnClickFillInIntent(R.id.widget_list_item_layout, intent);
rv.setTextViewText(android.R.id.text1, content);
return rv;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean hasStableIds() {
return true;
}
}
public List<String>getDataSources() {
List<String> list = new ArrayList<String>();
......
return list;
}
}
(2):在widget的onUpdate中添加代码:
for (int i = 0; i < appWidgetIds.length; i++) {//如果使用循环,当调试重新运行程序或者重启机器时widget点击事情会失灵
Intent intent = new Intent(ctxt, WidgetService.class);
RemoteViews widget = new RemoteViews(ctxt.getPackageName(),
R.layout.widget);
widget.setRemoteAdapter(appWidgetIds[i], R.id.words, intent);
Intent clickIntent = new Intent(ctxt, LoremActivity.class);
PendingIntent clickPI = PendingIntent.getActivity(ctxt, 0,
clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setPendingIntentTemplate(R.id.words, clickPI);//配合getViewAt方法中的setOnClickFillInIntent实现listView中Item的点击事件
appWidgetManager.updateAppWidget(appWidgetIds[i], widget);
}
(3): widget.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/words"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="3dp"
android:layout_marginLeft="3dp"
android:background="@drawable/widget_frame"
/>
(4):row.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:minHeight="?android:attr/listPreferredItemHeight"
/>
三: 如何更新widget
(1):定时更新
在widget配置文件中有这句 android:updatePeriodMillis="0",这种更新方式已经不能使用了,一般定时更新使用AlarmManager,如下代码
private void setAlarm(Context context, int appWidgetId, int updateRateSeconds) {
Intent widgetUpdate = new Intent();
widgetUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId });
// make this pending intent unique by adding a scheme to it
widgetUpdate.setData(Uri.withAppendedPath(Uri.parse(ImagesWidgetProvider.URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId)));
PendingIntent newPending = PendingIntent.getBroadcast(context, 0, widgetUpdate, PendingIntent.FLAG_UPDATE_CURRENT);
// schedule the updating
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (updateRateSeconds >= 0) {
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), updateRateSeconds * 1000, newPending);
} else {
alarms.cancel(newPending);
}
}
(2):事件更新,不通过时间更新widget一般使用广播来更新
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getAction().equals(
"com.example.widgettestone.WIDGET_CONTROL")) {
AppWidgetManager manager = AppWidgetManager.getInstance(context);
ComponentName thisWidget = new ComponentName(context, TaskPad.class);
int[] appWidgetIds = manager.getAppWidgetIds(thisWidget);
manager.notifyAppWidgetViewDataChanged(appWidgetIds,R.id.widget_list);//通知RemoteViews刷新数据,在渲染器中重写onDataSetChanged方法重写获取数据源
} else {
super.onReceive(context, intent);
}
}
AndroidManifest.xml中widget的配置中添加:
<action android:name="com.example.widgettestone.WIDGET_CONTROL" />
当需要更新的时候发出该广播即可更新:
Intent intentUpdate = new Intent("com.example.widgettestone.WIDGET_CONTROL");
context.sendBroadcast(intentUpdate);
四:小问题:
(1):ListView的getview()中重复调用(position重复调用)问题:
网友提供的解决方法:
重写的listview adapter中,在getView()方法中,打印语句时,相同的position打印了多次,修改方法:
将布局文件中ListView的高度改为“fill_parent”
<ListView
android:id="@+id/dynamic_list"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:scrollbars="vertical"
android:layout_weight="1"
android:drawSelectorOnTop="false"
android:fadingEdgeLength="0dip"
android:divider="@null"
android:dividerHeight="0dip"
android:cacheColorHint="#00000000"
android:background="@color/listview_bg_color" />