android App Widgets

时间:2024-01-21 12:32:27

http://developer.android.com/guide/practices/ui_guidelines/widget_design.html#design

http://developer.android.com/guide/topics/appwidgets/index.html

http://developer.android.com/guide/topics/appwidgets/host.html

http://www.cnblogs.com/alwaysyouare/archive/2010/01/06/1640219.html   
digitalclock Widget

Declaring an App Widget in the Manifest


First, declare the AppWidgetProvider class in yourapplication'sAndroidManifest.xml file. For example:

<receiver android:name="ExampleAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>

Adding the AppWidgetProviderInfo Metadata


The AppWidgetProviderInfo defines the essential qualities of an App Widget, such as its minimum layout dimensions,
its initiallayout resource,how often to update the App Widget, and (optionally) a configuration Activity tolaunch at create-time.Define the AppWidgetProviderInfo object in an XML resource using a single<appwidget-provider> element and save it
in the project'sres/xml/ folder.

For example:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/preview"
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen|keyguard"
android:initialKeyguardLayout="@layout/example_keyguard">
</appwidget-provider>

The updatePeriodMillis attribute defines how often the AppWidget framework should request an update from theAppWidgetProvider
by calling theonUpdate() callback
method. The actual updateis not guaranteed to occur exactly on time with this value and we suggestupdating as infrequently as possible—perhaps no more than once an hour toconserve the battery. You might also allow the user to adjust the frequency in aconfiguration—some
people might want a stock ticker to update every 15minutes, or maybe only four times a day.

Note: If the device is asleep when itis time for an update (as defined byupdatePeriodMillis), then the device willwake up in order to perform the update. If you don't update more than once per hour, thisprobably
won't cause significant problems for the battery life.If, however, you needto update more frequently and/or you do not need to update while the device is asleep,then you can instead perform updates based on an alarm that
will not wake the device. To doso, set an alarm with an Intent that your AppWidgetProvider receives, using theAlarmManager. Set
the alarm type to eitherELAPSED_REALTIME orRTC,
which will only deliver the alarm when the device is awake. Then setupdatePeriodMillis to zero ("0").

Creating the App Widget Layout


You must define an initial layout for your App Widget in XML and save it inthe project'sres/layout/ directory. You can design your App Widget using theView objects listedbelow, but before you begin designing your App Widget, please read andunderstand
theApp WidgetDesign Guidelines.

# of Cells

(Columns or Rows)
Available Size (dp)

(minWidth or minHeight)
1 40dp
2 110dp
3 180dp
4 250dp
n 70 × n − 30

Using the AppWidgetProvider Class


You must declare your AppWidgetProvider class implementation as abroadcast receiver using the<receiver> element in the AndroidManifest (seeDeclaring
an App Widget in the Manifest
above).

The AppWidgetProvider class extendsBroadcastReceiver as a convenienceclass to handle the App Widget broadcasts. The
AppWidgetProvider receives onlythe event broadcasts thatare relevant to the App Widget, such as when the App Widget is updated, deleted,enabled, and disabled.When these broadcast events occur, the AppWidgetProvider receives the followingmethod calls:

onUpdate()
This is called to update the App Widget at intervals defined by theupdatePeriodMillis attribute in the AppWidgetProviderInfo (seeAdding
the AppWidgetProviderInfo Metadata
above). This method is also called when the user adds the App Widget, so it should perform the essential setup, such as define event handlers for Views and start a temporaryService,
if necessary. However, if you have declared aconfiguration Activity,this method is not called when the user adds theApp Widget, but is called for the subsequent updates. It is the responsibility of the configuration Activity to perform the
first update when configuration isdone. (SeeCreating an App Widget ConfigurationActivity below.)

The most important AppWidgetProvider callback is onUpdate()
because it is called wheneach App Widget is added to a host (unless you use a configuration Activity). Ifyour App Widget accepts any user interaction events, then you need to registerthe event handlers in this callback. If your App Widget doesn't create temporaryfiles
or databases, or perform other work that requires clean-up, then onUpdate()
may be the only callbackmethod you need to define. For example, if you want an App Widget with a buttonthat launches an Activity when clicked, you could use the followingimplementation of AppWidgetProvider:

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;         // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];             // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);             // Get the layout for the App Widget and attach an on-click listener
            // to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);             // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

