contentDescription en XML

<!-- ImageView informativa — siempre con descripción -->
<ImageView
    android:contentDescription="@string/foto_perfil_desc"
    android:src="@drawable/foto_perfil" />

<!-- ImageView decorativa — vaciar la descripción -->
<ImageView
    android:contentDescription="@null"
    android:src="@drawable/fondo_decorativo"
    android:importantForAccessibility="no" />

<!-- ImageButton siempre necesita descripción -->
<ImageButton
    android:contentDescription="@string/btn_buscar"
    android:src="@drawable/ic_search" />

<!-- Desde código — cuando la descripción es dinámica -->
// En el Fragment/Activity:
binding.imagenProducto.contentDescription =
    "Foto del producto ${producto.nombre}"

// Para actualizar cuando cambia el estado:
binding.btnFavorito.contentDescription = if (esFavorito)
    getString(R.string.quitar_favorito, producto.nombre)
else
    getString(R.string.agregar_favorito, producto.nombre)

importantForAccessibility

<!-- Valores posibles: -->
<!-- auto (default): Android decide según el tipo de View -->
<!-- yes: siempre visible para accesibilidad -->
<!-- no: invisible para accesibilidad -->
<!-- noHideDescendants: ni la View ni sus hijos son visibles -->

<!-- Ocultar elemento decorativo completamente -->
<View
    android:importantForAccessibility="no"
    android:background="@drawable/separador_decorativo" />

<!-- Ocultar un contenedor y todo su contenido -->
<LinearLayout
    android:importantForAccessibility="noHideDescendants">
    <!-- Todo lo de aquí adentro es invisible para TalkBack -->
</LinearLayout>
// Desde código — útil cuando el estado cambia:
binding.panelCarga.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS

// Cuando el contenido está listo:
binding.panelCarga.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_AUTO

labelFor — asociar etiquetas con campos

<!-- labelFor asocia un TextView como etiqueta de un campo de entrada -->
<!-- TalkBack anuncia la etiqueta cuando el usuario enfoca el campo -->

<TextView
    android:id="@+id/lbl_email"
    android:text="Correo electrónico"
    android:labelFor="@id/et_email" />

<EditText
    android:id="@+id/et_email"
    android:inputType="textEmailAddress"
    android:hint="[email protected]" />
<!-- TalkBack anuncia: "Correo electrónico. [email protected]. Campo de texto. -->

<!-- Para TextInputLayout (Material) — la etiqueta ya está integrada -->
<com.google.android.material.textfield.TextInputLayout
    android:hint="Correo electrónico">
    <com.google.android.material.textfield.TextInputEditText
        android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<!-- TextInputLayout maneja la accesibilidad automáticamente -->

ViewGroups — agrupar para TalkBack

<!-- focusable en el contenedor agrupa todos los elementos para TalkBack -->
<LinearLayout
    android:focusable="true"
    android:contentDescription="Carlos Pensa, Desarrollador Android">

    <ImageView
        android:contentDescription="@null"
        android:importantForAccessibility="no"
        android:src="@drawable/avatar" />

    <LinearLayout android:importantForAccessibility="noHideDescendants">
        <TextView android:text="Carlos Pensa" />
        <TextView android:text="Desarrollador Android" />
    </LinearLayout>

</LinearLayout>
<!-- TalkBack enfoca el LinearLayout externo y lee la descripción del grupo -->

ViewCompat.setAccessibilityDelegate — comportamiento custom

// accessibilityDelegate permite definir comportamiento complejo de accesibilidad
// sin subclasear la View

ViewCompat.setAccessibilityDelegate(binding.cardProducto,
    object : AccessibilityDelegateCompat() {
        override fun onInitializeAccessibilityNodeInfo(
            host: View,
            info: AccessibilityNodeInfoCompat
        ) {
            super.onInitializeAccessibilityNodeInfo(host, info)

            // Agregar acciones custom
            info.addAction(
                AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                    AccessibilityNodeInfoCompat.ACTION_CLICK,
                    "Ver detalles del producto"
                )
            )

            // Establecer el role
            info.roleDescription = "Card de producto"

            // Estado
            info.stateDescription = if (producto.disponible)
                "Disponible" else "Sin stock"
        }

        override fun performAccessibilityAction(
            host: View,
            action: Int,
            args: Bundle?
        ): Boolean {
            if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
                navegarADetalle(producto)
                return true
            }
            return super.performAccessibilityAction(host, action, args)
        }
    }
)

RecyclerView accesible

// El ViewHolder debe tener semántica completa
class ProductoViewHolder(binding: ItemProductoBinding) :
    RecyclerView.ViewHolder(binding.root) {

    fun bind(producto: Producto) {
        binding.apply {
            tvNombre.text = producto.nombre
            tvPrecio.text = "$${producto.precio}"
            ivFoto.contentDescription = "Foto de ${producto.nombre}"

            // La card completa debería ser navegable como una unidad
            root.contentDescription =
                "${producto.nombre}, $${producto.precio}" +
                if (!producto.disponible) ", Sin stock" else ""

            // Marcar que la card es clickable (TalkBack lo anunciará)
            root.isClickable = true
            root.isFocusable = true

            btnAgregarCarrito.contentDescription =
                "Agregar ${producto.nombre} al carrito"
        }
    }
}

// Para listas muy largas — anunciar la posición:
// "Elemento 1 de 50"
override fun onBindViewHolder(holder: ProductoViewHolder, position: Int) {
    val producto = productos[position]
    holder.bind(producto)
    ViewCompat.setAccessibilityDelegate(holder.itemView,
        object : AccessibilityDelegateCompat() {
            override fun onInitializeAccessibilityNodeInfo(
                host: View, info: AccessibilityNodeInfoCompat
            ) {
                super.onInitializeAccessibilityNodeInfo(host, info)
                info.collectionItemInfo =
                    AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
                        position, 1, 0, 1, false
                    )
            }
        }
    )
}