Codelab: Crea RROs con componentes car-ui-lib a través del sistema de compilación de Gradle

Usa la biblioteca car-ui-lib para iniciar infoentretenimiento autocoherente en el vehículo y con coherencia (IVI). En este codelab, se presenta car-ui-lib y cómo puedes Usar superposiciones de recursos en tiempo de ejecución (RRO) para personalizar componentes de la biblioteca

Qué aprenderás

Aprenderás a hacer lo siguiente:

  • Incluye componentes de car-ui-lib en tu app para Android.
  • Usa Gradle a fin de compilar apps para Android y RRO.
  • Usa RRO con car-ui-lib.

En este codelab, no se detalla el funcionamiento de las RRO. Consulta Cambiar el valor de los recursos de una app en el tiempo de ejecución Soluciona problemas de superposiciones de recursos de tiempo de ejecución para obtener más información.

Antes de comenzar

Requisitos previos

Antes de comenzar, asegúrate de tener lo siguiente:

Cómo crear una app para Android nueva

Duración: 15 minutos

En esta sección, crearás un nuevo proyecto de Android Studio.

  1. En Android Studio, crea una app con un EmptyActivity.

    Cómo crear una actividad vacía
    Figura 1: Crea una actividad vacía
  2. Asígnale el nombre CarUiCodelab a la app y, luego, selecciona el lenguaje Java. Puedes también selecciona una ubicación de archivo si lo deseas. Acepta los valores predeterminados de parámetros de configuración restantes.

     Asigna un nombre a tu app
    Figura 2: Asígnale un nombre a tu app
  3. Reemplaza activity_main.xml por el siguiente bloque de código:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/sample_text"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    Este bloque de código muestra la cadena sample_text, que no está definida.

  4. Agrega la cadena de recurso sample_text y establécela en “Hello World!”. en tu archivo strings.xml. Para abrir este archivo, selecciona aplicación > src > principal > res > valores > strings.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">CarUiCodelab</string>
        <string name="sample_text">Hello World!</string>
    </resources>
    
  5. Para compilar tu app, haz clic en el botón verde Play ubicado en la parte superior derecha. Hacerlo instala automáticamente el apk en tu emulador o dispositivo Android a través de Gradle

    Botón para reproducir

La app nueva debería abrirse automáticamente en el emulador o dispositivo Android. Si no, abre la app de CarUiCodelab desde el selector de aplicaciones, que ahora está instalado. Se ve así:

Abrir nueva app de CarUiCodelab
Figura 3: Abre la nueva app de CarUiCodelab

Agrega car-ui-lib a tu app para Android

Duración: 15 minutos

Agrega car-ui-lib a tu app:

  1. Para agregar la dependencia car-ui-lib al archivo build.gradle de tu proyecto, haz lo siguiente: selecciona Aplicación > build.gradle. Tus dependencias deberían aparecer así:

    dependencies {
        implementation 'com.android.car.ui:car-ui-lib:2.0.0'
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.4.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    }
    

Cómo usar componentes car-ui-lib en tu app para Android

Ahora que tienes car-ui-lib, agrega una barra de herramientas a tu app.

  1. En tu archivo MainActivity.java, reemplaza el método onCreate:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Get the toolbar controller instance.
        ToolbarController toolbar = CarUi.getToolbar(this);
        // Set the title on toolbar.
        toolbar.setTitle(getTitle());
        // Set the logo to be shown with the title.
        toolbar.setLogo(R.mipmap.ic_launcher_round);
    }
    
  2. Asegúrate de importar ToolbarController:

    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.toolbar.ToolbarController;
    
  3. Para usar el tema Theme.CarUi.WithToolbar, selecciona app > src > principal > AndroidManifest.xml y, luego, actualiza AndroidManifest.xml se muestre de la siguiente manera:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.caruicodelab">
    
        <application
            android:allowBackup="true"
            android:dataExtractionRules="@xml/data_extraction_rules"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.CarUi.WithToolbar"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
  4. Para compilar la app, presiona el botón verde Play, igual que antes.

    Compila la app

Agrega RRO a tu app

Duración: 30 minutos

Si ya conoces las RRO, ve a la siguiente sección, Agrega un controlador de permisos a tu app. De lo contrario, para conocer los conceptos básicos de las RRO, consulta Cambia el valor de los recursos de una app en el tiempo de ejecución.

Cómo agregar un controlador de permisos a tu app

