1. Code
  2. Mobile Development
  3. Android Development

Android SDK: Erstellen eines rotierenden Dialers

Scroll to top

German (Deutsch) translation by Alex Grigorovich (you can also view the original English article)

Das Android SDK bietet eine Vielzahl von Schnittstellenkomponenten, darunter TextViews, Schaltflächen und EditText-Felder. Das Hinzufügen von benutzerdefinierten Oberflächenelementen zu Ihrer App ist jedoch eine hervorragende Möglichkeit, sich im App Market abzuheben. In diesem Tutorial wird die Erstellung von Komponenten für benutzerdefinierte Schnittstellen erläutert, indem Sie lernen, wie Sie einen schlanken Wählschalter bauen.

Dieses Tutorial setzt einige Grundkenntnisse in Java und Android voraus. Außerdem werde ich keine vollständige API einführen. Stattdessen werden bestimmte Klassen verwendet und erklärt. Ich werde auch Schritt für Schritt erklären, warum ich eine bestimmte Implementierung für diese Schnittstellenkomponente ausgewählt habe.


Schritt 1: Das Konzept

Wir haben einen Kreis und wollen ihn um seine Mitte drehen. Der einfachste Ansatz besteht darin, ein zweidimensionales kartesisches Koordinatensystem als Vorlage zu verwenden.

coordinate systemcoordinate systemcoordinate system

Sie berühren den Kreis, drehen ihn und lassen ihn dann los. Darüber hinaus sollte der Kreis in diesem Fall einen "fling" registrieren.

Android bietet eine sehr einfache Oberfläche zum Transformieren von Bildern mithilfe der Bitmap-Klasse und der Matrix-Klasse. Der Kreis wird in einer ImageView angezeigt. Die mathematische Basis des Einheitskreises kann mithilfe der Math-Klasse implementiert werden. Android bietet auch eine gute API zum Erkennen von Berührungsereignissen und Gesten. Wie Sie sehen, steht uns der größte Teil der Arbeit bereits mit dem SDK zur Verfügung!


Schritt 2: Projekteinrichtung

Erstellen Sie ein neues Android-Projekt und fügen Sie eine Aktivität hinzu. Fügen Sie dann die Dialer-Grafik zum Ordner "drawable" hinzu. Wir benötigen keine unterschiedlichen Versionen für unterschiedliche Anzeigedichten, da wir das Bild später programmgesteuert skalieren werden. Ein Bild mit hoher Auflösung, das alle Bildschirmgrößen abdeckt, sollte ausreichen und Speicherplatz auf dem Telefon des Benutzers sparen.

project setup

Schritt 3: Layout

In diesem Tutorial halten wir die Benutzeroberfläche sehr einfach. Wir fügen dem Layout nur eine ImageView hinzu. Für den Inhalt von ImageView wählen wir unsere Dialer-Grafik aus. Es ist kein Problem, später weitere Views hinzuzufügen, da dies keinen Einfluss auf unseren rotierenden Dialer hat.

1
2
<?xml version="1.0" encoding="utf-8"?>
3
<LinearLayout
4
	xmlns:android="https://schemas.android.com/apk/res/android"
5
	android:orientation="vertical"
6
	android:layout_width="fill_parent"
7
	android:layout_height="fill_parent"
8
	android:background="#FFCCCCCC">
9
	<ImageView
10
		android:src="@drawable/graphic_ring"
11
		android:id="@+id/imageView_ring"
12
		android:layout_height="fill_parent"
13
		android:layout_width="fill_parent"></ImageView>
14
</LinearLayout>

Schritt 4: Die Aktivität

Wie in den obigen Schritten beschrieben, müssen wir das Bild beim Erstellen der Aktivität als Bitmap laden. Darüber hinaus benötigen wir eine Matrix für die Transformationen, wie die Rotation. All dies tun wir in der onCreate-Methode.

