天天看点

android widget笔记

一:实现一个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" />