Para controlar a qué recursos se superpone un paquete RRO, agrega un archivo llamado overlayable.xml a la carpeta /res de tu app. Este archivo funciona como un permiso entre tu app (el objetivo) y tu paquete de RRO (la superposición).

  1. Agrega res/values/overlayable.xml a tu app y copia el siguiente contenido en tu archivo:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <overlayable name="CarUiCodelab">
            <policy type="public">
                <item type="string" name="sample_text"/>
            </policy>
        </overlayable>
    </resources>
    

    Dado que una RRO debe poder superponer la cadena sample_text, incluye el elemento nombre del recurso en el archivo Overlayable.xml de la app.

    Tu archivo overlayable.xml DEBE residir en res/values/. Si no es así, el OverlayManagerService no puede localizarlo.

    Para obtener más información sobre los recursos superpuestos y cómo se pueden consulta Cómo restringir el contenido recursos.

Crea un paquete RRO

En esta sección, crearás un paquete RRO para cambiar la cadena que se muestra arriba. de "Hello World!" por “Hello World RRO”.

  1. Para crear un proyecto nuevo, selecciona Archivo > Nuevo > Nuevo proyecto. Asegúrate de selecciona No Activity en lugar de Empty Activity, ya que los paquetes de RRO contienen solo recursos.

    Tus configuraciones tienen un aspecto similar a las que se muestran a continuación. El la ubicación en la que se guardan puede diferir:

  2. Después de crear el proyecto CarUiRRO nuevo, decláralo como un Modifica AndroidManifest.xml para la RRO.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.caruirro">
    
        <application android:hasCode="false" />
    
        <uses-sdk
            android:minSdkVersion="29"
            android:targetSdkVersion="29"/>
    
        <overlay
            android:targetPackage="com.example.caruicodelab"
            android:targetName="CarUiCodelab"
            android:isStatic="false"
            android:resourcesMap="@xml/sample_overlay"
            />
    </manifest>
    

    Esto crea un error con @xml/sample_overlay. El resourcesMap asigna nombres de recursos del paquete de destino al paquete RRO.

  3. Copia el siguiente bloque de código en …/res/xml/sample_overlay.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
    </overlay>
    
  4. Agrega sample_text a …/res/values/strings.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">CarUiRRO</string>
        <string name="sample_text">Hello World RRO</string>
    </resources>
    
    Se creó una compilación de Gradle de RRO.
    .
  5. Para compilar tu objetivo de RRO, presiona el botón verde Play para crear un Gradle. compilación de tu RRO en tu emulador o dispositivo Android.

  6. Para verificar que la RRO esté instalada correctamente, ejecuta el siguiente comando:

    shell:~$ adb shell cmd overlay list --user current | grep -i com.example
    com.example.caruicodelab
    [ ] com.example.caruirro
    

    Este comando muestra información útil sobre el estado de los paquetes RRO en el sistema.

    • [ ] indica que la RRO está instalada y lista para activarse.
    • --- indica que la RRO está instalada, pero contiene errores.
    • [X] significa que la RRO está instalada y activada.

    Si tu RRO tiene errores, consulta Soluciona problemas de superposiciones de recursos de tiempo de ejecución antes de continuar.

  7. Para habilitar la RRO y verificar que esté habilitada, haz lo siguiente:

    shell:~$ adb shell cmd overlay enable --user current com.example.caruirro
    shell:~$ adb shell cmd overlay list --user current | grep -i com.example
    com.example.caruicodelab
    [x] com.example.caruirro
    

Tu app muestra la string “Hello World RRO”.

Hola mundo RRO:
Figura 4: Hello World RRO!

¡Felicitaciones! Creaste tu primera RRO.

Cuando uses RRO, te recomendamos que uses Android Asset Packaging Tool (AAPT2) las marcas --no-resource-deduping y --no-resource-removal, descritas en Opciones de vinculación. No es necesario agregar las marcas de este codelab, pero te sugerimos que las uses en tus RRO para evitar la eliminación de recursos (y problemas de depuración). Tú puedes agregarlas al archivo build.gradle de tu RRO de la siguiente manera:

android {
    …
    aaptOptions {
        additionalParameters "--no-resource-deduping", "--no-resource-removal"
    }
}

Para obtener más información sobre estas marcas, consulta Compila el paquete y AAPT2.

Modifica los componentes de car-ui-lib usando RRO en tu app para Android

En esta página, se describe cómo puedes usar una superposición de recursos de entorno de ejecución (RRO) para modificar componentes de la biblioteca car-ui-lib en tu app para Android

