La SeekBar En Android es una subclase de la ProgressBar
que extiende sus funcionalidades para agregar un control deslizante en la pista del indicador.
Su objetivo es permitirte elegir un único valor de un rango de valores conocidos previamente. Esto puedes verlo en casos de uso como: ajustes de preferencias (volumen, brillo, alpha, etc.), filtros de características de items, control temporal de reproductores de música y video, etc.
A lo largo de este tutorial usaremos la siguiente App de ejemplo para interiorizar el conocimiento sobre la SeekBar
. Se trata de la inclusión de dos variaciones del view para procesar eventos de cambios del deslizador, cambio del color y cambio del drawable del deslizador:
Descarga el código del proyecto Android Studio desde el siguiente enlace:
Atributos De SeekBar En Android
SeekBar
es descendiente de AbsSeekBar
, que a su vez es descendiente de ProgressBar
. Por esta razón, podrás acceder a los atributos para el manejo de progreso sin problemas.
Para soportar al deslizador, se agregaron los siguientes elementos:
Atributo XML | Descripción |
---|---|
android:thumb | Es el drawable para proyectar al deslizador (pulgar o perilla) |
android:thumbTint | Tinte que se aplica a thumb |
android:tickMarkTint | Tinte que se aplica al drawable de las graduaciones sobre el indicador |
Ahora veamos como modificar estos y otros atributos para cambiar la presentación de la SeekBar.
Crear SeekBar
Supongamos que necesitamos crear una SeekBar con un rango de valores entre 0 y 60 y un progreso por defecto en 45:
Para añadir una SeekBar
en Android Studio ve a Palette>Widgets>SeekBar y arrastra el componente al lienzo o al Component Tree:
La creación en XML que satisface la configuración se representaría con la etiqueta <SeekBar>
y la modificación de los atriabutos android:max
y android:progress
:
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="60"
android:progress="45" />
También podemos hacerlo programáticamente. Asumiendo que su padre sea un ConstraintLayout
(todo), crear la SeekBar en Kotlin se vería así:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val parent: ConstraintLayout = findViewById(R.id.constraint_layout)
val seekbar = SeekBar(this).apply {
id = ViewCompat.generateViewId()
max = 60
progress = 45
layoutParams = LayoutParams(0, WRAP_CONTENT)
}
parent.addView(seekbar)
with(ConstraintSet()) {
clone(parent)
connect(seekbar.id, TOP, PARENT_ID, TOP)
connect(seekbar.id, RIGHT, PARENT_ID, RIGHT)
connect(seekbar.id, LEFT, PARENT_ID, LEFT)
applyTo(parent)
}
}
}
Nos valemos de la función apply()
para contextualizar la inicialización del objeto SeekBar
. Luego añadimos la barra al constraint layout y finalmente conectamos sus limites a través de with()
para mejorar la legibilidad de invocaciones.
SeekBar Discreta
Existe un estilo que añade graduaciones sobre el indicador lineal de la SeekBar. Estas son representadas por marcas uniformes a lo largo de la pista de selección:
A esta representación se le denomina SeekBar discreta y facilitan la selección exacta de un valor del rango. Para implementarla solo usa el estilo Widget.AppCompat.SeekBar.Discrete
:
<SeekBar
android:id="@+id/discrete_seekbar"
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="5"
android:progress="3" />
Cada punto de graduación representará a la unidad, por lo que seteas a max
muy alto se mostrará atosigado el indicador. Por lo que la SeekBar
discreta funciona mejor con pequeños conjuntos de valores preestablecidos.
Observar Cambios De La SeekBar
En nuestra app de ejemplo usamos el evento de cambio de progreso para actualizar el texto asociado a las SeekBars:
Lograr esta detección de cambios se consigue usando a la escucha OnSeekBarChangeListener
. Esta nos provee los siguientes controladores:
onProgressChanged()
: Notifica que el progreso ha cambiado. Puedes usar el tercer parámetrofromUser
para determinar si lo provocó el usuario.onStartTrackingTouch()
: Notifica que el usuario comenzó un gesto táctil sobre la SeekBaronStopTrackingTouch()
: Notifica que el usuario terminó el gesto táctil
En el caso de la SeekBar que hace seguimiento a la transparencia de la letra D, implementamos a onProgressChanged()
para actualizar al cambiar el progreso:
continuousSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
letter.alpha = progress/60f
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
Y por el lado de la seekbar discreta que actualiza el texto dependiendo de la temperatura, implementamos a onStopTrackingTouch()
:
discreteSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
seekBar?.apply {
tempLevel.text = when (progress) {
0 -> "Glacial"
1 -> "Páramo"
2 -> "Frío"
3 -> "Templado"
4 -> "Cálido"
else -> "Desconocido"
}
}
}
})
Debido a que el argumento seekBar
es anulable, usamos al operador de acceso seguro ?
, seguido de la invocación de apply()
para setear a text
el resultado de la expresión when
.
Cambiar Color Del Deslizador
De repente queremos que los drawables del thumb, el progreso y la pista se proyecten con escalas de rojo:
Para modificar estos colores modificamos los valores de android:thumb
, android:progressTint
y android:progressBackgroundTint
:
<SeekBar
android:id="@+id/continuous_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="60"
android:thumbTint="@color/red_700"
android:progressTint="@color/red_500"
android:progressBackgroundTint="@color/red_700"
android:progress="45" />
No obstante, estos requieren el uso de un nivel mínimo de API en 21. Si no cumples con estas condiciones, lo mejor es colorearlos desde el código con la clase DrawableCompat
:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val continuousSeekBar: SeekBar = findViewById(R.id.continuous_seekbar)
continuousSeekBar.apply {
tintDrawable(thumb, R.color.red_700)
tintDrawable(progressDrawable, R.color.red_500)
}
}
private fun tintDrawable(drawable: Drawable, @ColorRes color:Int){
DrawableCompat.setTint(
drawable,
ContextCompat.getColor(this, color)
)
}
}
Cambiar Drawable Del Thumb
En la seekbar discreta que hace seguimiento a niveles de temperatura modificamos el drawable del deslizador por un vector de termostato:
Reemplazar el drawable requiere la asignación del atributo android:thumb
por el recurso que necesites en tu etiqueta <SeekBar>
:
<SeekBar
android:id="@+id/discrete_seekbar"
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="4"
android:splitTrack="false"
android:thumb="@drawable/ic_temp_thumb"
android:progress="3"/>
Recuerda que puedes presionar click derecho en el directorio res/drawable
y seleccionar la opción New > Vector Asset
para agregar vectores. Ahí encontrarás una miscelánea con gran variedad de elementos. En este caso nosotros usamos el vector con nombre «thermostat»:
En versiones previas a Android Lollipop el deslizador se camufla con transparencia en la pista de la SeekBar, pero para habilitar este efecto en versiones posteriores, usa android:splitTrack
en false
.