Material Slider
Google hat am 16. Juli 2020 Material Components 1.3.0-alpha02 veröffentlicht. Eine Komponente, die in dieser Version viel Liebe erhalten hat, ist der bescheidene Schieberegler. Es gibt einige nette Ergänzungen zur Grundfunktionalität, und in diesem Beitrag werden wir sie ein wenig erkunden.
Ein Slider
ist ein nützliches Steuerelement, mit dem der Benutzer Werte angeben kann, ohne die Tastatur benutzen zu müssen. Der grundlegende Anwendungsfall ist dem Android Framework SeekBar
oder den AppCompatSeekBar
Widgets sehr ähnlich. Für diese gibt es verschiedene Verwendungszwecke: z. B. das Scrubben zu einer bestimmten Stelle während der Audio- oder Videowiedergabe oder die Angabe eines Wertes innerhalb eines bestimmten Bereichs. Es gibt jedoch einige zusätzliche Anwendungsfälle, die Slider
uns bietet, wie wir später in diesem Artikel sehen werden
Wir beginnen mit der Erstellung eines sehr einfachen Schieberegler-Steuerelements und sehen, wie es aussieht und sich verhält.
Grundlegendes Verhalten
Das Erstellen eines Material Sliders ist sehr einfach:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<?xml version=“1.0″ encoding=“utf-8″?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android“
xmlns:app=“http://schemas.android.com/apk/res-auto“
xmlns:tools=“http://schemas.android.com/tools“
android:layout_width=“match_parent“
android:layout_height=“match_parent“
android:padding=“32dp“
tools:context=“.MainActivity“>
<androidx.appcompat.widget.AppCompatSeekBar
android:id=“@+id/seek_bar“
android:layout_width=“0dp“
android:layout_height=“wrap_content“
android:valueFrom=“0″
android:valueTo=“100″
app:layout_constraintBottom_toTopOf=“@id/discrete_slider“
app:layout_constraintEnd_toEndOf=“parent“
app:layout_constraintStart_toStartOf=“parent“
app:layout_constraintTop_toTopOf=“parent“ />
<com.google.android.material.slider.Slider
android:id=“@+id/continuous_slider“
android:layout_width=“0dp“
android:layout_height=“wrap_content“
android:valueFrom=“0″
android:valueTo=“100″
app:layout_constraintBottom_toTopOf=“@id/discrete_slider“
app:layout_constraintEnd_toEndOf=“@id/seek_bar“
app:layout_constraintStart_toStartOf=“@id/seek_bar“
app:layout_constraintTop_toBottomOf=“@id/seek_bar“ />
</ConstraintLayout>
|
Wir legen für beide Steuerelemente mit den Attributen valueFrom
und valueTo
einen Bereich von 0-100 fest. Auf diese Weise erhalten wir die folgenden Verhaltensweisen (AppCompatSeekBar
steht oben und Slider
unten):
Auch wenn diese sehr ähnlich aussehen, gibt es einige wichtige Unterschiede. Erstens wird Slider
mit den in meinem Thema definierten Farben gestaltet, während AppCompatSeekBar
Standardfarben aus der AppCompat-Bibliothek verwendet. Zweitens ist Slider
etwas größer, was es für den Benutzer etwas übersichtlicher macht. Der wahrscheinlich wichtigste Unterschied ist die Hinzufügung einer Beschriftung zu Slider
, wenn der Benutzer die Position verschiebt. Dadurch wird es für den Benutzer einfacher, den genauen Wert zu verstehen, den er auswählt. Wir können zwar eine separate Ansicht in unser Layout einfügen, um diesen Wert anzuzeigen, aber die Beschriftung der QuickInfo macht die Benutzeroberfläche weniger unübersichtlich. Wir können diese Funktion deaktivieren, wenn sie nicht benötigt wird. Durch Hinzufügen von app:labelBehaviour="gone"
wird dies erreicht.
Es gibt jedoch noch zwei weitere Verhaltensweisen für die Beschriftung. Die erste ist die Standardeinstellung, die wir im vorherigen Beispiel sehen konnten – das ist app:labelBehaviour="floating"
, die das Etikett über der Ansicht schweben lässt. Die andere Option ist app:labelBehaviour="withinBounds"
. Diese Option wird genauso gerendert wie floating
, aber der Unterschied besteht darin, wie sie die Größe von Slider
beeinflusst. Bei floating
schließt die gemessene Höhe des Steuerelements die Beschriftung nicht ein, während bei withinBounds
die Beschriftung in die gemessene Höhe einbezogen wird. Dies kann wichtig sein, je nachdem, ob wir sicherstellen wollen, dass die Beschriftung keine anderen Steuerelemente in unserem Layout verdeckt.
Diskreter Schieberegler
Die Variante von Slider
, die wir uns bisher angesehen haben, ist als kontinuierlicher Schieberegler bekannt. Das bedeutet, dass der Wert, den er zurückgibt, ein beliebiger Wert innerhalb des Bereichs sein kann. Es gibt jedoch Fälle, in denen wir dies einschränken möchten. Zum Beispiel werden von diesem kontinuierlichen Slider
gebrochene Werte zurückgegeben, aber wir möchten den Wert vielleicht auf ganze Zahlen oder noch größere Schritte beschränken. Um dies zu erreichen, können wir einen diskreten Schieberegler verwenden, der eine diskrete Menge von Werten zurückgibt. Wir können einen kontinuierlichen Slider
in einen diskreten umwandeln, indem wir ein einzelnes Attribut hinzufügen:
1
2
3
4
5
6
7
8
9
10
11
|
<com.google.android.material.slider.Slider
android:id=“@+id/continuous_slider“
android:layout_width=“0dp“
android:layout_height=“wrap_content“
android:stepSize=“10″
android:valueFrom=“0″
android:valueTo=“100″
app:layout_constraintBottom_toTopOf=“@id/discrete_slider“
app:layout_constraintEnd_toEndOf=“@id/seek_bar“
app:layout_constraintStart_toStartOf=“@id/seek_bar“
app:layout_constraintTop_toBottomOf=“@id/seek_bar“ />
|
Durch das Hinzufügen von app:stepSize="10"
gibt die Slider
nur Werte zurück, die ein Vielfaches von 10 sind. Dies ändert die UX leicht:
Zunächst gibt es Häkchen, die die diskreten Werte anzeigen, die zurückgegeben werden. Diese sind zwar recht subtil, aber auf der Spur deutlich zu sehen. Der zweite Unterschied besteht darin, dass die Auswahl nicht mehr fließend ist, sondern zwischen den diskreten Werten hin und her springt, wie man am Wert der Beschriftung erkennen kann. Der Benutzer hat einen klaren Hinweis darauf, dass die Auswahl aus einer diskreten Menge von Werten erfolgt.
RangeSlider
Wir können auch eine andere Variante von Slider
verwenden. Bisher haben wir gesehen, wie man mit Slider
einen einzelnen Wert auswählt, aber der Benutzer kann mit RangeSlider
einen Bereich auswählen.
Als wir Slider
zum ersten Mal konfiguriert haben, haben wir valueFrom
und valueTo
verwendet, um den Wertebereich anzugeben, den der Schieberegler zurückgibt. RangeSlider
ermöglicht es dem Benutzer, einen Bereich anzugeben. Ein Beispiel: Wenn wir eine Suchfunktion in einer Shopping-App anbieten, möchten wir vielleicht, dass der Benutzer eine Preisspanne angeben kann. Dies kann mit einem RangeSlider
erreicht werden, der sowohl kontinuierliche als auch diskrete Modi unterstützt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<com.google.android.material.slider.RangeSlider
android:id=“@+id/continuous_range“
android:layout_width=“0dp“
android:layout_height=“wrap_content“
android:valueFrom=“0″
android:valueTo=“100″
app:layout_constraintBottom_toTopOf=“@id/discrete_range“
app:layout_constraintEnd_toEndOf=“@id/continuous_slider“
app:layout_constraintStart_toStartOf=“@id/continuous_slider“
app:layout_constraintTop_toBottomOf=“@id/discrete_slider“
app:values=“@array/double_slider_values“/>
<com.google.android.material.slider.RangeSlider
android:id=“@+id/discrete_range“
android:layout_width=“0dp“
android:layout_height=“wrap_content“
android:stepSize=“10″
android:valueFrom=“0″
android:valueTo=“100″
app:layout_constraintBottom_toBottomOf=“parent“
app:layout_constraintEnd_toEndOf=“@id/continuous_slider“
app:layout_constraintStart_toStartOf=“@id/continuous_slider“
app:layout_constraintTop_toBottomOf=“@id/continuous_range“
app:values=“@array/triple_slider_values“ />
|
Hier gibt es ein erforderliches Attribut: app:values
– ohne dieses fällt RangeSlider
auf das Standardverhalten Slider
zurück. Wir müssen ein Array von Werten in diesem Attribut angeben:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version=“1.0″ encoding=“utf-8″?>
<resources>
<array name=“double_slider_values“>
<item>20</item>
<Eintrag>60</Eintrag>
</array>
<array name=“triple_slider_values“>
<Eintrag>20</Eintrag>
<Eintrag>60</Eintrag>
<Eintrag>100</Eintrag>
</array>
</resources>
|
Diese Arrays haben zwei bzw. drei Werte, und das kontinuierliche RangeSlider
erhält den Bereich mit zwei Werten, während das diskrete RangeSlider
den Bereich mit drei Werten erhält. Daraus ergeben sich einige interessante neue Verhaltensweisen:
Das obere Beispiel zeigt einen einfachen Bereich mit zwei Werten, der es dem Benutzer ermöglicht, einen Bereich mit Ober- und Untergrenzen anzugeben. Der Gesamtbereich, den wir für RangeSlider
festlegen, schränkt die Höchst- und Mindestwerte ein, die der Benutzer angeben kann.
Das untere Beispiel ist noch interessanter. Es handelt sich nicht nur um einen diskreten RangeSlider
, sondern die drei Werte im Array erzeugen drei Daumenpunkte im Steuerelement. Der Benutzer kann nicht nur eine obere und eine untere Grenze für den Bereich angeben, sondern auch einen Punkt irgendwo innerhalb der oberen und unteren Grenze. Der Benutzer kann den mittleren Daumenpunkt nicht außerhalb der durch den oberen und unteren Punkt festgelegten Grenzen ziehen. Die Beschriftung des Daumenpunkts, der gezogen wird, wird immer über den anderen Beschriftungen gezeichnet, die er überlappen könnte. Dies ist wichtig, damit der Benutzer weiß, welchen Wert er gerade auswählt.
Wir können die ausgewählten Werte abrufen, indem wir die Methode getValues()
von RangeSlider
aufrufen. Dies gibt ein Array von Werten zurück, dessen Größe der Größe des Arrays entspricht, das wir in app:values
angegeben haben.
Etikettenformatierung
Den aufmerksamen Beobachtern ist vielleicht aufgefallen, dass es einen Unterschied in der Etikettenformatierung der doppel- und dreifachwertigen RangeSliders
gibt. Der kontinuierliche doppelte Wert RangeSlider
verwendet das Standardformat für die Beschriftung, während der diskrete dreifache Wert das Präfix Value:
hat. Dies bringt uns zu der Tatsache, dass wir die Bezeichnung anpassen können:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.discreteRange.setLabelFormatter {
getString(R.string.label_format, it)
}
}
}
|
LabelFormatter
ist eine SAM-Schnittstelle mit einer einzigen getFormattedValue()
-Methode, die in diesem Fall in ein Lambda umgewandelt wurde. Ich habe dafür eine Formatstring-Ressource verwendet:
1
2
3
4
|
<resources>
<string name=“app_name“>Material Slider</string>
<string name=“label_format“>Wert: %1$.0f</string>
</resources>
|
Wir müssen das programmatisch machen. Ich bin mir ziemlich sicher, dass ein Formatstring wie dieser für die meisten Anwendungsfälle geeignet ist, daher wäre es schön, wenn wir ihn mit einem XML-Attribut angeben könnten. Eine programmatische Anwendung ist jedoch ziemlich trivial.
Schlussfolgerung
Slider
und RangeSlider
sind wirklich schöne Abwandlungen von SeekBar
. Sie bieten die gleiche Funktionalität und noch viel mehr. Besonders beeindruckt bin ich von der Liebe zum Detail – zum Beispiel, dass die aktive Beschriftung über die passive Beschriftung gezeichnet wird. Dies ist ein gut konzipiertes und implementiertes Steuerelement, und ich habe die Absicht, es zu verwenden!