Archiv zur Kategorie ‘Android’

10.000+ Downloads

Unsere Android App Stadt, Land, Anruf hat die Grenze von 10.000 Installationen erreicht. Vielen Dank für die zahlreichen Anregungen und Kommentare, die uns geholfen haben, die App ständig zu verbessern.

Woher kommt der Anruf? Stadt, Land, Anruf ist eine kostenlose Android App, welche die Stadt oder das Land des Anrufers anzeigt.

 

 

 

 

 

 

Apps für Deutschland – Wir machen mit!

Der Wettbewerb Apps für Deutschland ruft auf um Ideen, Anwendungen oder Daten zu schaffen, die den Open-Data Gedanken unterstützen.

Wir machen mit! Und stellen unsere Android App zur Anruferkennung Stadt, Land, Anruf vor.

Android App mit Anruferkennung

Stadt? Land? Woher kommt der Anruf?

Unsere neue kostenlose Android App erkennt Anrufe anhand von über 5.000 deutschen und 600 internationalen Vorwahlen. Bei eingehenden Anrufen wird die deutsche Stadt oder das Land anzeigt. Außerdem wird die Anrufliste um die Informationen erweitert.

Zur Abfrage der Vorwahl-Datenbank wird keine Internetverbindung benötigt.

Android Color Picker

Dieses Android Tutorial zeigt einen Color Picker, welchen wir für unsere Android App Stadt, Land, Anruf entwickelt haben. Der Source Code kann auch als Eclipse Projekt herunter geladen werden: Android Color Picker.

Im ersten Beispiel haben wir eine vordefinierte Liste mit Farben:

android-color-picker

android-color-picker

Zur Darstellung benutzen wir ein GridView mit sechs Spalten. In den Spalten und Zeilen ist jeweils ein ImageView definiert, dessen Hintergrundfarbe über imageView.setBackgroundColor(colorList.get(position)); gesetzt wird.

Das Layout des GridView ist in res/layout/color_picker.xml definiert.

In der Datei ColorPickerAdapter.java befinden sich die Farben, die zur besseren Lesbarkeit in einem Array abgelegt sind.

// for convenience and better reading, we place the colors in a two dimension array
String colors[][] = { { "822111", "AC2B16", "CC3A21", "E66550", "EFA093", "F6C5BE" },
		{ "A46A21", "CF8933", "EAA041", "FFBC6B", "FFD6A2", "FFE6C7" },
		{ "AA8831", "D5AE49", "F2C960", "FCDA83", "FCE8B3", "FEF1D1" },
		{ "076239", "0B804B", "149E60", "44B984", "89D3B2", "B9E4D0" },
		{ "1A764D", "2A9C68", "3DC789", "68DFA9", "A0EAC9", "C6F3DE" },
		{ "1C4587", "285BAC", "3C78D8", "6D9EEB", "A4C2F4", "C9DAF8" },
		{ "41236D", "653E9B", "8E63CE", "B694E8", "D0BCF1", "E4D7F5" },
		{ "83334C", "B65775", "E07798", "F7A7C0", "FBC8D9", "FCDEE8" },
		{ "000000", "434343", "666666", "999999", "CCCCCC", "EFEFEF" } };

colorList = new ArrayList();

// add the color array to the list
for (int i = 0; i < colors.length; i++) {
	for (int j = 0; j < colors[i].length; j++) {
		colorList.add(Color.parseColor("#" + colors[i][j]));
	}
}

Im nächsten Schritt wird der Adapter dem GridView zugewiesen:

GridView gridViewColors = (GridView) findViewById(R.id.gridViewColors);
gridViewColors.setAdapter(new ColorPickerAdapter(getContext()));

Das zweite Beispiel zeigt einen Rainbow-Color-Picker bei dem die Anzahl der Farben beliebig erweiterbar ist:

android-color-picker-rainbow

android-color-picker-rainbow

Über die folgende Schritte wird der Regenbogen definiert:

