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 the
updatePeriodMillis
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:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
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"+" "+"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; } } }