Wir benötigen auch eine richtig skalierte Kopie des Bildes. Da wir erst nach dem Messen des Layouts wissen, wie viel Platz unsere ImageView ausfüllt, fügen wir einen OnGlobalLayoutListener hinzu. In der onGlobalLayout-Methode können wir das Ereignis abfangen, in dem das Layout gezeichnet wurde, und die Größe unserer Ansicht abfragen.

1
2
	private static Bitmap imageOriginal, imageScaled;
3
	private static Matrix matrix;
4
5
	private ImageView dialer;
6
	private int dialerHeight, dialerWidth;
7
	
8
	@Override
9
    public void onCreate(Bundle savedInstanceState) {
10
        super.onCreate(savedInstanceState);
11
        setContentView(R.layout.main);
12
        
13
        // load the image only once

14
        if (imageOriginal == null) {
15
        	imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.graphic_ring);
16
        }
17
        
18
        // initialize the matrix only once

19
        if (matrix == null) {
20
        	matrix = new Matrix();
21
        } else {
22
        	// not needed, you can also post the matrix immediately to restore the old state

23
        	matrix.reset();
24
        }
25
26
        
27
        dialer = (ImageView) findViewById(R.id.imageView_ring);
28
        dialer.setOnTouchListener(new MyOnTouchListener());
29
        dialer.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
30
31
        	@Override
32
			public void onGlobalLayout() {
33
        		// method called more than once, but the values only need to be initialized one time

34
        		if (dialerHeight == 0 || dialerWidth == 0) {
35
        			dialerHeight = dialer.getHeight();
36
        			dialerWidth = dialer.getWidth();
37
        			
38
        			// resize

39
					Matrix resize = new Matrix();
40
					resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight());
41
					imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);
42
        		}
43
			}
44
		});
45
        
46
    }

Der OnTouchListener ist sehr einfach gehalten. Im Ereignis ACTION_DOWN initialisieren wir den Winkel (d. H. Den Einheitskreis). Mit jeder Bewegung wird die Differenz zwischen dem alten und dem neuen Winkel additiv zum Dialer erhöht.

Wir fügen den OnTouchListener als private innere Klasse hinzu. Dies erspart uns die unnötige Übergabe der erforderlichen Parameter (z.B. die Größe der Ansicht).

1
2
	/**

3
	 * Simple implementation of an {@link OnTouchListener} for registering the dialer's touch events. 

4
	 */
5
	private class MyOnTouchListener implements OnTouchListener {
6
		
7
		private double startAngle;
8
9
		@Override
10
		public boolean onTouch(View v, MotionEvent event) {
11
12
			switch (event.getAction()) {
13
				
14
				case MotionEvent.ACTION_DOWN:
15
					startAngle = getAngle(event.getX(), event.getY());
16
					break;
17
					
18
				case MotionEvent.ACTION_MOVE:
19
					double currentAngle = getAngle(event.getX(), event.getY());
20
					rotateDialer((float) (startAngle - currentAngle));
21
					startAngle = currentAngle;
22
					break;
23
					
24
				case MotionEvent.ACTION_UP:
25
					
26
					break;
27
			}		
28
			
29
			return true;
30
		}
31
		
32
	}

Die Berechnung des Winkels kann mit Hilfe des Einheitskreises schnell gelöst werden. Die Koordinate des Berührungsereignisses muss jedoch zuerst in die Koordinaten des zweidimensionalen kartesischen Koordinatensystems konvertiert werden. Es würde Ihnen wahrscheinlich helfen, diese Formeln noch einmal selbst einzurichten, um sie vollständig zu verstehen.

1
2
	/**

3
	 * @return The angle of the unit circle with the image view's center

4
	 */
