2010-09-22

Rolle rückwärts – CountDownTimer kann’s

Manchmal sind es die kleinen Dinge, die dem Programmierer das Leben merklich erleichtern. Deshalb möchte ich in loser Folge nützliche Helfer vorstellen, die leider allzu oft in der riesigen Android-Klassenbibliothek untergehen. CountDownTimer beispielsweise ruft regelmäßig eine bestimmte Methode auf, bis eine vorher festgelegte Zeit verstrichen ist. Hier das Quelltextfragment aus der Android-Doku:

    new CountDownTimer(30000, 1000) {

         public void onTick(long millisUntilFinished) {
             view.setText("seconds remaining: " + millisUntilFinished / 1000);
         }

         public void onFinish() {
             view.setText("done!");
         }
      }.start();

Der Timer des Beispiels läuft also insgesamt 30 Sekunden, wobei einmal pro Sekunde die Methode onTick aufgerufen wird. Laut Doku ist garantiert, dass ein nachfolgender Aufruf erst zum Zuge kommt, wenn der vorherige abgearbeitet wurde (durch ein synchronized auf das Timer-Objekt). Sie können das ausprobieren, indem Sie ein Thread.sleep(2000); einfügen. Nach Ablauf der 30 Sekunden wird onFinish aufgerufen – es sei denn, es wurde vorher cancel aufgerufen. Nicht ausdrücklich dokumentiert ist übrigens, dass Sie jederzeit erneut start aufrufen können. Auch das können Sie ausprobieren, indem Sie in onTick folgendes einfügen:

      if (millisUntilFinished / 1000 == 3)
        cdt.start();

Kennen Sie nützliche Klassen, die selten in Beispielen auftauchen? Schreiben Sie mir…

2010-09-13

Präferenzen mit Häkchen – DialogPreference leichtgemacht

Unter Android ist es extrem einfach, Anwendungseinstellungen zu verwalten. Für die Benutzeroberfläche stehen eine Reihe von Komponenten zur Verfügung, beispielsweise CheckBoxPreference (Häkchen ist gesetzt/nicht gesetzt) und ListPreference (aus einer Liste wird genau ein Element ausgewählt). Eine Kombination aus beiden (also eine Liste mit Checkboxen) ist im Standard nicht vorhanden, kann aber mit sehr wenigen Zeilen Code realisiert werden:

/**
 * PickCountriesPreference.java
 *
 * TKWeek (c) Thomas Künneth 2010
 * Alle Rechte beim Autoren. All rights reserved.
 */
package com.thomaskuenneth.android.util;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ScrollView;

public class PickCountriesPreference extends DialogPreference {

  private static final String TAG = PickCountriesPreference.class
      .getSimpleName();

  private SharedPreferences prefs;
  private Hashtable<String, CheckBox> ht;

  public PickCountriesPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
    ht = new Hashtable<String, CheckBox>();
  }

  public static boolean isSelected(Context context, Locale l) {
    SharedPreferences prefs = context.getSharedPreferences(TAG,
        Context.MODE_PRIVATE);
    return prefs.getBoolean(l.getCountry(), true);
  }

  @Override
  protected View onCreateDialogView() {
    Context context = getContext();
    prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
    ScrollView v = new ScrollView(context);
    LinearLayout layout = new LinearLayout(context);
    layout.setOrientation(LinearLayout.VERTICAL);
    v.addView(layout);
    layout.addView(getCheckbox(context, Locale.GERMANY));
    layout.addView(getCheckbox(context, Locale.US));
    return v;
  }

  private View getCheckbox(Context context, Locale l) {
    CheckBox cb = new CheckBox(context);
    String country = l.getCountry();
    ht.put(country, cb);
    cb.setText(l.getDisplayCountry());
    cb.setChecked(prefs.getBoolean(country, true));
    return cb;
  }

  @Override
  protected void onDialogClosed(boolean positiveResult) {
    if (positiveResult) {
      Editor editor = prefs.edit();
      Enumeration<String> e = ht.keys();
      while (e.hasMoreElements()) {
        String country = e.nextElement();
        editor.putBoolean(country, ht.get(country).isChecked());
      }
      editor.commit();
    }
  }
}
Java2html

Android stellt hierfür die Klasse DialogPreference zur Verfügung, die abstrakt ist, also nicht direkt verwendet werden kann. Deshalb leitet meine Klasse PickCountriesPreference von ihr ab und implementiert die Methode onCreateDialogView. Sie liefert eine View, die den Inhaltsbereich des anzuzeigenden Dialogs repräsentiert. In meinem Fall ist das eine ScrollView, die wiederum ein LinearLayout enthält. Hierin sind CheckBoxes enthalten. Die einzige Aufgabe, die noch zu tun ist: das Setzen der Werte in den SharedPreferences. Wie das gemacht wird, finden Sie in der Methode onDialogClosed.

Der hier gezeigte Quelltext ist – Sie sehen das sicher – an die Bedürfnisse von TKWeek angepasst. Er lässt sich aber leicht verallgemeinern.

2010-09-10

TKWeek 1.2.2

Screenshot von TKWeek 1.2.2
Screenshot von TKWeek 1.2.2
Ich habe eine neue Version von TKWeek im Android Market abgelegt, die in den Einstellungen einen Abschnitt für Nationalfeiertage enthält. Aktuell kennt meine App zwar nur recht wenige solcher Feiertage, aber ich möchte das im Laufe der Zeit ausbauen (…ähm, erweitern…). Neu an Bord ist der amerikanische Unabhängigkeitstag (Independence Day, 4. Juli). Ferner wird der Tag der deutschen Einheit jetzt auch auf diese Weise behandelt.
Haben Sie Ideen oder Wünsche für meine App? Schreiben Sie mir…