// FF 00 00 --> FF FF 00
for (red = 255, green = 0, blue = 0; green <= 255; green += step)
    colorList.add(Color.rgb(red, green, blue));

// FF FF 00 --> 00 FF 00
for (red = 255, green = 255, blue = 0; red >= 0; red -= step)
    colorList.add(Color.rgb(red, green, blue));

// 00 FF 00 --> 00 FF FF
for (red = 0, green = 255, blue = 0; blue <= 255; blue += step)
    colorList.add(Color.rgb(red, green, blue));

// 00 FF FF -- > 00 00 FF
for (red = 0, green = 255, blue = 255; green >= 0; green -= step)
    colorList.add(Color.rgb(red, green, blue));

// 00 00 FF --> FF 00 FF
for (red = 0, green = 0, blue = 255; red <= 255; red += step)
    colorList.add(Color.rgb(red, green, blue));

// FF 00 FF -- > FF 00 00
for (red = 255, green = 0, blue = 255; blue >= 0; blue -= 256 / step)
    colorList.add(Color.rgb(red, green, blue));

Tipps zum Layout

Im Augenblick ist unser Layout für hdpi Geräte optimiert. Auf einem ldpi (small screen) sieht unser Color Picker so aus:

android-color-picker-small-device

android-color-picker-small-device

Damit die App auf verschiedenen Geräte das gewünschte Layout hat müssen wir die Spaltenbreite des GridView anpassen. Hierfür legen wir die Datei res\values-small\strings.xml an und setzten mit colorGridColumnWidth die Breite und erhalten danach diese Ansicht:

android-color-picker-small-device-fixed

android-color-picker-small-device-fixed

Download Eclipse Project Android Color Picker

Global Time für Android – immer die genaue GPS Uhrzeit

GPS Zeit: GlobalTime für AndroidMit Global Time haben Sie die genaue Uhrzeit immer dabei.
Die Uhrzeit wird über das GPS-Zeitsignal der am aktuellen Standort verfügbaren Satelliten ermittelt. GPS-Satelliten besitzen eine Atomuhr und liefern immer die korrekte Uhrzeit. Anders als bei dem Zeitsignalsender DCF77 ist der Empfang nicht auf Mitteleuropa begrenzt; GPS ist weltweit verfügbar. Zur Bestimmung der Uhrzeit ist keine Internetverbindung erforderlich. Das macht Global Time zum idealen Reisebegleiter und spart hohe Internetkosten im Ausland.

ipcas bietet Ihnen nicht nur unterwegs immer die genaue Uhrzeit, sondern mit dem GPS TimeServer ipNTP auch in ihrem Netzwerk.

GPS Zeit-Server ipNTP - TimeServer
Mit ipNTP synchronisieren Sie Ihre Rechner, Systeme und Anlagen mit dem Zeitsignal des globalen Satelliten-Navigationssystems (GPS).

Die Android Anwendung Global Time steht ab sofort kostenlos im Android Market zum Download bereit.
GlobalTime, erhältlich im Android Marketipcas Global Time Android Market QR-Code

Global Time Screenshots
ipcas Global Time ScreenShot: GPS wird gesucht …ipcas Global Time ScreenShot: GPS Signal empfangenipcas Global Time ScreenShot: GPS ist deaktiviert

 

Threads in Android Teil 2 (Aktualisierung der UI)

Werden Threads in Android überhaupt benötigt? Ja, besonders bei Operationen mit langer Laufzeit wie z.B. beim Zugriff auf das Netzwerk. Solche Operationen können den Haupt-Thread, durch den die Benutzeroberfläche (UI) ausgeführt wird, blockieren. Als Folge dessen ist die UI nicht in der Lage Benutzereingaben entgegenzunehmen.
Das folgende Beispiel soll das Problem veranschaulichen:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    // simulate long running operation
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        Log.e(TAG, "Thread.sleep", e);
    }
}