This AppWidgetProvider defines only the onUpdate()
method for the purpose ofdefining a PendingIntent that launches anActivity
and attaching it to the App Widget's button withsetOnClickPendingIntent(int,
PendingIntent)
. Noticethat it includes a loop that iterates through each entry inappWidgetIds, which is an array of IDs that identify each AppWidget created by this provider. In this way, if the user creates more than oneinstance of
the App Widget, then they are all updated simultaneously. However,only one
updatePeriodMillis
schedule will be managed for allinstances of the App Widget. For example, if the update schedule is defined tobe every two hours, and a second instance of the App Widget is added one hourafter the first one, then they will both be
updated on the period defined by thefirst one and the second update period will be ignored (they'll both be updatedevery two hours, not every hour).

Note: Because AppWidgetProvider is an extension ofBroadcastReceiver,
your process is not guaranteed to keeprunning after the callback methods return (seeBroadcastReceiver for information about
the broadcastlifecycle). If your App Widget setup process can take several seconds (perhapswhile performing web requests) and you require that your process continues,consider starting aService
in theonUpdate() method. From within
the Service, you can perform your own updatesto the App Widget without worrying about the AppWidgetProvider closing down dueto anApplicationNot Responding
(ANR) error. See the Wiktionary sample's AppWidgetProvider for an example of an App Widget running aService.

Also see the ExampleAppWidgetProvider.javasample class.

Receiving App Widget broadcast Intents

AppWidgetProvider is just a convenience class. Ifyou would liketo receive the App Widget broadcasts directly, you can
implement your own BroadcastReceiver or override theonReceive(Context,
Intent)
callback. The Intents you need to care about are as follows:

note: you can get appWidgetIds from broadcast intents of ACTION_APPWIDGET_UPDATE
as below:

                final int[] appWidgetIds = intent.getExtras().getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);



AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ango.angowidget"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<receiver android:name="AppWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="AUTO_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" />
</receiver>
</application> </manifest>

appwidget_info.xml

<?xml version="1.0" encoding="UTF-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="250dp"
android:minHeight="110dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/digitalclock"/>

digitalclock.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- res/layout/bday_widget.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="250dp"
android:layout_height="110dp"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="60dp"
>
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="0"
android:gravity="center"
android:textSize="30sp"
android:textStyle="bold"
android:layout_weight="50"
android:textColor="#ff000000"
android:background="#FFFFFF"
/>
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:textSize="20sp"
android:text="Buy"
android:layout_weight="50"
android:background="#30C326"
android:textColor="#ff000000"
android:gravity="center"
/>
</LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:background="#FF6633"
android:layout_height="match_parent" > <ImageView
android:id="@+id/profiles"
android:layout_width="40dp"
android:layout_height="match_parent"
android:src="@drawable/ic_settings_profiles" /> </LinearLayout> </LinearLayout>

AppWidget.java