5
	private double getAngle(double xTouch, double yTouch) {
6
		double x = xTouch - (dialerWidth / 2d);
7
		double y = dialerHeight - yTouch - (dialerHeight / 2d);
8
9
		switch (getQuadrant(x, y)) {
10
			case 1:
11
				return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
12
			case 2:
13
				return 180 - Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
14
			case 3:
15
				return 180 + (-1 * Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
16
			case 4:
17
				return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
18
			default:
19
				return 0;
20
		}
21
	}
22
	
23
	/**

24
	 * @return The selected quadrant.

25
	 */
26
	private static int getQuadrant(double x, double y) {
27
		if (x >= 0) {
28
			return y >= 0 ? 1 : 4;
29
		} else {
30
			return y >= 0 ? 2 : 3;
31
		}
32
	}

Die Methode zum Drehen des Dialers ist sehr einfach. Wir ersetzen jedes Mal den Inhalt des alten ImageView durch die neue gedrehte Bitmap.

1
2
	/**

3
	 * Rotate the dialer.

4
	 * 

5
	 * @param degrees The degrees, the dialer should get rotated.

6
	 */
7
	private void rotateDialer(float degrees) {
8
		matrix.postRotate(degrees);
9
		dialer.setImageBitmap(Bitmap.createBitmap(imageScaled, 0, 0, imageScaled.getWidth(), imageScaled.getHeight(), matrix, true));
10
	}

Beachten Sie, dass das Drehen des Dialers, abgesehen von einigen kleinen Problemen, bereits funktioniert. Beachten Sie außerdem, dass die Bitmap automatisch verkleinert wird, wenn die gedrehte Bitmap nicht in die Ansicht passt (siehe Skalierungstyp der ImageView). Dies verursacht die unterschiedliche Größe.


Schritt 5: Algorithmuskorrektur

Im obigen Schritt wurden einige Fehler eingeführt, beispielsweise die unterschiedliche Skala, die jedoch leicht behoben werden können. Sehen Sie, wo das Problem mit der oben implementierten Methode liegt?

Wenn Sie sich die Implementierung der Berechnung des gedrehten Bildes genauer ansehen, werden Sie schnell feststellen, dass jedes Mal eine neue Bitmap-Instanz erstellt wird. Dies verschwendet viel RAM und führt dazu, dass Garbage Collector häufig im Hintergrund ausgeführt wird (Sie bemerken dieses Problem in der Ausgabe von LogCat). Dies führt dazu, dass die Animation abgehackt aussieht.

Das Problem kann nur durch eine Änderung des Ansatzes vermieden werden. Anstatt eine Bitmap zu drehen und auf die ImageView anzuwenden, sollten wir den Inhalt der ImageView direkt drehen. Zu diesem Zweck können wir den Skalierungstyp der ImageView in "Matrix" ändern und die Matrix jedes Mal auf die ImageView anwenden. Dies fügt auch eine weitere kleine Änderung hinzu: Jetzt müssen wir die skalierte Bitmap direkt nach der Initialisierung zur ImageView hinzufügen.

1
2
<?xml version="1.0" encoding="utf-8"?>
3
<LinearLayout
4
	xmlns:android="http://schemas.android.com/apk/res/android"
5
	android:orientation="vertical"
6
	android:layout_width="fill_parent"
7
	android:layout_height="fill_parent"
8
	android:background="#FFCCCCCC">
9
	<ImageView
10
		android:src="@drawable/graphic_ring"
11
		android:id="@+id/imageView_ring"
12
		android:scaleType="matrix"
13
		android:layout_height="fill_parent"
14
		android:layout_width="fill_parent"></ImageView>
15
</LinearLayout>
1
2
...
3
4
			@Override
5
			public void onGlobalLayout() {
6
        		// method called more than once, but the values only need to be initialized one time

7
        		if (dialerHeight == 0 || dialerWidth == 0) {
8
        			dialerHeight = dialer.getHeight();
9
        			dialerWidth = dialer.getWidth();
10
        			
11
        			// resize

12
					Matrix resize = new Matrix();
13
					resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight());
14
					imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);
15
					
16
					dialer.setImageBitmap(imageScaled);
17
					dialer.setImageMatrix(matrix);
18
        		}
19
			}
20
21
...
22
23
	/**

24
	 * Rotate the dialer.

25
	 * 

26
	 * @param degrees The degrees, the dialer should get rotated.

27
	 */
28
	private void rotateDialer(float degrees) {
29
		matrix.postRotate(degrees);
30
		
31
		dialer.setImageMatrix(matrix);
32
	}