Gekapselte Prozesse brauchen in Threads länger, besonders dann wenn der Prozess in den Lebenszyklus der Aktivität gestartet wird (onCreate, onResume, onPause).
Wenn eine Anwendung nicht reagiert, liefert das Android-System normalerweise die Meldung "Anwendung reagiert nicht. Sofort schließen / Warten" zurück (ANR Dialog – Application Not Responding).

Um dies zu umgehen kann ein Thread erstellt werden der am Ende eine Meldung ausgibt:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    thread = new Thread() {
        public void run() {
            try {
                // simulate long running operation
                Thread.sleep(5000);
                textView.setText("new value...");
            } catch (InterruptedException e) {
                Log.e(TAG, "run in thread", e);
            }
        }
    };
    thread.start();
}

War es das schon? Nein, leider nicht. Nachfolgend können Sie sehen wie sich die Anwendung mit folgender Meldung verabschiedet:
CalledFromWrongThreadException Only the original thread that created a view hierarchy can touch its views.

CalledFromWrongThreadException

Genau wie in Java oder C# ein UI-Objekt (in unserem Fall ein textView) kann dieses nur durch den Thread verändert werden, durch den es auch erzeugt wurde. In diesem Beispiel ist dies der Main-UI-Thread (nicht der neue Thread).

Schauen Sie sich dazu auch den Artikel Painless Threading auf der Android-Entwickler-Seite an. Ein weiteres Tutorial dort zeigt wie mittels des Handler die UI eines Threads aktualisiert werden kann.

Eine Handler-Instanz wird an den Main-UI-Thread gebunden und ist so in der Lage das UI-Objekt zu aktualisieren, wenn Benachrichtigungen von anderen Threads empfangen werden.

Als nächstes wird eine Unterklasse der Handler-Klasse erzeugt und die handleMessage Methode überschrieben. msg.obj enthält die Nachricht aus dem anderen Thread.

private Handler uiHandler = new UIHandler();

class UIHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        // a message is received; update UI text view
        textView.setText(msg.obj.toString());
        super.handleMessage(msg);
    }
}

Erstellen Sie in dem länger laufenden Thread das Message-Objekt und übergeben Sie es an den Handler. Verwenden Sie Message.obtain() um eine Nachricht aus dem Pool wieder verwendbarer Objekte zu übernehmen.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    thread = new Thread() {
        public void run() {
            try {
                // simulate long running operation
                Thread.sleep(5000);

                // create message which will be send to handler
                Message msg = Message.obtain(uiHandler);
                msg.obj = "new value...";
                uiHandler.sendMessage(msg);

            } catch (InterruptedException e) {
                Log.e(TAG, "run in thread", e);
            }
        }
    };
    thread.start();
}

Das uiHandler-Objekt erhält die Nachricht und gibt den Inhalt im UI-Objekt aus.

Falls Sie unterschiedliche Threads haben und kontrollieren möchten woher die Nachricht kommt, können Sie msg.what verwenden um die Nachricht zu identifizieren:

class UIHandler extends Handler {
    private static final int ID_0 = 0;
    private static final int ID_1 = 1;

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case ID_0:
            // a message is received; update UI text view
            if (msg.obj != null)
                textView.setText(msg.obj.toString());
            break;

        default:
            break;
        }
        super.handleMessage(msg);
    }
}

Ändern Sie den Thread um eine Nachricht zu senden (benutzerdefinierter Code):

// create message which will be send to handler
Message msg = Message.obtain(uiHandler, UIHandler.ID_0);
msg.obj = "new value...";
uiHandler.sendMessage(msg);

Subversion in Android Projekten (bei Verwendung von Eclipse)

Es ist eigentlich nicht erforderlich den bin und gen Ordner im Subversion-Repository zu halten. Weil der Inhalt dieser beiden Ordner generiert wird, kann man sie genauso gut zur Liste der nicht zu beachtenden Ordner (ignore lis) hinzufügen.
bin und gen Ordner

Aber was passiert wenn das Projekt mit Eclipse kompilieren und erstellt wird?

