Android 3.0自带的天气预报例子代码

Android 3.0正式版API Level 11中加入了一个天气预报例子,下面是manifest.xml中的关键代码,只是少了xml的编码头:









     <service android:name="WeatherWidgetService"
        android:permission="android.permission.BIND_REMOTEVIEWS"
        android:exported="false" />

     <provider android:name="WeatherDataProvider"
          android:authorities="com.example.android.weatherlistwidget.provider" />
</application>

这里WeatherDataProvider.java的源码为主要是ContentProvider相关的处理,这里作为appWidget的receiver

class WeatherDataPoint {
String city; //城市
int degrees; //度数

WeatherDataPoint(String c, int d) {
    city = c;
    degrees = d;
}

}

public class WeatherDataProvider extends ContentProvider {
public static final Uri CONTENT_URI =
Uri.parse(“content://com.example.android.weatherlistwidget.provider”);
public static class Columns {
public static final String ID = “_id”;
public static final String CITY = “city”;
public static final String TEMPERATURE = “temperature”;
}

 private static final ArrayList<WeatherDataPoint> sData = new ArrayList<WeatherDataPoint>();

@Override
public boolean onCreate() {
    sData.add(new WeatherDataPoint("San Francisco", 13));
    sData.add(new WeatherDataPoint("New York", 1));
    sData.add(new WeatherDataPoint("Seattle", 7));
    sData.add(new WeatherDataPoint("Boston", 4));
    sData.add(new WeatherDataPoint("Miami", 22));
    sData.add(new WeatherDataPoint("Toronto", -10));
    sData.add(new WeatherDataPoint("Calgary", -13));
    sData.add(new WeatherDataPoint("Tokyo", 8));
    sData.add(new WeatherDataPoint("Kyoto", 11));
    sData.add(new WeatherDataPoint("London", -1));
    sData.add(new WeatherDataPoint("Nomanisan", 27));
    return true;
}

@Override
public synchronized Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
    assert(uri.getPathSegments().isEmpty());

     final MatrixCursor c = new MatrixCursor(
            new String[]{ Columns.ID, Columns.CITY, Columns.TEMPERATURE });
    for (int i = 0; i < sData.size(); ++i) {
        final WeatherDataPoint data = sData.get(i);
        c.addRow(new Object[]{ new Integer(i), data.city, new Integer(data.degrees) });
    }
    return c;
}

@Override
public String getType(Uri uri) {
    return "vnd.android.cursor.dir/vnd.weatherlistwidget.citytemperature";
}

@Override
public Uri insert(Uri uri, ContentValues values) {
    return null;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    return 0;
}

@Override
public synchronized int update(Uri uri, ContentValues values, String selection,
        String[] selectionArgs) {
    assert(uri.getPathSegments().size() == 1);

    final int index = Integer.parseInt(uri.getPathSegments().get(0));
    final MatrixCursor c = new MatrixCursor(
            new String[]{ Columns.ID, Columns.CITY, Columns.TEMPERATURE });
    assert(0 <= index && index < sData.size());
    final WeatherDataPoint data = sData.get(index);
    data.degrees = values.getAsInteger(Columns.TEMPERATURE);

     getContext().getContentResolver().notifyChange(uri, null);
    return 1;
}

}

上面可以看到,对于插入和删除没有做过多的处理,对于天气更新给出了详细的解决方法。

有关 WeatherWidgetProvider.java 主要是appWidget的核心,为provider

class WeatherDataProviderObserver extends ContentObserver { //监控数据库的变化
private AppWidgetManager mAppWidgetManager;
private ComponentName mComponentName;

WeatherDataProviderObserver(AppWidgetManager mgr, ComponentName cn, Handler h) {
    super(h);
    mAppWidgetManager = mgr;
    mComponentName = cn;
}

@Override
public void onChange(boolean selfChange) {
       mAppWidgetManager.notifyAppWidgetViewDataChanged(
            mAppWidgetManager.getAppWidgetIds(mComponentName), R.id.weather_list);
}

}

public class WeatherWidgetProvider extends AppWidgetProvider {
public static String CLICK_ACTION = “com.example.android.weatherlistwidget.CLICK”;
public static String REFRESH_ACTION = “com.example.android.weatherlistwidget.REFRESH”;
public static String EXTRA_CITY_ID = “com.example.android.weatherlistwidget.city”;

private static HandlerThread sWorkerThread;
private static Handler sWorkerQueue;
private static WeatherDataProviderObserver sDataObserver;

public WeatherWidgetProvider() {
     sWorkerThread = new HandlerThread("WeatherWidgetProvider-worker"); //开一个线程,这里用到了HandlerThread
    sWorkerThread.start();
    sWorkerQueue = new Handler(sWorkerThread.getLooper()); //不了解Thread的Looper可以看下这个例子比较简单清晰
}

@Override
public void onEnabled(Context context) { //当appWidget添加到桌面上时
       final ContentResolver r = context.getContentResolver();
    if (sDataObserver == null) {
        final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);
        sDataObserver = new WeatherDataProviderObserver(mgr, cn, sWorkerQueue);
        r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver); //注册监控数据库变化的回调
    }
}