Cómo establecer el color de fondo de la barra de herramientas

Duración: 15 minutos

Para cambiar el color de fondo de la barra de herramientas:

  1. Agrega el siguiente valor a tu app de RRO y establece el recurso en Bright verde (#0F0):

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <drawable name="car_ui_toolbar_background">#0F0</drawable>
    </resources>
    

    La biblioteca car-ui-lib contiene un recurso llamado car_ui_toolbar_background Cuando este recurso esté contenido en el de un RRO, la barra de herramientas no cambia porque el valor está orientado.

  2. En el AndroidManifest.xml de tu RRO, actualiza targetName para que apunte a car-ui-lib:

    …
    android:targetName="car-ui-lib"
    …
    

    DEBES crear un paquete RRO nuevo para cada paquete de destino que desees RRO. Por ejemplo, al crear superposiciones para dos objetivos diferentes, debes crear dos APK superpuestos.

  3. Compila, verifica, instala y habilita la RRO de la misma manera que antes.

Tu app se ve de la siguiente manera:

Nuevo color de fondo de la barra de herramientas
Figura 5: Nuevo color de fondo de la barra de herramientas

Diseños y estilos de RRO

Duración: 15 minutos

En este ejercicio, compilarás una app nueva similar a la que creaste anteriormente. Esta permite superponer el diseño. Sigue los mismos pasos que antes o modifica tu app existente.

  1. Asegúrate de agregar las siguientes líneas a overlayable.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
      <overlayable name="CarUiCodelab">
        <policy type="public">
          <item type="string" name="sample_text"/>
          <item type="layout" name="activity_main"/>
          <item type="id" name="textView"/>
        </policy>
      </overlayable>
    </resources>
    
  2. Asegúrate de que activity_main.xml se muestre de la siguiente manera:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/sample_text"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  3. En tu app de RRO, crea un res/layout/activity_main.xml y agrega el lo siguiente:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
      <TextView
          android:id="@+id/textView"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/sample_text"
          android:textAppearance="@style/TextAppearance.CarUi"
          android:layout_gravity="center_vertical|center_horizontal"/>
    </FrameLayout>
    
  4. Actualiza res/values/styles.xml para agregar nuestro estilo a la RRO:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <style name="TextAppearance.CarUi" parent="android:TextAppearance.DeviceDefault">
            <item name="android:textColor">#0f0</item>
            <item name="android:textSize">100sp</item>
        </style>
    </resources>
    
  5. Cambia la targetName de AndroidManifest.xml para que apunte al nombre de tu nueva app:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. Agrega los recursos al archivo sample_overlay.xml en tu RRO:

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
        <item target="id/textView" value="@id/textView"/>
        <item target="layout/activity_main" value="@layout/activity_main"/>
    </overlay>
    
  7. Compila e instala la app y la RRO de la misma manera que antes (Play verde de color verde). ). Asegúrate de habilitar tu RRO.

La app y la RRO se renderizan de la siguiente manera. El texto de la RRO de Hello World es verde y es centrado, como se especifica en la RRO de diseño.

RRO de Hello World
Figura 6: RRO de Hello World

Cómo agregar CarUiRecyclerView a tu app

Duración: 15 minutos

La interfaz de CarUiRecyclerView proporciona APIs para acceder a un RecyclerView que se personaliza a través de los recursos de car-ui-lib. Por ejemplo, CarUiRecyclerView. Verifica una marca en el tiempo de ejecución para determinar si la barra de desplazamiento debe estar habilitada o no. y selecciona el diseño correspondiente.

