2013-03-31

Quick Tipp: Quelltexte formatieren

Für den vorangehenden Post war ich auf der Suche nach einem Tool, das XML für die Anzeige in Weblog-Posts aufbereitet. Dabei bin ich auf den Source Code Formatter gestoßen. Diese Online-Konvertierung funktioniert, wie Sie vielleicht gesehen haben, sehr gut. Vielen Dank an den Entwickler.

2013-03-29

Ein SeekBar-Beispiel

Mit SeekBars kann man in Android bequem Werte einstellen. Der folgende Screenshot zeigt die Regulierung der Display-Helligkeit.
Screenshot: Display-Helligkeit einstellen
Display-Helligkeit einstellen
Das Hin und Her schieben des kreisrunden Knopfes ist fingerfreundlich. Allerdings fehlt meiner Meinung nach eine Rückmeldung, wie stark sich der Wert schon geändert hat. Natürlich lässt sich so etwas leicht nachrüsten. Die folgenden Quelltextfragmente sind einer zukünftigen Version von TKWeek entnommen. Dort setze ich die SeekBar unter anderem ein, um die Deckkraft meiner Widget-Hintergründe zu steuern.
Screenshot: SeekBar in TKWeek
SeekBar in TKWeek
Lassen Sie uns zunächst einen Blick auf die Layoutdatei werfen. Sie besteht aus zwei geschachtelten LinearLayouts. Das äußere ordnet zwei Zeilen vertikal an, nämlich die Beschriftung Opacity sowie die SeekBar mit der Angabe 0 / 255. Das innere LinearLayout positioniert diese beiden Elemente nebeneinander, also in horizontaler Richtung.
 <?xml version="1.0" encoding="utf-8"?>  
 <!--  
   widget_preference.xml  
   TKWeek (c) Thomas Künneth 2013  
   Alle Rechte beim Autoren. All rights reserved.  
 -->  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="match_parent"  
   android:layout_height="wrap_content"  
   android:orientation="vertical"  
   android:padding="6dp"  
 >  
   <TextView   
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content"  
     android:text="@string/widget_opacity"  
   />  
   <LinearLayout  
     android:layout_width="match_parent"  
     android:layout_height="wrap_content"  
     android:orientation="horizontal"  
   >  
     <SeekBar  
       android:id="@+id/widget_opacity"  
       android:layout_width="0dp"  
       android:layout_height="wrap_content"  
       android:layout_weight="1"  
     />  
     <TextView   
       android:id="@+id/widget_opacity_info"  
       android:layout_width="wrap_content"  
       android:layout_height="match_parent"  
       android:gravity="center_vertical|right"  
       android:ems="5"  
     />  
   </LinearLayout>  
 </LinearLayout>  
Geschachtelte LinearLayouts werden gelegentlich kritisiert, weil sie etwas mehr Speicherplatz als optimierte RelativeLayouts verbrauchen. Allerdings sind sie leicht les- und wartbar. Sobald der Beispiel-Dialog weitere Elemente (denken Sie an Picker zum Auswählen von Vorder- und Hintergrundfarbe) enthält, wird es unübersichtlich. Ich würde deshalb davon abraten, mehrzeilige Label-Element-Konstruktionen mit einem einzigen RelativeLayout zu realisieren.
Ist Ihnen aufgefallen, dass die TextView rechts neben der SeekBar die merkwürdige Zuweisung android:ems="5" enthält? Damit sorgen wir für eine feste, Zeichensatz-abhängige Breite und verhindern in Verbindung mit der rechtsbündigen Ausrichtung des Texts ein “Zappeln” der SeekBar-Breite, wenn sich die Textlänge ändert. 1 / 255 braucht weniger Platz als beispielsweise 255 / 255.
Die x / y-Ausgabe wird in strings.xml definiert:
 <!-- aus strings.xml -->  
 <string name="int_slash_int">%1$d / %2$d</string>  
Das folgende Quelltextfragment zeigt, wie die Klasse SeekBar verwendet wird. Es entfaltet die bereits vorgestellte Layoutdatei und bettet sie in einen Dialog ein.
-->
/*
 * WidgetPreference.java
 *
 * TKWeek (c) Thomas Künneth 2013
 * Alle Rechte beim Autoren. All rights reserved.
 */
package com.thomaskuenneth.android.util;

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.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

import com.thomaskuenneth.tkweek.DateWidget;
import com.thomaskuenneth.tkweek.DayOfYearWidget;
import com.thomaskuenneth.tkweek.R;
import com.thomaskuenneth.tkweek.TKWeek;

/**
 * Stellt einen Dialog dar, in dem die Deckkraft des Widget-Hintergrunds
 * eingestellt werden kann. Der Wert wird in den SharedPreferences abgelegt.
 *
 * @author Thomas Künneth
 */