@Override
public void onReceive(Context ctx, Intent intent) {
    final String action = intent.getAction();
    if (action.equals(REFRESH_ACTION)) { //接收数据库改变的回调广播
           final Context context = ctx;
        sWorkerQueue.removeMessages(0);
        sWorkerQueue.post(new Runnable() {
            @Override
            public void run() {
                final ContentResolver r = context.getContentResolver();
                final Cursor c = r.query(WeatherDataProvider.CONTENT_URI, null, null, null, 
                        null);
                final int count = c.getCount();
                final int maxDegrees = 96;

                r.unregisterContentObserver(sDataObserver); //首先取消数据库监控
                for (int i = 0; i < count; ++i) {
                    final Uri uri = ContentUris.withAppendedId(WeatherDataProvider.CONTENT_URI, i);
                    final ContentValues values = new ContentValues();
                    values.put(WeatherDataProvider.Columns.TEMPERATURE,
                            new Random().nextInt(maxDegrees));
                    r.update(uri, values, null, null); //更新数据库记录层
                }
                r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver); //重新设置监视数据库

                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);
                mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn), R.id.weather_list); //提示Widget有数据更新并刷新UI
            }
        });
    } else if (action.equals(CLICK_ACTION)) {
         final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
        final String city = intent.getStringExtra(EXTRA_CITY_ID);
        final String formatStr = ctx.getResources().getString(R.string.toast_format_string);
        Toast.makeText(ctx, String.format(formatStr, city), Toast.LENGTH_SHORT).show();
    }

    super.onReceive(ctx, intent);
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
             for (int i = 0; i < appWidgetIds.length; ++i) {
                  final Intent intent = new Intent(context, WeatherWidgetService.class);   //当桌面上有多个这个相同的appWidget需要分别处理
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        rv.setRemoteAdapter(appWidgetIds[i], R.id.weather_list, intent);

        rv.setEmptyView(R.id.weather_list, R.id.empty_view);

        final Intent onClickIntent = new Intent(context, WeatherWidgetProvider.class);
        onClickIntent.setAction(WeatherWidgetProvider.CLICK_ACTION);
        onClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
        onClickIntent.setData(Uri.parse(onClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
        final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0,
                onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        rv.setPendingIntentTemplate(R.id.weather_list, onClickPendingIntent);

        final Intent refreshIntent = new Intent(context, WeatherWidgetProvider.class);
        refreshIntent.setAction(WeatherWidgetProvider.REFRESH_ACTION);
        final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0,
                refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        rv.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);

        appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
    }
    super.onUpdate(context, appWidgetManager, appWidgetIds);
}

}

对于WeatherWidgetService.java这个Service集成于RemoteViewsService,主要是UI上的处理

public class WeatherWidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
}
}

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context mContext;
private Cursor mCursor;
private int mAppWidgetId;

public StackRemoteViewsFactory(Context context, Intent intent) {
    mContext = context;
    mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
            AppWidgetManager.INVALID_APPWIDGET_ID);
}

public void onCreate() {
}

public void onDestroy() {
    if (mCursor != null) {
        mCursor.close();
    }
}

public int getCount() {
    return mCursor.getCount();
}

public RemoteViews getViewAt(int position) {
    String city = "Unknown City";
    int temp = 0;
    if (mCursor.moveToPosition(position)) {
        final int cityColIndex = mCursor.getColumnIndex(WeatherDataProvider.Columns.CITY);
        final int tempColIndex = mCursor.getColumnIndex(
                WeatherDataProvider.Columns.TEMPERATURE);
        city = mCursor.getString(cityColIndex);
        temp = mCursor.getInt(tempColIndex);
    }

    final String formatStr = mContext.getResources().getString(R.string.item_format_string);
    final int itemId = (position % 2 == 0 ? R.layout.light_widget_item
            : R.layout.dark_widget_item);
    RemoteViews rv = new RemoteViews(mContext.getPackageName(), itemId);
    rv.setTextViewText(R.id.widget_item, String.format(formatStr, temp, city));

    final Intent fillInIntent = new Intent();
    final Bundle extras = new Bundle();
    extras.putString(WeatherWidgetProvider.EXTRA_CITY_ID, city);
    fillInIntent.putExtras(extras);
    rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);

    return rv;
}
public RemoteViews getLoadingView() {
    return null;
}

public int getViewTypeCount() {
    return 2;
}

public long getItemId(int position) {
    return position;
}

public boolean hasStableIds() {
    return true;
}

public void onDataSetChanged() {
    if (mCursor != null) {
        mCursor.close();
    }
    mCursor = mContext.getContentResolver().query(WeatherDataProvider.CONTENT_URI, null, null,
            null, null);
}

}