package com.ango.angowidget;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date; //import android.R;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.IBinder;
import android.os.SystemClock;
import android.provider.AlarmClock;
import android.text.format.Time;
import android.util.Log;
import android.widget.RemoteViews; public class AppWidget extends AppWidgetProvider
{
public static final String ACTION_AUTO_UPDATE = "AUTO_UPDATE";
private final int INTERVAL_MILLIS = 60000;
public static final String URI_SCHEME = "images_widget"; @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
//Log.d(ACTION_AUTO_UPDATE, "onUpdate(): ");
myUpdate(context, appWidgetIds);
} public void myUpdate(Context context, int[] appWidgetIds){
//Time estTime = new Time("EST");
//Time estTime = new Time("CCT");
Time estTime = new Time();
for (int appWidgetId : appWidgetIds) {
estTime.setToNow();
RemoteViews updateViews =
new RemoteViews(context.getPackageName(),
R.layout.digitalclock);
updateViews.setTextViewText(R.id.time, estTime.format("%H:%M"));
SimpleDateFormat dateformat1=new SimpleDateFormat("yyyy-MM-dd"+"\r\n"+"E");
updateViews.setTextViewText(R.id.date, dateformat1.format(new Date()) ); //Intent intent=new Intent("/");
//ComponentName cn=new ComponentName("com.android.settings", "com.android.settings.Settings");
//intent.setComponent(cn);
Intent intent=new Intent(AlarmClock.ACTION_SET_ALARM);
PendingIntent pendingIntent =
PendingIntent.getActivity(context,
0 /* no requestCode */,
intent,
0 /* no flags */);
updateViews.setOnClickPendingIntent(R.id.time, pendingIntent); Intent i = new Intent();
ComponentName cn=new ComponentName("com.android.calendar", "com.android.calendar.LaunchActivity");
i.setComponent(cn);
PendingIntent datePendingIntent =
PendingIntent.getActivity(context,
0 /* no requestCode */,
i,
0 /* no flags */);
updateViews.setOnClickPendingIntent(R.id.date, datePendingIntent); Intent intentProfiles=new Intent("/");
ComponentName cnProfiles=new ComponentName("com.android.settings", "com.android.settings.profile.ProfileSettings");
intentProfiles.setComponent(cnProfiles);
PendingIntent ProfilesPendingIntent =
PendingIntent.getActivity(context,
0 /* no requestCode */,
intentProfiles,
0 /* no flags */);
updateViews.setOnClickPendingIntent(R.id.profiles, ProfilesPendingIntent); //ComponentName thisWidget = new ComponentName(this,AppWidget.class); AppWidgetManager manager = AppWidgetManager.getInstance(context);
//int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
manager.updateAppWidget(appWidgetId, updateViews);
} } @Override
public void onReceive(Context context, Intent intent)
{
final String action = intent.getAction();
//Log.v(ACTION_AUTO_UPDATE, "onReceive:"+action); if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
final int appWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
} // action must be defined in intent-filter part of AndroidManifest.xml
//
// <receiver android:name="AppWidget" >
// <intent-filter>
// <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
// </intent-filter>
// <intent-filter>
// <action android:name="AUTO_UPDATE" />
// </intent-filter>
// <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" />
// </receiver>
if(action.equals(ACTION_AUTO_UPDATE))
{
// DO SOMETHING
//Log.v(ACTION_AUTO_UPDATE, "onReceive:action.equals(ACTION_AUTO_UPDATE)"); int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
myUpdate(context, appWidgetIds); //context.startService(new Intent(context,UpdateService.class));
// Time estTime = new Time("EST");
// estTime.setToNow();
// RemoteViews updateViews =
// new RemoteViews(context.getPackageName(),
// R.layout.digitalclock);
// updateViews.setTextViewText(R.id.time, estTime.format("%H:%M"));
//
// //ComponentName thisWidget = new ComponentName(this,AppWidget.class);
//
// AppWidgetManager manager = AppWidgetManager.getInstance(context);
// int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
// if (appWidgetId != -1) manager.updateAppWidget(appWidgetId, updateViews);
//UpdateService.updateView();
} else if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
//if (!URI_SCHEME.equals(intent.getScheme())) {
// start alarm
//Log.v(ACTION_AUTO_UPDATE, "onReceive: ACTION_APPWIDGET_UPDATE");
// AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action) means it wasn't from the
// alarm
// either it's the first time in (even before the configuration
// is done) or after a reboot or update final int[] appWidgetIds = intent.getExtras().getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
for (int appWidgetId : appWidgetIds) { //Log.i(ACTION_AUTO_UPDATE, "Starting recurring alarm for id " + appWidgetId);
Intent widgetUpdate = new Intent();
// it takes me quite some time to figure out the action must be system defined
//like AppWidgetManager.ACTION_APPWIDGET_UPDATE, or it won't work.
//widgetUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.setAction(ACTION_AUTO_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId }); // make this pending intent unique by adding a scheme to
// it
//if we want to use this trick, URI_SCHEME must be defined in AndroidManifest.xml
// <receiver
// android:name="AngoWidgetProvider">
// <intent-filter>
// <action
// android:name="android.appwidget.action.APPWIDGET_UPDATE" />
// </intent-filter>
// <meta-data
// android:name="android.appwidget.provider"
// android:resource="@xml/imageswidget_info" />
// </receiver>
// <receiver
// android:name="ImagesWidgetProvider">
// <intent-filter>
// <action
// android:name="android.appwidget.action.APPWIDGET_UPDATE" />
// <data android:scheme="images_widget" />
// </intent-filter>
// <meta-data
// android:name="android.appwidget.provider"
// android:resource="@xml/imageswidget_info" />
// </receiver> //widgetUpdate.setData(Uri.withAppendedPath(Uri.parse(AppWidget.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);
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), INTERVAL_MILLIS, newPending);
}
//AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
//appWidgetAlarm.startAlarm();
// } else {
// Log.i(ACTION_AUTO_UPDATE, "URI_SCHEME.equals(intent.getScheme()" );
// }
}
super.onReceive(context, intent); } @Override
public void onEnabled(Context context)
{
// start alarm
//Log.v(ACTION_AUTO_UPDATE, "onEnabled");
//AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
//appWidgetAlarm.startAlarm();
} @Override
public void onDisabled(Context context)
{
//Log.v(ACTION_AUTO_UPDATE, "onDisabled");
// TODO: alarm should be stopped only if all widgets has been disabled // stop alarm
//AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
//appWidgetAlarm.stopAlarm();
} @Override
public void onDeleted(Context context, int[] appWidgetIds) {
//Log.v(ACTION_AUTO_UPDATE, "onDeleted");
// TODO: alarm should be stopped only if all widgets has been disabled // stop alarm
//AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
//appWidgetAlarm.stopAlarm(); for (int appWidgetId : appWidgetIds) { // stop alarm
Intent widgetUpdate = new Intent();
//widgetUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.setAction(ACTION_AUTO_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
//widgetUpdate.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId)));
PendingIntent newPending = PendingIntent.getBroadcast(context, 0, widgetUpdate, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarms.cancel(newPending); } super.onDeleted(context, appWidgetIds);
} public class AppWidgetAlarm
{
private final int ALARM_ID = 0;
private final int INTERVAL_MILLIS = 30000; private Context mContext; public AppWidgetAlarm(Context context)
{
mContext = context;
} public void startAlarm()
{
//Log.v(ACTION_AUTO_UPDATE, "startAlarm");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MILLISECOND, INTERVAL_MILLIS); Intent alarmIntent = new Intent(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
// RTC does not wake the device up
alarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), INTERVAL_MILLIS, pendingIntent);
} public void stopAlarm()
{
//Log.v(ACTION_AUTO_UPDATE, "stopAlarm");
Intent alarmIntent = new Intent(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
} public static class UpdateService extends Service {
@Override
public void onStart(Intent intent,int startId){
//Log.v(ACTION_AUTO_UPDATE, "UpdateService:onStart");
updateView();
} public static void updateView(){ } @Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
} } }