public class WidgetPreference extends DialogPreference implements
    OnSeekBarChangeListener {

  private static final String TAG = WidgetPreference.class.getSimpleName();
  private static final String OPACITY = "opacity";

  private TextView seekbarInfo;
  private SeekBar seekbar;

  public WidgetPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
    setDialogLayoutResource(R.layout.widget_preference);
  }

  public static int getOpacity(Context c) {
    SharedPreferences prefs = c.getSharedPreferences(TAG,
        Context.MODE_PRIVATE);
    return prefs.getInt(OPACITY, 128);
  }

  @Override
  protected void onBindDialogView(View view) {
    super.onBindDialogView(view);
    seekbarInfo = (TextView) view.findViewById(R.id.widget_opacity_info);
    seekbar = (SeekBar) view.findViewById(R.id.widget_opacity);
    seekbar.setOnSeekBarChangeListener(this);
    seekbar.setMax(255);
    seekbar.setProgress(getOpacity(getContext()));
  }

  @Override
  protected void onDialogClosed(boolean positiveResult) {
    if (positiveResult) {
      SharedPreferences prefs = getContext().getSharedPreferences(TAG,
          Context.MODE_PRIVATE);
      Editor e = prefs.edit();
      e.putInt(OPACITY, seekbar.getProgress());
      e.commit();
      TKWeek.updateWidgets(getContext(), DateWidget.class);
      TKWeek.updateWidgets(getContext(), DayOfYearWidget.class);
    }
  }

  // OnSeekBarChangeListener

  @Override
  public void onProgressChanged(SeekBar seekBar, int progress,
      boolean fromUser) {
    seekbarInfo.setText(getContext().getString(R.string.int_slash_int,
        progress, seekBar.getMax()));
  }

  @Override
  public void onStartTrackingTouch(SeekBar seekBar) {
  }

  @Override
  public void onStopTrackingTouch(SeekBar seekBar) {
  }
}
Java2html
Der Wertebereich einer SeekBar beginnt bei 0. setMax() legt den größten zulässigen Wert fest. setOnSeekBarChangeListener() registriert einen Listener, der bei Bedarf über Änderungen informiert. Die beiden Methoden setProgress() und getProgress() setzen bzw. liefern den aktuellen Wert.

2013-03-23

TKWeek 1.6.1 ist da

Screenshot von TKWeek 1.6.1
Screenshot von TKWeek 1.6.1
TKWeek 1.6.1 enthält eine Reihe spannender Erweiterungen. So zeigt die Tagesübersicht nun alle nicht erledigten Aufgaben aus Google Tasks an. Die Aufgaben werden nach Fälligkeit sortiert; wurde einer Aufgabe keine Fälligkeit zugewiesen, gilt sie als heute fällig. In Google Tasks können Aufgaben einer bestimmten Liste zugewiesen werden. Der Name der Liste erscheint unterhalb der Fälligkeit (sofern diese angegeben wurde) am rechten Rand. Wurde einer Aufgabe eine Notiz hinzugefügt, wird diese ebenfalls angezeigt (dies ist auf dem Screenshot aber nicht zu sehen).

Noch fehlen ein paar Dinge, die ich in den kommenden Versionen einbauen möchte. Ganz wichtig natürlich das als erledigt markieren sowie das Anlegen neuer sowie das Bearbeiten bestehender Aufgaben.

Wie gefallen Ihnen denn die neuen Funktionen? Würden Sie sich etwas spezielles wünschen? Schreiben Sie mir...

2013-03-05

Kaffeesatz lesen Ausgabe 03/2013

Das Jahr ist noch jung, doch wir haben schon zwei medienwirksame Ereignisse hinter uns gebracht.Da war die Consumer Electronics Show in Las Vegas, und natürlich der Mobile World Congress in Barcelona.Beide Ereignisse waren voll von Android, Windows (Phone) 8 und Blackberry 10.
Natürlich wird all das schnell vergessen sein, wenn Cupertino zur nächsten Messe läutet. Die Erfahrung lehrt, dass es noch diesen Monat wieder soweit sein wird. Und dann noch einmal im Juni. Aber mal ehrlich – was können wir in diesem Jahr an wirklichen Neuigkeiten erwarten? Der Druck auf Apple ist hoch, aber die Umbauten im Kern sind noch zu frisch, als dass man wirklich mehr sehen dürfte als wieder Produktpflege. Sicher scheint mir, dass iOS eine Frischzellenkur erhalten wird, die vor allem auch an der Oberfläche spürbar sein wird. Jonathan Ive will ganz sicher seinen Stempel endlich auch der Software aufdrücken. Allerdings glaube ich nicht, dass es dieses Mal schon so weit sein wird. Letztlich werden wir überarbeitete Hardware sehen, das prognostizierte preiswerte iPhone wird irgendwie kommen; und ich wette, dass es bald noch einen anderen Formfaktor in der Familie geben wird – ob das dann iPhone XL oder iPad Nano heißt ist fast schon egal.
Und Google? Die I/O findet vom 15. bis 17. Mai statt. Natürlich werden wir Key Lime Pie sehen, natürlich wird es Änderungen am Look and Feel geben, und natürlich wird es wieder Lippenbekenntnisse geben, die neue Plattformversion schneller auf die Endgeräte zu bringen. Wenn es neue Hardware gibt, wird Google sie unter den Teilnehmern verteilen und wird die Mehrzahl der Supporter unbedacht lassen. Dass Google Deutschland irgendwann noch aufwacht und mal etwas Evangelismus hierzulande betreibt, sehen wir in diesem Leben wahrscheinlich nicht mehr.
Aber was ich mir wirklich, wirklich wünsche: endlich wieder Highend-Geräte mit ordentlichen Formfaktor. Ich kann diese XXL-Toasts nicht mehr sehen. Wie geht es Ihnen damit? Schreiben Sie mir…