Jetzt tritt ein weiterer Fehler auf. Die Berechnung des Winkels funktioniert ordnungsgemäß mit der ImageView mitte. Das Bild dreht sich jedoch um die Koordinate [0, 0]. Daher müssen wir das Bild bei der Initialisierung an die richtige Position verschieben. Gleiches gilt für die Rotation. Zuerst muss die Bitmap verschoben werden, damit sich die Mitte im Ursprung der ImageView befindet, dann kann die Bitmap gedreht und wieder zurück verschoben werden.

1
2
...
3
			@Override
4
			public void onGlobalLayout() {
5
        		// method called more than once, but the values only need to be initialized one time

6
        		if (dialerHeight == 0 || dialerWidth == 0) {
7
        			dialerHeight = dialer.getHeight();
8
        			dialerWidth = dialer.getWidth();
9
        			
10
        			// resize

11
					Matrix resize = new Matrix();
12
					resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight());
13
					imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);
14
					
15
					// translate to the image view's center

16
					float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
17
					float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
18
					matrix.postTranslate(translateX, translateY);
19
					
20
					dialer.setImageBitmap(imageScaled);
21
					dialer.setImageMatrix(matrix);
22
        		}
23
			}
24
			
25
...
26
27
	/**

28
	 * Rotate the dialer.

29
	 * 

30
	 * @param degrees The degrees, the dialer should get rotated.

31
	 */
32
	private void rotateDialer(float degrees) {
33
		matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);
34
		
35
		dialer.setImageMatrix(matrix);
36
	}

Jetzt funktioniert unsere Animation viel besser und ist sehr flüssig.


Schritt 6: Schleudern

Android bietet eine benutzerfreundliche API zum Erkennen von Gesten. Zum Beispiel können ein Bildlauf, langes Drücken und ein Schleudern erkannt werden. Für unsere Zwecke erweitern wir die SimpleOnGestureListener-Klasse und überschreiben die Methode onFling. Diese Klasse wird in der Dokumentation empfohlen.

Außerdem müssen wir die Rotation animieren und dadurch ihre Geschwindigkeit verringern. Da es sich nur um kleine Berechnungen handelt, ist es nicht erforderlich, die Funktionalität in einem zusätzlichen Thread auszulagern. Stattdessen können Sie die Aktion an einen Handler übertragen, der die GUI im Hauptthread bearbeiten kann. Wir können die Handlerinstanz sogar vermeiden, indem wir die Aktion an ImageView übergeben, das einen eigenen Handler verwendet.

1
2
...
3
4
	private int dialerHeight, dialerWidth;
5
	
6
	private GestureDetector detector;
7
	
8
	@Override
9
    public void onCreate(Bundle savedInstanceState) {
10
11
...
12
   
13
        detector = new GestureDetector(this, new MyGestureDetector());
14
        
15
        dialer = (ImageView) findViewById(R.id.imageView_ring);
16
        dialer.setOnTouchListener(new MyOnTouchListener());
17
		
18
...
19
20
	/**

21
	 * Simple implementation of an {@link OnTouchListener} for registering the dialer's touch events. 

22
	 */
23
	private class MyOnTouchListener implements OnTouchListener {
24
		
25
		private double startAngle;
26
27
		@Override
28
		public boolean onTouch(View v, MotionEvent event) {
29
30
			switch (event.getAction()) {
31
				
32
				case MotionEvent.ACTION_DOWN:
33
					startAngle = getAngle(event.getX(), event.getY());
34
					break;
35
					
36
				case MotionEvent.ACTION_MOVE:
37
					double currentAngle = getAngle(event.getX(), event.getY());
38
					rotateDialer((float) (startAngle - currentAngle));
39
					startAngle = currentAngle;
40
					break;
41
					
42
				case MotionEvent.ACTION_UP:
43
					
44
					break;
45
			}
46
			
47
			detector.onTouchEvent(event);
48
			
49
			return true;
50
		}
51
	}
52
	
53
	/**

54
	 * Simple implementation of a {@link SimpleOnGestureListener} for detecting a fling event. 

55
	 */
56
	private class MyGestureDetector extends SimpleOnGestureListener {
57
		@Override
58
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
59
			dialer.post(new FlingRunnable(velocityX + velocityY));
60
			return true;
61
		}
62
	}