CarUiRecyclerViewContainer
Figura 7: CarUiRecyclerViewContainer
  1. Para agregar un CarUiRecyclerView, agrégalo a tu activity_main.xml y MainActivity.java. Puedes crear una app nueva desde cero modificar la app existente. Si modificas la app existente, asegúrate de quitarla recursos no declarados en overlayable.xml.

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <com.android.car.ui.recyclerview.CarUiRecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    

    Es posible que aparezca el siguiente error, que puedes ignorar:

    Cannot resolve class com.android.car.ui.recyclerview.CarUiRecyclerView

    Siempre y cuando la clase esté escrita correctamente y hayas agregado car-ui-lib una dependencia, puedes crear y compilar tu APK. Para quitar el error, Selecciona Archivo > Invalida cachés y, luego, haz clic en Invalidar y reiniciar.

    Agrega lo siguiente a MainActivity.java

    package com.example.caruicodelab;
    
    import android.app.Activity;
    import android.os.Bundle;
    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.recyclerview.CarUiContentListItem;
    import com.android.car.ui.recyclerview.CarUiListItem;
    import com.android.car.ui.recyclerview.CarUiListItemAdapter;
    import com.android.car.ui.recyclerview.CarUiRecyclerView;
    import com.android.car.ui.toolbar.ToolbarController;
    import java.util.ArrayList;
    
    /** Activity with a simple car-ui layout. */
    public class MainActivity extends Activity {
    
        private final ArrayList<CarUiListItem> mData = new ArrayList<>();
        private CarUiListItemAdapter mAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ToolbarController toolbar = CarUi.getToolbar(this);
            toolbar.setTitle(getTitle());
            toolbar.setLogo(R.mipmap.ic_launcher_round);
    
            CarUiRecyclerView recyclerView = findViewById(R.id.list);
            mAdapter = new CarUiListItemAdapter(generateSampleData());
            recyclerView.setAdapter(mAdapter);
        }
    
        private ArrayList<CarUiListItem> generateSampleData() {
            for (int i = 0; i < 20; i++) {
                CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.ICON);
                item.setTitle("Title " + i);
                item.setPrimaryIconType(CarUiContentListItem.IconType.CONTENT);
                item.setIcon(getDrawable(R.drawable.ic_launcher_foreground));
                item.setBody("body " + i);
                mData.add(item);
            }
            return mData;
    }
    
  2. Compila e instala tu app como lo hiciste antes.

Ahora ves un CarUiRecyclerView:

CarUiRecyclerView
Figura 7 : CarUiRecyclerView

Cómo usar un RRO para quitar la barra de desplazamiento

Duración: 10 minutos

En este ejercicio, se muestra cómo usar una RRO para quitar la barra de desplazamiento de CarUiRecyclerView

  1. En tu RRO, agrega y modifica los siguientes archivos:

    AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.caruirro">
    
        <application android:hasCode="false" />
    
        <uses-sdk
            android:minSdkVersion="29"
            android:targetSdkVersion="29"/>
    
        <overlay
            android:targetPackage="com.example.caruicodelab"
            android:targetName="car-ui-lib"
            android:isStatic="false"
            android:resourcesMap="@xml/sample_overlay"
            />
    </manifest>
    

    res/values/bools.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="car_ui_scrollbar_enable">false</bool>
    </resources>
    

    El recurso car_ui_scrollbar_enable es un recurso booleano car-ui-lib, que controla si la barra de desplazamiento optimizó el vehículo con los botones Arriba y Abajo en CarUiRecyclerView está presente o no. Cuando se establece en false, CarUiRecyclerView funciona como un RecyclerView de AndroidX.

    res/xml/sample_overlay.xml

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="bool/car_ui_scrollbar_enable" value="@bool/car_ui_scrollbar_enable"/>
    </overlay>
    

Compila e instala tu app como lo hiciste antes. La barra de desplazamiento ya no está disponible CarUiRecyclerView:

CarUiRecyclerView sin barra de desplazamiento
Figura 8: CarUiRecyclerView sin barra de desplazamiento

Cómo usar un diseño para superponer la barra de desplazamiento de CarUiRecyclerView

Duración: 15 minutos

