El View Binding en el nuevo sistema de vinculación de vistas con el que trabajemos a partir de ahora y en este capítulo aprenderás a usarlo.

Puede que si te hablo del view binding o la vinculación de vistas no sepas exactamente a lo que me refiero ¿Verdad? Pero aunque no lo tengas claro trabajas con las vinculaciones en cada proyecto. Imagínate que tienes que hacer un setOnClickListener de un button, en el momento que accedes a dicho botón desde la activity estás vinculando la vista.

Vinculación de vistas

A día de hoy tenemos tres tipos distintos de vinculación distintos.

FindViewById

Esta es la primera forma de vincular las vistas, es por decirlo de algún modo la forma original y la hemos usado al principio del curso.

Por ejemplo supongamos que tenemos un botón con una id llamada btnToast, para acceder a él desde la vista haríamos

   val button = findViewById<Button>(R.id.btnToast)
        button.setOnClickListener { 
            Toast.makeText(this, "Toast!", Toast.LENGTH_SHORT).show() 
        }

Esto ha funcionado y funcionará siempre, pero si te fijas toda la parte del findViewById es un código que tendremos que repetir por cada uno de los componentes que queramos usar de la vista, generando un montón de código innecesario que no aporta nada, comúnmente llamado boilerplate.

Synthetic

Shyntetic sale de la librería de Android Extensions y nos permite acceder a las vistas simplemente nombrando la id. Simulando el caso anterior, nuestro código quedaría así

    btnToast.setOnClickListener {
            Toast.makeText(this, "Toast!", Toast.LENGTH_SHORT).show()
        }

Esta es posiblemente la solución que menos código aporta, pero también trae problemas. Para que esto funcione, la primera vez que añadimos una dependencia hay que importar synthetic, si por ejemplo estoy en el MainActivity importaría

import kotlinx.android.synthetic.main.activity_main.*

Hasta aquí todo parece perfecto, pero imagínate que en otra activity tengo otro btnToast e importo este sin querer, al pulsar el botón la aplicación reventaría. Esto pasa porque nos permite importar otras vistas que no son la de la activity y al no tener un control sobre los null la aplicación falla.

Es por ello que han deprecado synthetics. Cuando algo es deprecado, significa que no te recomiendan usarlo y que dentro de algunas versiones lo quitarán. Obviamente no va a ser de un día para otro (suelen tardas meses o años) pero tenemos que estar preparados y a la vanguardia y es por eso que a partir de ahora usaremos view binding.

View Binding

A día de hoy es la forma más recomendada porque es la más similar a shyntetic pero soluciona los problemas que esta tenía.

Para configurar view binding deberemos ir a build.gradle (app) y buscaremos la sección android{}, dentro de esta, en la parte inferior debemos añadir

buildFeatures{
        viewBinding = true
    }

O si usas una versión inferior a Android Studio 4 debes añadir

 viewBinding {
        enabled = true
   }

Para ver la versión que tienes en Android Studio es tan sencillo como ir al menú superior Android Studio > About Android Studio.

Información android studio
Información Android Studio.

Ahora pulsamos el botón de sincronizar que nos aparecerá y ya tenemos view binding configurado.

View Binding en Activity

Lo primero que tenemos que saber es que una vez activado el view binding, cada activity que creemos, creará por detrás el binding, es decir, si nosotros tenemos MainActivity que tiene un layout llamado activity_main.xml, automáticamente se creará ActivityMainBinding.

Entonces crearemos una variable de clase con lateinit y que sea de tipo ActivityMainBinding.

    private lateinit var binding: ActivityMainBinding

Esta variable la inicializaremos en el método onCreate() de nuestra activity. Después solo tendremos que cambiar la función setContentView (que ahora mismo mandamos el layout) y le ponemos la ruta de nuestro binding.

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }

Si te preguntas como ahora sabe la activity cual es el layout, pues lo recupera de nuestro ActivityMainBinding.

Ahora ya podríamos acceder a nuestros componentes simplemente usando binding.LaIdDelComponente.

    binding.btnToast.setOnClickListener {
            Toast.makeText(this, "Toast!", Toast.LENGTH_SHORT).show()
        }

View Binding en Fragment

Para usar view binding en fragments es algo más complicado pero super sencillo cuando lo entiendas. Imaginemos que tenemos un fragment normal

class BlankFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_blank, container, false)
    }
}

Este sería un fragment habitual, simplemente infla la vista y no hace absolutamente nada más. Luego si quisiéramos trabajar con él accederíamos a las vistas en el método onViewCreated().

Este es un buen momento para recordarte que si no has leído mi artículo de fragments, es el momento adecuado.

Ahora debemos hacer algo similar a la activity

class ExampleFragment : Fragment() {
    private var _binding:FragmentExampleBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentExampleBinding.inflate(inflater, container, false)
        return binding.root
    }

}

Posiblemente te parezca un poco loco así que vamos por partes. Lo primero que hago en la línea 2 es crear una variable _binding pero en esta ocasión no puedo usar el lateInit, por lo que tengo que ponerle la interrogación al final e inicializarla en null. Null es básicamente un valor nulo, es decir, que no contiene ningún valor.

Luego en la línea 3 lo único que hago es llamar al _binding!!, esas dos exclamaciones lo que me dicen es que mi valor puede ser null (por ponerle interrogación), pero si antes era null ¿Porqué ahora no lo va a ser? Pues porque si te fijas en la línea 9, a _binding le estoy asignando el valor del FragmentExampleBinding. Es por ello que siempre que llamemos al binding (sin el barra baja) nos devolverá la vinculación con el layout.

Ahora simplemente en nuestra función onViewCreated(), podemos acceder al layout.

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.btnToast.setOnClickListener { Toast.makeText(activity, "click", Toast.LENGTH_SHORT).show() }
    }

El fragment completo quedaría así

class ExampleFragment : Fragment() {
    private var _binding:FragmentExampleBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentExampleBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.btnToast.setOnClickListener { Toast.makeText(activity, "click", Toast.LENGTH_SHORT).show() }
    }
}

View Binding en RecyclerView (adapter)

Para usar view binding en un recyclerView es super sencillo. Lo primero que tendrás que hacer será echarle un ojo al capítulo 15 si todavía no sabes usar recyclerView. Si por el contrario ya eres un experto esto te será muy sencillo.

Imaginemos que tenemos creado un adapter y el layout de cada celda se llama item_superhero_list, pues para acceder a nuestras vistas solo debemos ir al ViewHolder (donde pintamos los items) y crearemos una variable binding de tipo ItemSuperHeroListBinding.

 class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val binding = ItemSuperheroListBinding.bind(view)
    }

Fíjate que la clase ItemSuperHeroListBinding recibe la view que nos da el ViewHolder.

Ahora para usar los componentes es tan sencillo como siempre, en la función donde vayamos a utilizar los componentes llamamos a binding.idDelComponente

  class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val binding = ItemSuperheroListBinding.bind(view)

        fun bind(superhero:String){
            binding.tvSuperHero.text = superhero
        }
    }

Espero que este capítulo te haya servido de utilidad. También te recuerdo que tienes el capítulo en vídeo por si prefieres verlo de una forma más interactiva.

Continúa con el curso: Capítulo 30 – Google Maps en Kotlin (Segunda parte)


Te recuerdo que puedes seguirme en mis redes sociales en Aristi.Dev. Y si tienes dudas con este o cualquier otro artículo del blog únete al Discord de la comunidad y te ayudaremos.