63
	
64
	/**

65
	 * A {@link Runnable} for animating the the dialer's fling.

66
	 */
67
	private class FlingRunnable implements Runnable {
68
69
		private float velocity;
70
71
		public FlingRunnable(float velocity) {
72
			this.velocity = velocity;
73
		}
74
75
		@Override
76
		public void run() {
77
			if (Math.abs(velocity) > 5) {
78
				rotateDialer(velocity / 75);
79
				velocity /= 1.0666F;
80
				
81
				// post this instance again

82
				dialer.post(this);
83
			}
84
		}
85
	}

Schritt 7: Fixieren der umgekehrten Rotationen

Wenn Sie den Schleudergang ausgiebig testen, wird schnell klar, dass sich der Dialer an verschiedenen Positionen in die falsche Richtung dreht. Dies ist beispielsweise der Fall, wenn sich Start- und Endpunkt nur im dritten Quadranten befinden. Es gibt jedoch auch schwierigere Fehlerfälle (z. B. beim Abwischen von Quadrant zwei auf vier über Quadrant drei).

Die einfachste Lösung besteht darin, sich an die berührten Quadranten zu erinnern. Die Fehlerfälle werden in der onFling-Methode abgefangen und die Geschwindigkeit wird entsprechend invertiert.

1
2
...
3
4
	private GestureDetector detector;
5
	
6
	// needed for detecting the inversed rotations

7
	private boolean[] quadrantTouched;
8
	
9
...
10
11
        detector = new GestureDetector(this, new MyGestureDetector());
12
        
13
		// there is no 0th quadrant, to keep it simple the first value gets ignored

14
        quadrantTouched = new boolean[] { false, false, false, false, false };
15
        
16
        dialer = (ImageView) findViewById(R.id.imageView_ring);
17
		
18
...
19
20
	/**

21
	 * Simple implementation of an {@link OnTouchListener} for registering the dialer's touch events. 

22
	 */
23
	private class MyOnTouchListener implements OnTouchListener {
24
		
25
		private double startAngle;
26
27
		@Override
28
		public boolean onTouch(View v, MotionEvent event) {
29
30
			switch (event.getAction()) {
31
				
32
				case MotionEvent.ACTION_DOWN:
33
					
34
					// reset the touched quadrants

35
					for (int i = 0; i < quadrantTouched.length; i++) {
36
						quadrantTouched[i] = false;
37
					}
38
					
39
					startAngle = getAngle(event.getX(), event.getY());
40
					break;
41
					
42
				case MotionEvent.ACTION_MOVE:
43
					double currentAngle = getAngle(event.getX(), event.getY());
44
					rotateDialer((float) (startAngle - currentAngle));
45
					startAngle = currentAngle;
46
					break;
47
					
48
				case MotionEvent.ACTION_UP:
49
					
50
					break;
51
			}
52
			
53
			// set the touched quadrant to true

54
			quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true;
55
			
56
			detector.onTouchEvent(event);
57
			
58
			return true;
59
		}
60
	}
61
	
62
	/**

63
	 * Simple implementation of a {@link SimpleOnGestureListener} for detecting a fling event. 

64
	 */
65
	private class MyGestureDetector extends SimpleOnGestureListener {
66
		@Override
67
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
68
			
69
			// get the quadrant of the start and the end of the fling

70
			int q1 = getQuadrant(e1.getX() - (dialerWidth / 2), dialerHeight - e1.getY() - (dialerHeight / 2));
71
			int q2 = getQuadrant(e2.getX() - (dialerWidth / 2), dialerHeight - e2.getY() - (dialerHeight / 2));
72
73
			// the inversed rotations

74
			if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math.abs(velocityY))
75
					|| (q1 == 3 && q2 == 3)