Eclipse kopiert die versteckten .svn Ordner aus dem Quellverzeichnis in das bin Verzeichnis.

Hier ist die Lösung: Gehen Sie in die Projekt-Eigenschaften (Project Properties) und wählen Sie Java Build Path aus. Fügen Sie nun das Ausschluss-Muster **/.svn/ zum Quellverzeichnis hinzu.

Java Build Path: exclusion **/.svn/

Beim Aufräumen (Cleaning) und das Erstellen des Projektes kann der .svn Ordner entfernt werden. Beachten Sie daher, dass die Datei .classpath jetzt den folgenden, neuen Eintrag beinhaltet: <classpathentry excluding=“**/.svn/“ kind=“src“ path=“src“/>

Threads in Android Teil 1 (Endlosschleife)

Diese Serie zeigt Tipps und Tricks über die Verwendung von Threads in der Android-Entwicklung. Teil 1 beschreibt, wie man einen Thread mit einer Endlosschleife erzeugt. Da der Thread nur gebraucht wird wenn die Anwendung läuft, muss zunächst sichergestellt werden, dass der Thread stoppt, sobald die Anwendung nicht mehr aktiv ist.

In diesem Beispiel haben wir einen Thread erstellen, der alle n Sekunden ausführen wird.
Schauen Sie sich den nachfolgenden Quelltext an. Die onCreate Methode der Activity wird überschrieben und in einem separaten Thread wird die Aufgabe abgearbeitet. Dieser Thread wird nach erledigter arbeit „Schlafen“ geschickt.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    thread = new Thread() {
        public void run() {
            while (true) {
                try {
                    // do something here
                    Log.d(TAG, "local Thread sleeping");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Log.e(TAG, "local Thread error", e);
                }
            }
        }
    };
    thread.start();
}

Was passiert, wenn die Anwendung pausiert, z.B. wenn eine andere Anwendung in den Vordergrund gebracht wird?
Der nachfolgende ScreenShot vom Debugger zeigt, dass der Thread weiterhin ausgeführt wird wenn die Anwendung nicht aktiv ist. Debugger: Der Thread läuft noch

Es ist also erforderlich den Thread zu kontrollieren und ihn in der onPause-Methode zu stoppen.

Die Lösung dazu stammt aus dem Google Artikel "Updating the UI from a Timer" (http://developer.android.com/resources/articles/timed-ui-updates.html). Wir instanziieren einen Handler der es uns erlaubt ablauffähige-Objekte (Runnable Objects) verarbeiten können.

Der Handler ist an den Main-UI-Thread der Activity gebunden.
Der neue Thread wird durch den Aufruf handler.postDelayed(this, 1000); zur "Message-Queue" des Main-UI-Thread hinzugefügt und nach 1 Sekunde ausgeführt.

private Thread thread;
private Handler handler = new Handler();

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	
	thread = new Thread() {
		public void run() {
			// do something here
			Log.d(TAG, "local Thread sleeping");
			handler.postDelayed(this, 1000);
		}
	};
}

Der Rest ist einfach. Wir beginnen den Thread in onResume, welches nach onCreate aufgerufen wird, sobald die Activity durch den Aufruf von handler.postDelayed(thread, 0); in den Vordergrund kommt (Parameter 0 zeigt, dass es keine Verzögerung gibt).

Bevor der Thread zum Handler hinzugefügt wird, muss er entfernt werden um sicherzustellen, dass er nicht schon an den Handler gebunden ist. Das kann mit onPause erledigt werden:

@Override
protected void onResume() {
	super.onResume();
	
	handler.removeCallbacks(thread);
	handler.postDelayed(thread, 0);
	Log.d(TAG, "onResume");
}

@Override
protected void onPause() {
	super.onPause();

	handler.removeCallbacks(thread);
	Log.d(TAG, "onPause");
}

Wenn man sich die Debugger anschaut, merkt man, dass der Thread gestoppt wird wenn die Activity pausiert. Threads in Android - onResume  onPause