Vinculación de vistas   Parte de Android Jetpack.

La vinculación de vistas es una función que facilita la escritura de código que interactúa con las vistas. Una vez que se habilita la vinculación de vistas en un módulo, se genera una clase de vinculación para cada archivo de diseño XML presente en ese módulo. Una instancia de una clase de vinculación contiene referencias directas a todas las vistas que tienen un ID en el diseño correspondiente.

En la mayoría de los casos, la vinculación de vistas reemplaza a findViewById.

Configuración

La vinculación de vistas se habilita módulo por módulo. Para habilitar la vinculación de vistas en un módulo, establece la opción de compilación viewBinding en true en el archivo build.gradle a nivel del módulo, como se muestra en el siguiente ejemplo:

Groovy

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

Kotlin

android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

Si deseas que se ignore un archivo de diseño mientras se generan clases de vinculación, agrega el atributo tools:viewBindingIgnore="true" a la vista raíz de ese archivo de diseño:

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

Uso

Si la vinculación de vistas está habilitada para un módulo, se genera una clase de vinculación para cada archivo de diseño XML que contiene el módulo. Cada clase de vinculación contiene referencias a la vista raíz y a todas las vistas que tienen un ID. El nombre de la clase de vinculación se genera convirtiendo el nombre del archivo en formato XML según la convención letra mayúscula inicial (pascal case), y agregando la palabra "Binding" al final.

Por ejemplo, considera un archivo de diseño llamado result_profile.xml que contiene lo siguiente:

<LinearLayout ... >
    <TextView android:id="@+id/name" />
    <ImageView android:cropToPadding="true" />
    <Button android:id="@+id/button"
        android:background="@drawable/rounded_button" />
</LinearLayout>

La clase de vinculación generada se llama ResultProfileBinding. Esta clase tiene dos campos: un TextView llamado name y un Button llamado button. El elemento ImageView del diseño no tiene ID, por lo que no hay referencia a él en la clase de vinculación.

Cada clase de vinculación también incluye un método getRoot(), que proporciona una referencia directa para la vista raíz del archivo de diseño correspondiente. En este ejemplo, el método getRoot() de la clase ResultProfileBinding devuelve la vista raíz LinearLayout.

En las siguientes secciones, se muestra el uso de clases de vinculación generadas en actividades y fragmentos.

Cómo usar la vinculación de vista en actividades

Para configurar una instancia de la clase de vinculación que se usará con una actividad, sigue estos pasos en el método onCreate() de la actividad:

  1. Llama al método inflate() estático incluido en la clase de vinculación generada. Esto crea una instancia de la clase de vinculación para la actividad que se usará.
  2. Llama al método getRoot() o usa la sintaxis de propiedades de Kotlin para obtener una referencia a la vista raíz.
  3. Pasa la vista raíz a setContentView() para que sea la vista activa en la pantalla.

Estos pasos se muestran en el siguiente ejemplo:

Kotlin

private lateinit var binding: ResultProfileBinding

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

Java

private ResultProfileBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = ResultProfileBinding.inflate(getLayoutInflater());
    View view = binding.getRoot();
    setContentView(view);
}

Ahora puedes usar la instancia de la clase de vinculación para hacer referencia a cualquiera de las vistas:

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

Cómo usar la vinculación de vista en fragmentos

Para configurar una instancia de la clase de vinculación que se usará con un fragmento, sigue estos pasos en el método onCreateView() del fragmento:

  1. Llama al método inflate() estático incluido en la clase de vinculación generada. Esto crea una instancia de la clase de vinculación para que la use el fragmento.
  2. Para obtener una referencia a la vista raíz, llama al método getRoot() o usa la sintaxis de la propiedad de Kotlin.
  3. Muestra la vista raíz desde el método onCreateView() para que sea la vista activa en la pantalla.

Kotlin

private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!

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

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

Java

private ResultProfileBinding binding;

@Override
public View onCreateView (LayoutInflater inflater,
                          ViewGroup container,
                          Bundle savedInstanceState) {
    binding = ResultProfileBinding.inflate(inflater, container, false);
    View view = binding.getRoot();
    return view;
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}