En este ejercicio, modificarás el diseño de la barra de desplazamiento CarUiRecyclerView.

  1. Agrega y modifica los siguientes archivos en tu app de RRO.

    res/layout/car_ui_recycler_view_scrollbar.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="112dp"
        android:layout_height="match_parent"
        android:id="@+id/car_ui_scroll_bar">
        <!-- View height is dynamically calculated during layout. -->
        <View
            android:id="@+id/car_ui_scrollbar_thumb"
            android:layout_width="6dp"
            android:layout_height="20dp"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/car_ui_recyclerview_scrollbar_thumb"/>
        <View
            android:id="@+id/car_ui_scrollbar_track"
            android:layout_width="10dp"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            android:layout_centerHorizontal="true"
            android:layout_above="@+id/car_ui_scrollbar_page_up"/>
        <View
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            android:background="#323232"
            android:layout_toLeftOf="@+id/car_ui_scrollbar_thumb"
            android:layout_above="@+id/car_ui_scrollbar_page_up"
            android:layout_marginRight="5dp"/>
        <View
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            android:background="#323232"
            android:layout_toRightOf="@+id/car_ui_scrollbar_thumb"
            android:layout_above="@+id/car_ui_scrollbar_page_up"
            android:layout_marginLeft="5dp"/>
        <ImageView
            android:id="@+id/car_ui_scrollbar_page_up"
            android:layout_width="75dp"
            android:layout_height="75dp"
            android:focusable="false"
            android:hapticFeedbackEnabled="false"
            android:src="@drawable/car_ui_recyclerview_ic_up"
            android:scaleType="centerInside"
            android:background="?android:attr/selectableItemBackgroundBorderless"
            android:layout_centerHorizontal="true"
            android:layout_above="@+id/car_ui_scrollbar_page_down"/>
        <ImageView
            android:id="@+id/car_ui_scrollbar_page_down"
            android:layout_width="75dp"
            android:layout_height="75dp"
            android:focusable="false"
            android:hapticFeedbackEnabled="false"
            android:src="@drawable/car_ui_recyclerview_ic_down"
            android:scaleType="centerInside"
            android:background="?android:attr/selectableItemBackgroundBorderless"
            android:layout_centerHorizontal="true"
            android:layout_alignParentBottom="true"/>
    </RelativeLayout>
    

    Para superponer un archivo de diseño, debes agregar todos los IDs y el espacio de nombres a la overlay.xml de tu RRO. Consulta los archivos que aparecen a continuación.

    res/xml/sample_overlay.xml

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
       <item target="drawable/car_ui_recyclerview_ic_down" value="@drawable/car_ui_recyclerview_ic_down"/>
       <item target="drawable/car_ui_recyclerview_ic_up" value="@drawable/car_ui_recyclerview_ic_up"/>
       <item target="drawable/car_ui_recyclerview_scrollbar_thumb" value="@drawable/car_ui_recyclerview_scrollbar_thumb"/>
       <item target="id/car_ui_scroll_bar" value="@id/car_ui_scroll_bar"/>
       <item target="id/car_ui_scrollbar_thumb" value="@id/car_ui_scrollbar_thumb"/>
       <item target="id/car_ui_scrollbar_track" value="@id/car_ui_scrollbar_track"/>
       <item target="id/car_ui_scrollbar_page_up" value="@id/car_ui_scrollbar_page_up"/>
       <item target="id/car_ui_scrollbar_page_down" value="@id/car_ui_scrollbar_page_down"/>
       <item target="layout/car_ui_recyclerview_scrollbar" value="@layout/car_ui_recyclerview_scrollbar"/>
    </overlay>
    

    res/drawable/car_ui_recyclerview_ic_up.xml

    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="48.0"
        android:viewportHeight="48.0">
        <path
            android:pathData="M14.83,30.83L24,21.66l9.17,9.17L36,28 24,16 12,28z"
            android:fillColor="#0000FF"/>
    </vector>
    

    res/drawable/car_ui_recyclerview_ic_down.xml

    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="48.0"
        android:viewportHeight="48.0">
        <path
            android:pathData="M14.83,16.42L24,25.59l9.17,-9.17L36,19.25l-12,12 -12,-12z"
            android:fillColor="#0000FF"/>
    </vector>
    

    res/drawable/car_ui_recyclerview_scrollbar_thumb.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <solid android:color="#0000FF" />
        <corners android:radius="100dp"/>
    </shape>
    

    Se sugiere examinar cómo interactúan estos archivos.

    Para simplificar, las dimensiones y los colores están codificados. Sin embargo, una mejor la práctica es declarar estos valores en dimens.xml y colors.xml, o designados como archivos de color en la carpeta res/color/. Para obtener más información, ver Estilo de código Java del AOSP para colaboradores.

  2. Compila e instala tu app como lo hiciste antes. Compilaste CarUiRecyclerView con una barra de desplazamiento azul y rieles grises.

¡Felicitaciones! Ambas flechas aparecen en la parte inferior de la barra de desplazamiento, aplicó correctamente un RRO a un archivo de recursos de diseño car-ui-lib con la compilación de Gradle del sistema con Android Studio.

CarUiRecyclerView con una barra de desplazamiento azul con rieles grises
Figura 9: CarUiRecyclerView con una barra de desplazamiento azul con rieles grises

Elementos de la lista de RRO

Duración: 15 minutos

