SeekBar En Android

La SeekBar En Android es una subclase de la ProgressBar que extiende sus funcionalidades para agregar un control deslizante en la pista del indicador.

Aspecto de SeekBar en Android

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 XMLDescripción
android:thumbEs el drawable para proyectar al deslizador (pulgar o perilla)
android:thumbTintTinte que se aplica a thumb
android:tickMarkTintTinte 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:

Ejemplo SeekBar

Para añadir una SeekBar en Android Studio ve a Palette>Widgets>SeekBar y arrastra el componente al lienzo o al Component Tree:

SeekBar en Android Studio

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:

SeekBar discreta en Android Studio

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:

Ejemplo de SeekBar en Android cambiando alpha de TextView
Ejemplo de SeekBar en Android cambiando temperaturas

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ámetro fromUser para determinar si lo provocó el usuario.
  • onStartTrackingTouch(): Notifica que el usuario comenzó un gesto táctil sobre la SeekBar
  • onStopTrackingTouch(): 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:

Cambiar color de thumb en SeekBar

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:

Personalizar thumb de SeekBar

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»:

Icono termostato de Android

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.

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!