Ahora puedes usar la instancia de la clase de vinculación para hacer referencia a cualquiera de las vistas:

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

Proporciona sugerencias para diferentes configuraciones

Cuando declaras vistas en varias configuraciones, en ocasiones, tiene sentido usar un tipo de vista diferente según el diseño en particular. En el siguiente fragmento de código, se muestra un ejemplo de esto:

# in res/layout/example.xml

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" />

En este caso, es posible que esperes que la clase generada exponga un campo userBio de tipo TextView, ya que TextView es la clase base común. Debido a limitaciones técnicas, el generador de código de vinculación de vistas no puede determinar esto y, en su lugar, genera un campo View. Esto requiere que se convierta el campo más adelante con binding.userBio as TextView.

Para solucionar esta limitación, la vinculación de vistas admite un atributo tools:viewBindingType, que te permite indicarle al compilador qué tipo usar en el código generado. En el ejemplo anterior, puedes usar este atributo para que el compilador genere el campo como un TextView:

# in res/layout/example.xml (unchanged)

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" tools:viewBindingType="TextView" />

En otro ejemplo, supongamos que tienes dos diseños, uno que contiene un BottomNavigationView y otro que contiene un NavigationRailView. Ambas clases extienden NavigationBarView, que contiene la mayoría de los detalles de implementación. Si tu código no necesita saber exactamente qué subclase está presente en el diseño actual, puedes usar tools:viewBindingType para establecer el tipo generado en NavigationBarView en ambos diseños:

# in res/layout/navigation_example.xml

<BottomNavigationView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

# in res/layout-w720/navigation_example.xml

<NavigationRailView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

La vinculación de vistas no puede validar el valor de este atributo cuando genera código. Para evitar errores de tiempo de compilación y de ejecución, el valor debe cumplir con las siguientes condiciones:

  • El valor debe ser una clase que herede de android.view.View.
  • El valor debe ser una superclase de la etiqueta en la que se coloca. Por ejemplo, los siguientes valores no funcionan:

      <TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView. -->
      <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView. -->
    
  • El tipo final debe resolverse de manera coherente en todas las configuraciones.

Diferencias de findViewById

La vinculación de vistas tiene ventajas importantes frente al uso de findViewById:

  • Seguridad de nulos: Como la vinculación de vistas crea referencias directas a las vistas, no hay riesgo de que se produzca una excepción de puntero nulo debido a un ID de vista no válido. Además, cuando una vista solo está presente en algunas configuraciones de un diseño, el campo que contiene su referencia en la clase de vinculación se marca con @Nullable.
  • Seguridad de tipos: Los campos de cada clase de vinculación tienen tipos que coinciden con las vistas a las que hacen referencia en el archivo en formato XML. Esto significa que no hay riesgo de que se produzca una excepción de conversión de clase.

Estas diferencias implican que las incompatibilidades entre tu diseño y tu código harán que la compilación falle en el tiempo de compilación, en lugar de en el tiempo de ejecución.

Comparación con la vinculación de datos

Tanto la vinculación de vistas como la vinculación de datos generan clases de vinculación que puedes usar para hacer referencia a las vistas directamente. Sin embargo, la vinculación de vistas está diseñada para controlar casos de uso más simples y proporciona los siguientes beneficios en comparación con la vinculación de datos:

  • Compilación más rápida: La vinculación de vistas no requiere procesamiento de anotaciones, por lo que los tiempos de compilación son más rápidos.
  • Facilidad de uso: La vinculación de vistas no requiere archivos de diseño XML con etiquetas especiales, por lo que es más rápida de adoptar en tus apps. Una vez que habilitas la vinculación de vistas en un módulo, se aplica automáticamente a todos los diseños de ese módulo.

Por otro lado, la vinculación de vistas tiene las siguientes limitaciones en comparación con la vinculación de datos:

Debido a estas consideraciones, en algunos casos, es mejor usar la vinculación de vistas y la vinculación de datos en un proyecto. Puedes usar la vinculación de datos en los diseños que requieren funciones avanzadas y la vinculación de vista en los que no.

Recursos adicionales

Para obtener más información sobre la vinculación de vistas, consulta los siguientes recursos adicionales:

Blogs

Videos