Przygotujemy aplikacj�-widget, kt�rego jedyn� funkcj� b�dzie pokazywanie zegara. 1. Z menu File, New, Other... tworzymy projekt typu Android Application Project (jak zwykle). a. API 4 (Android 1.6) b. Nazwa: ZegarekWidget c. Przestrze� nazw: pl.umk.fizyka.zegarekwidget d. W kolejnym kroku kreatora odznaczamy "Create Activity" - aplikacja nie b�dzie mia�a aktywno�ci. 2. Po uwtorzeniu projektu, musimy do niego doda� klas�: menu File, New, Class a. Pakiet: pl.umk.fizyka.zegarekwidget b. Nazwa klasy: Main c. Dziedziczy po (pole Superclass): android.appwidget.AppWidgetProvider e. Klikamy Finish. Uzyskamy: package pl.umk.fizyka.zegarekwidget; import android.appwidget.AppWidgetProvider; public class Main extends AppWidgetProvider { } 3. Klas� ZegarekWidget uzupe�niamy o metod� onUpdate, z package pl.umk.fizyka.zegarekwidget; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.widget.RemoteViews; //wygodniejszy od BroadcastReceiver w przypadku widgetow public class Odbiorca extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { ComponentName tenWidget = new ComponentName(context, Odbiorca.class); int[] wszystkieWidgety = appWidgetManager.getAppWidgetIds(tenWidget); for (int widget : wszystkieWidgety) { DateFormat format = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()); //import java.text.DateFormat; RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.main); rv.setTextViewText(R.id.textView1, format.format(new Date())); appWidgetManager.updateAppWidget(widget, rv); } } } Poza onUpdate s� metody onEnabled, onDisable i onDeleted. 4. Tworzymy plik XML opisuj�cy widok = interface okna: a. Z menu File, New, Other... -> Android XML Layout File b. W kreatorze podajmy nazw� pliku main.xml c. zaznaczmy root element: LinearLayout d. Finish. e. W pliku XML ustalmy konktetn� wysoko�� i szeroko�� okna i wstawmy zegar analogowy oraz TextView, na kt�rym b�dziemy dodatkowo wy�wietla� czas: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/linearLayout1" android:layout_width="180dp" android:layout_height="220dp" android:orientation="vertical" android:background="@drawable/background" > <!-- android:background="#99AAAAAA" --> <AnalogClock android:id="@+id/analogClock1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|center_horizontal" android:scrollY="-10dp" /> <TextView android:id="@+id/textView1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="top|center_horizontal" android:layout_margin="10dp" android:gravity="center_horizontal|center_vertical" android:text="@string/zegar_init" android:textColor="@android:color/black" android:textSize="12pt" /> </LinearLayout> f. U�ywany �a�cuch definiujemy w res/values/strings.xml <resources> <string name="app_name">ZegarekWidget</string> <string name="zegar_init">--:--:--</string> </resources> Widget nie mo�e u�ywa� ka�dego widoku (mo�liwe to FrameLayout, LinearLayout i RelativeLayout). Dopuszczalne kontrolki to AnalogClock, Button, Chromometer, ImageButton, ImageView, ProgressBar i TextView. Od Androida 3.0 widok�w i kontrolek jest wi�cej. Obrazek background.png pobieramy z http://developer.android.com/guide/practices/ui_guidelines/widget_design.html lub bezpo�rednio z http://kasperholtze.com/wp-content/uploads/2009/11/background.png i zapisujemy do res/drawable-hdpi. 5. Definiujemy plik XML opisuj�cego dostawc�: a. W katalogu res tworzymy podkatalog xml, b. w nim tworzymy plik dostawca.xml: <?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/main" android:minWidth="100dp" android:minHeight="200dp" android:updatePeriodMillis="1000" /> Aktualizacja ma nast�powa� co sekund� (1000 ms). Co taki czas ma by� wywo�ywana metoda onUpdate. To o wiele za cz�sto. szybko padnie bateria, bo telefon nie bedzie zasypial, ale my od razu bedziemy widzieli efekt dzialania widgetu (w teorii). Tak naprawd� system nie gwarantuje od�wie�ania widgetu co taki czas. Mo�e to by� znacznie rzadziej!!! W szczeg�lno�ci minimalny okres od�wie�ania to 30 min = 1800000 ms. Inna metoda aktualizacji widgetu korzysta z AlarmManager (zob. poni�ej i http://www.vogella.com/articles/AndroidWidgets/article.html). 6. Modyfikujemy plik manifestu - rejestrujemy widget: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pl.umk.fizyka.zegarekwidget" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" /> <application android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@style/AppTheme"> <receiver android:name="Odbiorca" android:label="Zegarek"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/dostawca" /> </receiver> </application> </manifest> Ten wpis oznacza, �e rejestrujemy klas� Odbiorca jako odbiorce komunikat APPWIDGET_UPDATE i ustala metadane dla widgetu. 7. Uruchom widget w normalny spos�b. Aplikacja si� nie uruchomi (nie ma aktywno�ci), ale zostanie umieszczona w emulatorze lub urz�dzeniu. W�wczas mo�emy j� umie�cic na ekranie. B�dzie dost�pna w�r�d Widget�w pod nazw� Zegarek (label z manifestu). W zale�no�ci od urz�dzenia aplikacja mo�e si� nie od�wie�a� (dotyczy to tekstu, bo zegarek ma chyba w�asny w�tek). Na emulatorze onUpdate w og�le nie jest uruchamiana. Komentarz z http://kasperholtze.com/android/how-to-make-a-simple-android-widget/: Please note that this is a very bad practice to set updatePeriodMillis to a high frequency (or low value) like this. At every updatePeriodMillis the phone will be waken up to deliver the call to the receiver�s onUpdate which will effectively prevent your phone from sleeping at all. What you shall do is to implement the other widget events as well and properly catch the moment when your first widget is created (and the last is removed) and deliver your updates using a non-wakeup type AlarmManager periodic callback or an AsyncTask, from a separate service. Beside this there is an other minor problem, as updatePeriodMillis updates will not be synced to a whole second boundary, resulting in seconds displayed being potentially skewed by 0-999 milliseconds. ---------- Klikni�cie widgetu - od�wie�enie. 8. Nale�y uzupe�ni� metod� onUpdate: public class Odbiorca extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { for (int widgetId : appWidgetIds) { DateFormat format = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()); //import java.text.DateFormat; RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.main); rv.setTextViewText(R.id.textView1, format.format(new Date())); //klikniecie (czy to najlepsze miejsce?) Intent intent = new Intent(context, Odbiorca.class); intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setOnClickPendingIntent(R.id.linearLayout1, pendingIntent); // appWidgetManager.updateAppWidget(widgetId, rv); } } } ------------------ Od�wie�anie widgetu za pomoc� us�ugi (AlarmManager) 9. Do katalogu pl.umk.fizyka.zegarekwidget dodaj klase: - nazwa: AktualizujWidgetService - klasa bazowa: android.app.Service - napisz abstrakcyjne metody (jest tylko onBind) 10. W tej klasie definiujemy metod� onStart: package pl.umk.fizyka.zegarekwidget; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; import android.app.PendingIntent; import android.app.Service; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.widget.RemoteViews; public class AktualizujWidgetService extends Service { Timer timer; @Override public void onStart(final Intent intent, int startId) { final Context context = this.getApplicationContext(); timer=new Timer(); timer.scheduleAtFixedRate( new TimerTask() { @Override public void run() { //znaczna czesc tego kodu powinna byc wylaczona //i uwspolniona z metoda Odbiorca.onUpdate!!! AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); //ComponentName tenWidget = new ComponentName(getApplicationContext(),Odbiorca.class); //int[] allWidgetIds2 = appWidgetManager.getAppWidgetIds(tenWidget); for (int widgetId : allWidgetIds) { DateFormat format = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()); //import java.text.DateFormat; RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.main); rv.setTextViewText(R.id.textView1, format.format(new Date())); //klikniecie Intent clickIntent = new Intent(context,Odbiorca.class); clickIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,allWidgetIds); PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, clickIntent,PendingIntent.FLAG_UPDATE_CURRENT); rv.setOnClickPendingIntent(R.id.linearLayout1, pendingIntent); appWidgetManager.updateAppWidget(widgetId, rv); } }; }, 0, 1000); //co 1000 ms stopSelf(); super.onStart(intent, startId); } @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } } 11. Deklarujemy us�ug� w pliku manifestu: <application android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@style/AppTheme"> <service android:name="AktualizujWidgetService" /> ... 12. I wreszcie zmieniamy metod� onUpdate widgetu (plik Odbiorca.java) tak, �eby ogranicza�a si� do uruchamiania us�ugi: public class Odbiorca extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { ComponentName thisWidget = new ComponentName(context,Odbiorca.class); int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget); Intent intent = new Intent(context,AktualizujWidgetService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds); context.startService(intent); } } Komentarz: The AlarmManager allows you to be more resource efficient and to have a higher frequency of updates. To use this approach you define a service and schedule this service via the AlarmManager regularly. This service updates the widget.