76
					|| (q1 == 1 && q2 == 3)
77
					|| (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math.abs(velocityY))
78
					|| ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))
79
					|| ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))
80
					|| (q1 == 2 && q2 == 4 && quadrantTouched[3])
81
					|| (q1 == 4 && q2 == 2 && quadrantTouched[3])) {
82
			
83
				dialer.post(new FlingRunnable(-1 * (velocityX + velocityY)));
84
			} else {
85
				// the normal rotation

86
				dialer.post(new FlingRunnable(velocityX + velocityY));
87
			}
88
89
			return true;
90
		}
91
	}

Schritt 8: Stop Fling

Natürlich muss die Fling-Animation gestoppt werden, wenn Sie den Dialer berühren, während die Animation ausgeführt wird. Dazu muss nur ein boolescher Wert hinzugefügt werden, um zu überprüfen, ob die Animation abgespielt werden darf oder nicht.

Im ACTION_DOWN-Ereignis möchten Sie es stoppen. Sobald Sie den Dialer loslassen (ACTION_UP), sollte die Animation abgespielt werden.

1
2
...
3
4
	// needed for detecting the inversed rotations

5
	private boolean[] quadrantTouched;
6
7
	private boolean allowRotating;
8
	
9
...
10
11
		allowRotating = true;
12
        
13
        dialer = (ImageView) findViewById(R.id.imageView_ring);
14
		
15
...
16
17
			switch (event.getAction()) {
18
				
19
				case MotionEvent.ACTION_DOWN:
20
					
21
					// reset the touched quadrants

22
					for (int i = 0; i < quadrantTouched.length; i++) {
23
						quadrantTouched[i] = false;
24
					}
25
					
26
					allowRotating = false;
27
					
28
					startAngle = getAngle(event.getX(), event.getY());
29
					break;
30
					
31
				case MotionEvent.ACTION_MOVE:
32
					double currentAngle = getAngle(event.getX(), event.getY());
33
					rotateDialer((float) (startAngle - currentAngle));
34
					startAngle = currentAngle;
35
					break;
36
					
37
				case MotionEvent.ACTION_UP:
38
					allowRotating = true;
39
					break;
40
			}
41
			
42
...
43
44
		@Override
45
		public void run() {
46
			if (Math.abs(velocity) > 5 && allowRotating) {
47
				rotateDialer(velocity / 75);
48
				velocity /= 1.0666F;
49
50
				// post this instance again

51
				dialer.post(this);
52
			}
53
		}

Schritt 9: Wie geht es weiter?

Alle gewünschten Funktionen sind im Dialer enthalten. Dies ist jedoch nur ein Beispiel und kann auf unbestimmte Zeit erweitert werden.

Einige zusätzliche Verbesserungen, die mir in den Sinn kommen, sind: Sie können den Winkel verfolgen und bestimmte Werte invertieren oder verringern (ein Beispiel hierfür finden Sie in meiner App Shutdown Remote). Nachdem Sie den Dialer losgelassen haben, können Sie ihn wieder in die ursprüngliche Position animieren, um die mechanische Funktion von echten, antiken Drehknöpfen zu reproduzieren. Ferner könnte der Dialer in eine benutzerdefinierte View-Klasse extrahiert werden, was die Wiederverwendbarkeit der Komponente erhöhen würde. Die Möglichkeiten zur Verbesserung sind vielfältig!


Abschluss

In diesem Tutorial habe ich Ihnen gezeigt, wie Sie einen einfachen Wählschalter implementieren. Natürlich kann die Funktionalität wie oben beschrieben erweitert und verbessert werden. Das Android SDK bietet eine großartige und sehr nützliche API mit vielen Klassen. Es ist nicht nötig, viel neu zu erfinden. Normalerweise müssen Sie nur das implementieren, was bereits vorhanden ist. Zögern Sie nicht, Kommentare abzugeben oder Vorschläge zu hinterlassen!


Quellen

http://en.wikipedia.org/wiki/File:Cartesian_coordinates_2D.svg