Hasta ahora, aplicaste una RRO a los componentes de car-ui-lib usando un framework (no de AndroidX). Para usar componentes de AndroidX en una RRO, debes agregar las dependencias de ese componente a la app y a la RRO. build.gradle. También debes agregar el attrs de ese componente a overlayable.xml en tu app. como sample_overlay.xml en tu RRO.

Nuestra biblioteca (car-ui-lib) usa ConstraintLayout y otros formatos de AndroidX por lo que su overlayable.xml podría verse de la siguiente manera:

<?xml version='1.0' encoding='UTF-8'?>
<resources>
    <overlayable name="car-ui-lib">
        …
        <item type="attr" name="layout_constraintBottom_toBottomOf"/>
        <item type="attr" name="layout_constraintBottom_toTopOf"/>
        <item type="attr" name="layout_constraintCircle"/>
        <item type="attr" name="layout_constraintCircleAngle"/>
        <item type="attr" name="layout_constraintCircleRadius"/>
        <item type="attr" name="layout_constraintDimensionRatio"/>
        <item type="attr" name="layout_constraintEnd_toEndOf"/>
        <item type="attr" name="layout_constraintEnd_toStartOf"/>
        <item type="attr" name="layout_constraintGuide_begin"/>
        <item type="attr" name="layout_constraintGuide_end"/>
        <item type="attr" name="layout_constraintGuide_percent"/>
        <item type="attr" name="layout_constraintHeight_default"/>
        <item type="attr" name="layout_constraintHeight_max"/>
        <item type="attr" name="layout_constraintHeight_min"/>
        <item type="attr" name="layout_constraintHeight_percent"/>
        <item type="attr" name="layout_constraintHorizontal_bias"/>
        <item type="attr" name="layout_constraintHorizontal_chainStyle"/>
        <item type="attr" name="layout_constraintHorizontal_weight"/>
        <item type="attr" name="layout_constraintLeft_creator"/>
        <item type="attr" name="layout_constraintLeft_toLeftOf"/>
        <item type="attr" name="layout_constraintLeft_toRightOf"/>
        <item type="attr" name="layout_constraintRight_creator"/>
        <item type="attr" name="layout_constraintRight_toLeftOf"/>
        <item type="attr" name="layout_constraintRight_toRightOf"/>
        <item type="attr" name="layout_constraintStart_toEndOf"/>
        <item type="attr" name="layout_constraintStart_toStartOf"/>
        <item type="attr" name="layout_constraintTag"/>
        <item type="attr" name="layout_constraintTop_creator"/>
        <item type="attr" name="layout_constraintTop_toBottomOf"/>
        <item type="attr" name="layout_constraintTop_toTopOf"/>
        <item type="attr" name="layout_constraintVertical_bias"/>
        <item type="attr" name="layout_constraintVertical_chainStyle"/>
        …
    </overlayable>
