Material Slider
A Google 2020. július 16-án kiadta a Material Components 1.3.0-alpha02 verziót. Az egyik komponens, ami ebben a kiadásban sok szeretetet kapott, az a szerény csúszka. Az alapvető funkcionalitáson felül van néhány szép kiegészítés, és ebben a bejegyzésben ezeket vizsgáljuk meg egy kicsit.
A Slider
egy hasznos vezérlőelem, amely lehetővé teszi a felhasználó számára, hogy értékeket adjon meg anélkül, hogy a billentyűzetet kellene használnia. Az alapvető felhasználási módja nagyon hasonló az Android Framework SeekBar
vagy a AppCompatSeekBar
widgetekhez. Ezeknek különböző felhasználási módjai vannak: például egy adott helyre való súrolás hang- vagy videolejátszás közben; vagy egy adott tartományon belüli érték megadása. Van azonban néhány további felhasználási lehetőség, amelyet a Slider
biztosít számunkra, amint azt a cikk későbbi részében látni fogjuk
Egy nagyon egyszerű csúszkavezérlő létrehozásával kezdjük, és megnézzük, hogyan néz ki és hogyan viselkedik.
Az alapvető viselkedés
A Material Slider létrehozása nagyon egyszerű:
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>
|
Az valueFrom
és valueTo
attribútumok segítségével mindkét vezérlőn 0-100 közötti tartományt adunk meg. Ezzel a következő viselkedéseket kapjuk (a AppCompatSeekBar
van felül, a Slider
pedig alul):
Míg ezek nagyon hasonlónak tűnnek, van néhány fontos különbség. Először is, a Slider
a témámban meghatározott színeket használja, míg a AppCompatSeekBar
az AppCompat könyvtárból származó alapértelmezett színeket. Másodszor, a Slider
kissé nagyobb, így a felhasználó számára kicsit áttekinthetőbb. Valószínűleg a legfontosabb különbség a Slider
címke hozzáadása, amikor a felhasználó áthúzza a pozíciót. Ez megkönnyíti a felhasználó számára, hogy megértse, pontosan milyen értéket választ ki. Bár természetesen hozzáadhatunk egy külön View-t az elrendezésünkhöz, hogy megjelenítsük ezt az értéket, a tooltip címke kevésbé zsúfolttá teszi a felhasználói felületet. Ezt kikapcsolhatjuk, ha nincs rá szükség. A app:labelBehaviour="gone"
hozzáadásával ezt elérhetjük.
Mégis van két másik címke viselkedés. Az első az alapértelmezett, amelyet az előző példában láthatunk – ez a app:labelBehaviour="floating"
, amely a címkét a nézet fölé lebegtet. A másik lehetőség a app:labelBehaviour="withinBounds"
. Ez ugyanazt rendereli, mint a floating
, de a különbség az, hogy hogyan befolyásolja a Slider
méretét. A floating
esetén a vezérlő mért magassága nem tartalmazza a címkét, míg a withinBounds
esetén a címke szerepel a mért magasságban. Ez attól függően lehet fontos, hogy szeretnénk-e biztosítani, hogy a címke ne takarjon el más vezérlőelemeket az elrendezésünkben.
Diszkrét csúszka
A Slider
eddig vizsgált változatát folyamatos csúszkának nevezzük. Ez azt jelenti, hogy az általa visszaadott érték a tartományon belül bármilyen érték lehet. Vannak azonban olyan esetek, amikor ezt korlátozni szeretnénk. Például tört értékeket ad vissza ez a folytonos Slider
, de lehet, hogy igényünk van arra, hogy az értéket egész számokra vagy még nagyobb lépésekre korlátozzuk. Ennek eléréséhez használhatunk diszkrét csúszkát – amely diszkrét értékkészletet ad vissza. A folyamatos Slider
-t egyetlen attribútum hozzáadásával diszkrétre változtathatjuk:
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:
|
A app:stepSize="10"
hozzáadásával a Slider
csak olyan értékeket ad vissza, amelyek 10 többszörösei. Ez kissé megváltoztatja a UX-et:
Először is vannak jelölések, amelyek a visszaadandó diszkrét értékeket jelzik. Ezek quire finom, de jól láthatóak a pályán. A második különbség, hogy a kijelölés már nem sima, hanem ugrál a diszkrét értékek között, ahogy azt a címke értékénél láthatjuk. A felhasználónak egyértelműen jelzi, hogy a kiválasztás egy diszkrét értékkészletből történik.
RangeSlider
A Slider
egy másik változatát is használhatjuk. Eddig láttuk, hogyan lehet egyetlen értéket kiválasztani egy Slider
segítségével, de a felhasználó egy tartományt is kiválaszthat egy RangeSlider
segítségével.
Az Slider
első konfigurálásakor a valueFrom
és valueTo
értékeket használtuk annak az értéktartománynak a megadására, amelyet a csúszka visszaad. egy RangeSlider
lehetővé teszi a felhasználó számára egy tartomány megadását. Például: ha keresési lehetőséget biztosítanánk egy vásárlási alkalmazáson belül, akkor azt szeretnénk, ha a felhasználó meg tudna adni egy ártartományt. Ezt a RangeSlider
használatával érhetjük el, amely folyamatos és diszkrét módot egyaránt támogat:
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” />
|
Itt egy kötelező attribútum van: app:values
– e nélkül a RangeSlider
visszaesik a szokásos Slider
viselkedésre. Ebben az attribútumban egy értékekből álló tömböt kell megadnunk:
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>.
<item>60</item>
</array>
<array name=”triple_slider_values”>
<item>20</item>
<item>60</item>
<item>100</item>
</array>
</resources>
|
Ezeknek a tömböknek két, illetve három értéke van, és a folyamatos RangeSlider
a két értékkel rendelkező tartományt kapja, míg a diszkrét RangeSlider
a három értékkel rendelkező tartományt. Ez néhány érdekes új viselkedést eredményez:
A felső példa egy egyszerű kétértékű tartományt mutat, amely lehetővé teszi a felhasználó számára, hogy felső és alsó határokkal rendelkező tartományt adjon meg. A RangeSlider
számára beállított teljes tartomány korlátozza a felhasználó által megadható maximális és minimális értékeket.
Az alsó példa még érdekesebb. Amellett, hogy diszkrét RangeSlider
, a tömb három értéke három hüvelykujjpontot hoz létre a vezérlőben. Amellett, hogy a tartománynak csak egy felső és egy alsó határa van, a felhasználó egy pontot is megadhat valahol a felső és az alsó határon belül. A felhasználó nem húzhatja a középső hüvelykujjpontot a felső és alsó pontok által meghatározott határokon kívülre. A húzott hüvelykujjpont címkéje mindig a többi, esetleg átfedő címke fölé rajzolódik. Ez azért fontos, hogy a felhasználó tudja, hogy éppen melyik értéket választja ki.
A kiválasztott értékeket a RangeSlider
getValues()
metódusának meghívásával tudjuk lekérdezni. Ez egy értékekből álló tömböt ad vissza, amelynek mérete megegyezik a app:values
-ben megadott tömb méretével.
Címkeformázás
A figyelmesebbek talán észrevették, hogy a dupla és a tripla értékű RangeSliders
címkeformázása között különbség van. A folytonos kettős értékű RangeSlider
az alapértelmezett címkeformátumot használja, míg a diszkrét hármas értékű Value:
előtaggal rendelkezik. Ezzel szépen rátérünk arra a tényre, hogy testre szabhatjuk a címkét:
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
egy SAM interfész egyetlen getFormattedValue()
metódussal, amelyet ebben az esetben lambdává alakítottunk. Ehhez egy formázott string erőforrást használtam:
1
2
3
4
|
. <resources>
<string name=”app_name”>Material Slider</string>
<string name=”label_format”>Value: %1$.0f</string>
</resources>
|
Ezt programozottan kell megtennünk. Biztos vagyok benne, hogy egy ilyen formátumú karakterlánc a felhasználási esetek többségének megfelelne, ezért jó lenne, ha egy XML-attribútummal megadhatnánk. Egy ilyen programozott alkalmazása azonban elég triviális.
Következtetés
Slider
és RangeSlider
igazán szép iterációk a SeekBar
-on. Ugyanazt a funkcionalitást és még sokkal többet nyújtanak. Különösen lenyűgözött a részletekre való odafigyelés – például az aktív címke rajzolása a passzív címkék tetejére. Ez egy jól megtervezett és megvalósított vezérlő, és teljes mértékben használni szándékozom!