plusone

18. Mai 2012

Zeitliche- und Interaktive-Updates des Widgets

Hallo und herzlich willkommen zu meiner zweiter Widget-Nacht,

das Anzeigen von Views mit Inhalt war nur der Anfang.
Jetzt kommen wir zum Aktualisieren von Daten.

Für dieses Tutorial werden Kenntnisse für das Erstellen eines Widget und Starten eines Service vorausgesetzt und basiert auf das Tutorial erstellen von Widgets.

Als ersten erstellen wir ein Projekt. Dann eine Klasse, die von AppWidgetProvider erbt. In der Methode onUpdate starten wir einen Service, der für das Aktualisieren der Daten zuständig ist.

package de.alexroid.widgetsample;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;

public class BasisAppWidgetProvider extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
       
        // Build the intent to call the service
        Intent intent = new Intent(context.getApplicationContext(),
                UpdateWidgetService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
       
        // Update the widgets via the service
        context.startService(intent);
    }
}
 Als Extra übergeben wir gleich alle Widget IDs. Diese werden wir später brauchen. Und nicht vergessen den Service in der Manifest hinzuzufügen.
<application ..>
    <service android:name="de.alexroid.widgetsample.UpdateWidgetService" />
</application>

Kommen wir zum Layout.

widget_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:background="@drawable/widget_background"
    android:orientation="horizontal"
    android:padding="@dimen/widget_margin"
    android:weightSum="3" >

    <ImageButton
        android:id="@+id/button"
        style="@android:style/Widget.Button"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_margin="
8dp"
        android:layout_weight="1"
        android:padding="
8dp"
        android:src="@drawable/navigation_refresh" />

    <TextView
        android:id="@+id/textViewU"
        style="@android:style/Widget.TextView"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_margin="
8dp"
        android:layout_weight="1"
        android:background="@drawable/widget_item_background"
        android:gravity="center_horizontal|center_vertical"
        android:padding="
8dp"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/textViewC"
        style="@android:style/Widget.TextView"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_margin="8dp"
        android:layout_weight="1"
        android:background="@drawable/widget_item_background"
        android:gravity="center_horizontal|center_vertical"
        android:padding="
8dp"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

widget_background
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <stroke
        android:width="5dp"
        android:color="#FF000000" />

    <gradient
        android:angle="135"
        android:endColor="#EE666666"
        android:startColor="#EE333333" />

    <corners android:radius="5dp" />

</shape>

widget_item_background
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <gradient
        android:angle="45"
        android:endColor="#EE33B5E5"
        android:startColor="#EE0099CC" />

</shape>
Damit haben wir einen Button zum Inteagieren und zwei Textfelder für die Anzeige. Das Icon navigation_refresh ist aus dem Design-Pack.
Der Service, der im Hintergrund für uns alles aktualisiert, sieht wie folgt aus.
package de.alexroid.widgetsample;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;

public class UpdateWidgetService extends Service {

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        int[] allWidgetIds = intent
                .getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
        //für das Klicken, s.u.      
        boolean clicked = intent.getBooleanExtra(
                AppWidgetManager.EXTRA_CUSTOM_EXTRAS, false);

        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this
                .getApplicationContext());

        SharedPreferences preferences = getSharedPreferences(getClass()
                .getSimpleName(), 0);

        for (int widgetId : allWidgetIds) {
            RemoteViews remoteViews = new RemoteViews(this
                    .getApplicationContext().getPackageName(),
                    R.layout.widget_frame);

            //Hier beziehen wir beide Zahlen, die wir darstellen
            //numberC für das Klickevent und number für das zeitliche Update
            int numberC = preferences.getInt("numberC", -1);
            if (clicked) {

                Editor edit = preferences.edit();
                numberC += 1;
                edit.putInt("number2", numberC );
                edit.commit();

            } else {
                int number = preferences.getInt("number", -1) + 1;

                Editor edit = preferences.edit();
                edit.putInt("number", number);
                edit.commit();

                // Text setzen
                remoteViews.setTextViewText(R.id.textViewU,
                        String.valueOf(number));
            }

            // Text setzen
            remoteViews
                    .setTextViewText(R.id.textViewC, String.valueOf(numberC));

            //Service wegen dem Klick starten
            Intent clickService = new Intent(this.getApplicationContext(),
                    UpdateWidgetService.class);

            clickService.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
            clickService.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,
                    allWidgetIds);
            clickService.putExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, true);

            PendingIntent pendingIntent = PendingIntent.getService(
                    getApplicationContext(), 0, clickService,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent);

            //Widget tatsächlich updaten
            appWidgetManager.updateAppWidget(widgetId, remoteViews);

            stopSelf();
            super.onStart(intent, startId);
        }
    }
}

Die Variable allWidgetIds hilft uns das Update auf mehreren Widgets durchzuführen.


Beim jedem Druchlauf des Updatemechanismus wird pro Widget die Zahl um eins hochgezählt und als Text gesetzt. Die Zahl in der Mitte wird im Invervall Aktualisiert und die Rechte durch das Klicken auf den Button.
Es wird derzeit nicht garantiert, dass der Service in dem Interval anspringt, wie ihr angegeben habt. Aber einmal die Stunde sollte den Akku nicht zu strapazieren. HIER ist der originale Text - unter updatePeriodMillis.

So der nächste Schritt ist vollbracht. Das Aktualisieren klappt nun auch.

Ich verabschiede mich für diese Nacht.
Alexander Fink

Keine Kommentare:

Kommentar veröffentlichen