</resources>
  1. Cambia el diseño de los elementos de lista en el CarUiRecyclerView con ConstraintLayout Agrega o modifica los siguientes archivos en tu RRO:

    res/xml/sample_overlay.xml

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
       <item target="id/car_ui_list_item_touch_interceptor" value="@id/car_ui_list_item_touch_interceptor"/>
       <item target="id/car_ui_list_item_reduced_touch_interceptor" value="@id/car_ui_list_item_reduced_touch_interceptor"/>
       <item target="id/car_ui_list_item_start_guideline" value="@id/car_ui_list_item_start_guideline"/>
       <item target="id/car_ui_list_item_icon_container" value="@id/car_ui_list_item_icon_container"/>
       <item target="id/car_ui_list_item_icon" value="@id/car_ui_list_item_icon"/>
       <item target="id/car_ui_list_item_content_icon" value="@id/car_ui_list_item_content_icon"/>
       <item target="id/car_ui_list_item_avatar_icon" value="@id/car_ui_list_item_avatar_icon"/>
       <item target="id/car_ui_list_item_title" value="@id/car_ui_list_item_title"/>
       <item target="id/car_ui_list_item_body" value="@id/car_ui_list_item_body"/>
       <item target="id/car_ui_list_item_action_container_touch_interceptor" value="@id/car_ui_list_item_action_container_touch_interceptor"/>
       <item target="id/car_ui_list_item_action_container" value="@id/car_ui_list_item_action_container"/>
       <item target="id/car_ui_list_item_action_divider" value="@id/car_ui_list_item_action_divider"/>
       <item target="id/car_ui_list_item_switch_widget" value="@id/car_ui_list_item_switch_widget"/>
       <item target="id/car_ui_list_item_checkbox_widget" value="@id/car_ui_list_item_checkbox_widget"/>
       <item target="id/car_ui_list_item_radio_button_widget" value="@id/car_ui_list_item_radio_button_widget"/>
       <item target="id/car_ui_list_item_supplemental_icon" value="@id/car_ui_list_item_supplemental_icon"/>
       <item target="id/car_ui_list_item_end_guideline" value="@id/car_ui_list_item_end_guideline"/>
       <item target="attr/layout_constraintBottom_toBottomOf" value="@attr/layout_constraintBottom_toBottomOf"/>
       <item target="attr/layout_constraintBottom_toTopOf" value="@attr/layout_constraintBottom_toTopOf"/>
       <item target="attr/layout_constraintEnd_toEndOf" value="@attr/layout_constraintEnd_toEndOf"/>
       <item target="attr/layout_constraintEnd_toStartOf" value="@attr/layout_constraintEnd_toStartOf"/>
       <item target="attr/layout_constraintGuide_begin" value="@attr/layout_constraintGuide_begin"/>
       <item target="attr/layout_constraintGuide_end" value="@attr/layout_constraintGuide_end"/>
       <item target="attr/layout_constraintHorizontal_bias" value="@attr/layout_constraintHorizontal_bias"/>
       <item target="attr/layout_constraintLeft_toLeftOf" value="@attr/layout_constraintLeft_toLeftOf"/>
       <item target="attr/layout_constraintLeft_toRightOf" value="@attr/layout_constraintLeft_toRightOf"/>
       <item target="attr/layout_constraintRight_toLeftOf" value="@attr/layout_constraintRight_toLeftOf"/>
       <item target="attr/layout_constraintRight_toRightOf" value="@attr/layout_constraintRight_toRightOf"/>
       <item target="attr/layout_constraintStart_toEndOf" value="@attr/layout_constraintStart_toEndOf"/>
       <item target="attr/layout_constraintStart_toStartOf" value="@attr/layout_constraintStart_toStartOf"/>
       <item target="attr/layout_constraintTop_toBottomOf" value="@attr/layout_constraintTop_toBottomOf"/>
       <item target="attr/layout_constraintTop_toTopOf" value="@attr/layout_constraintTop_toTopOf"/>
       <item target="attr/layout_goneMarginBottom" value="@attr/layout_goneMarginBottom"/>
       <item target="attr/layout_goneMarginEnd" value="@attr/layout_goneMarginEnd"/>
       <item target="attr/layout_goneMarginLeft" value="@attr/layout_goneMarginLeft"/>
       <item target="attr/layout_goneMarginRight" value="@attr/layout_goneMarginRight"/>
       <item target="attr/layout_goneMarginStart" value="@attr/layout_goneMarginStart"/>
       <item target="attr/layout_goneMarginTop" value="@attr/layout_goneMarginTop"/>
       <item target="attr/layout_constraintVertical_chainStyle" value="@attr/layout_constraintVertical_chainStyle"/>
       <item target="layout/car_ui_list_item" value="@layout/car_ui_list_item"/>
    </overlay>
    

    res/layout/car_ui_list_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:tag="carUiListItem"
        android:minHeight="@dimen/car_ui_list_item_height">
    
        <!-- The following touch interceptor views are sized to encompass the specific sub-sections of
        the list item view to easily control the bounds of a background ripple effects. -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <!-- This touch interceptor does not include the action container -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_reduced_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@id/car_ui_list_item_action_container"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/car_ui_list_item_start_guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="@dimen/car_ui_list_item_start_inset" />
    
        <FrameLayout
            android:id="@+id/car_ui_list_item_icon_container"
            android:layout_width="@dimen/car_ui_list_item_icon_container_width"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@+id/car_ui_list_item_start_guideline"
            app:layout_constraintTop_toTopOf="parent">
    
            <ImageView
                android:id="@+id/car_ui_list_item_icon"
                android:layout_width="@dimen/car_ui_list_item_icon_size"
                android:layout_height="@dimen/car_ui_list_item_icon_size"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_content_icon"
                android:layout_width="@dimen/car_ui_list_item_content_icon_width"
                android:layout_height="@dimen/car_ui_list_item_content_icon_height"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_avatar_icon"
                android:background="@drawable/car_ui_list_item_avatar_icon_outline"
                android:layout_width="@dimen/car_ui_list_item_avatar_icon_width"
                android:layout_height="@dimen/car_ui_list_item_avatar_icon_height"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
        </FrameLayout>
    
        <CarUiTextView
            android:id="@+id/car_ui_list_item_title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/car_ui_list_item_text_start_margin"
            android:singleLine="@bool/car_ui_list_item_single_line_title"
            android:textAppearance="@style/TextAppearance.CarUi.ListItem"
            android:layout_gravity="right"
            android:gravity="right"
            android:textAlignment="viewEnd"
            app:layout_constraintBottom_toTopOf="@+id/car_ui_list_item_body"
            app:layout_constraintEnd_toStartOf="@+id/car_ui_list_item_action_container"
            app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_chainStyle="packed"
            app:layout_goneMarginStart="@dimen/car_ui_list_item_text_no_icon_start_margin" />
        <CarUiTextView
            android:id="@+id/car_ui_list_item_body"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/car_ui_list_item_text_start_margin"
            android:textAppearance="@style/TextAppearance.CarUi.ListItem.Body"
            android:layout_gravity="right"
            android:gravity="right"
            android:textAlignment="viewEnd"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/car_ui_list_item_action_container"
            app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
            app:layout_constraintTop_toBottomOf="@+id/car_ui_list_item_title"
            app:layout_goneMarginStart="@dimen/car_ui_list_item_text_no_icon_start_margin" />
    
        <!-- This touch interceptor is sized and positioned to encompass the action container   -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_action_container_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="@id/car_ui_list_item_action_container"
            app:layout_constraintEnd_toEndOf="@id/car_ui_list_item_action_container"
            app:layout_constraintStart_toStartOf="@id/car_ui_list_item_action_container"
            app:layout_constraintTop_toTopOf="@id/car_ui_list_item_action_container" />
    
        <FrameLayout
            android:id="@+id/car_ui_list_item_action_container"
            android:layout_width="wrap_content"
            android:minWidth="@dimen/car_ui_list_item_icon_container_width"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@+id/car_ui_list_item_end_guideline"
            app:layout_constraintTop_toTopOf="parent">
    
            <View
                android:id="@+id/car_ui_list_item_action_divider"
                android:layout_width="@dimen/car_ui_list_item_action_divider_width"
                android:layout_height="@dimen/car_ui_list_item_action_divider_height"
                android:layout_gravity="start|center_vertical"
                android:background="@drawable/car_ui_list_item_divider" />
    
            <Switch
                android:id="@+id/car_ui_list_item_switch_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <CheckBox
                android:id="@+id/car_ui_list_item_checkbox_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <RadioButton
                android:id="@+id/car_ui_list_item_radio_button_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_supplemental_icon"
                android:layout_width="@dimen/car_ui_list_item_supplemental_icon_size"
                android:layout_height="@dimen/car_ui_list_item_supplemental_icon_size"
                android:layout_gravity="center"
                android:scaleType="fitCenter" />
        </FrameLayout>
    
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/car_ui_list_item_end_guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_end="@dimen/car_ui_list_item_end_inset" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. car_ui_list_item.xml hace referencia a varias referencias de varios componentes/ que no se incluyen como dependencias de la app. Estos son car-ui-lib recursos. Puedes solucionar este problema agregando car-ui-lib como una dependencia a tu app de RRO en app/build.gradle:

    dependencies {
        implementation 'com.android.car.ui:car-ui-lib:2.0.0'
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.4.0'
    }
    

El título y el cuerpo ahora están alineados a la derecha en lugar de a la izquierda.

Título y cuerpo alineados a la derecha
Figura 10: Título y cuerpo alineados a la derecha

Solo aplicamos un RRO a car-ui-lib con componentes de AndroidX (ConstraintLayout) cuando sus atributos estaban presentes en el car-ui-lib llamado overlayable.xml, así como la RRO sample_overlay.xml. Es hacer algo similar en tu propia aplicación. Simplemente agrega todas las attrs al overlayable.xml de tu app, de manera similar a car-ui-lib

Sin embargo, no es posible RRO una app que use componentes de AndroidX cuando la app tiene car-ui-lib como dependencia en su build.gradle (cuando la app usa car-ui-lib). Dado que las asignaciones de atributos ya se definieron en el archivo overlayable.xml de la biblioteca de car-ui-lib y los agregarás a tu overlayable.xml de la app con car-ui-lib como dependencia generaría una mergeDebugResources error como el siguiente. Esto se debe a que estos atributos están presentes en varios archivos overlayable.xml:

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugResources'