Codelab: cree RRO con componentes car-ui-lib utilizando el sistema de compilación Gradle

Utilice la biblioteca car-ui-lib para iniciar sistemas de información y entretenimiento (IVI) autoconsistentes en el vehículo. Este codelab le presenta car-ui-lib y cómo puede utilizar superposiciones de recursos en tiempo de ejecución (RRO) para personalizar los componentes de la biblioteca.

lo que aprenderás

Cómo:

  • Incluya componentes car-ui-lib en su aplicación de Android.
  • Utilice Gradle para crear aplicaciones y RRO de Android.
  • Utilice RRO con car-ui-lib .

Este codelab no detalla cómo funcionan los RRO. Consulte Cambiar el valor de los recursos de una aplicación en tiempo de ejecución y Solucionar problemas de superposiciones de recursos en tiempo de ejecución para obtener más información.

Antes de que empieces

Requisitos previos

Antes de comenzar, asegúrese de tener:

Crear una nueva aplicación de Android

Duración: 15 minutos

En esta sección, crea un nuevo proyecto de Android Studio.

  1. En Android Studio, cree una aplicación con una EmptyActivity .

    Crear una actividad vacía
    Figura 1. Crear una actividad vacía
  2. Nombra la aplicación CarUiCodelab y luego selecciona el lenguaje Java. También puede seleccionar una ubicación de archivo si lo desea. Acepte los valores predeterminados para las configuraciones restantes.

    Nombra tu aplicación
    Figura 2. Asigne un nombre a su aplicación
  3. Reemplace activity_main.xml con 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. Agregue la cadena de recurso sample_text y configúrela en "¡Hola mundo!" en su archivo strings.xml . Para abrir este archivo, seleccione 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 crear su aplicación, haga clic en el botón verde Reproducir en la parte superior derecha. Al hacerlo, se instala automáticamente el apk en su emulador o dispositivo Android a través de Gradle.

    Botón de play

La nueva aplicación debería abrirse automáticamente en su emulador o dispositivo Android. De lo contrario, abra la aplicación CarUiCodelab desde el iniciador de aplicaciones, que ahora está instalado. Aparece así:

Abra la nueva aplicación CarUiCodelab
Figura 3. Abra la nueva aplicación CarUiCodelab

Añade car-ui-lib a tu aplicación de Android

Duración: 15 minutos

Agregue car-ui-lib a su aplicación:

  1. Para agregar la dependencia car-ui-lib al archivo build.gradle de su proyecto, seleccione 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'
    }
    

Utilice componentes car-ui-lib en su aplicación de Android

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

  1. En su archivo MainActivity.java , sobrescriba 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úrese 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 , seleccione aplicación > src > principal > AndroidManifest.xml y luego actualice AndroidManifest.xml para que aparezca 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 crear la aplicación, presione el botón verde Reproducir como antes.

    Construye la aplicación

Agregue RRO a su aplicación

Duración: 30 minutos

Si está familiarizado con los RRO, vaya a la siguiente sección, Agregar un controlador de permisos a su aplicación . De lo contrario, para conocer los conceptos básicos de las RRO, consulte Cambiar el valor de los recursos de una aplicación en tiempo de ejecución .

Agregue un controlador de permisos a su aplicación

Para controlar qué recursos superpone un paquete RRO, agregue un archivo llamado overlayable.xml a la carpeta /res de su aplicación. Este archivo sirve como controlador de permisos entre su aplicación (el destino ) y su paquete RRO (la superposición ).

  1. Agregue res/values/overlayable.xml a su aplicación y copie el siguiente contenido en su 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 un RRO debe poder superponer la cadena sample_text , incluya el nombre del recurso en el archivo overlayable.xml de la aplicación.

    Su archivo overlayable.xml DEBE residir en res/values/ . De lo contrario, OverlayManagerService no puede localizarlo.

    Para obtener más información sobre los recursos superpuestos y cómo se pueden configurar, consulte Restringir recursos superpuestos .

Crear un paquete RRO

En esta sección, creará un paquete RRO para cambiar la cadena que se muestra arriba de "¡Hola mundo!" a "Hola mundo RRO".

  1. Para crear un nuevo proyecto, seleccione Archivo > Nuevo > Nuevo proyecto . Asegúrese de seleccionar Sin actividad en lugar de Actividad vacía, ya que los paquetes RRO solo contienen recursos.

    Sus configuraciones son similares a las que se ilustran a continuación. La ubicación en la que se guardan puede diferir:

  2. Después de crear el nuevo proyecto CarUiRRO , declara el proyecto como RRO modificando 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="CarUiCodelab"
            android:isStatic="false"
            android:resourcesMap="@xml/sample_overlay"
            />
    </manifest>
    

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

  3. Copie 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. Agregue 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>
    
    Creó la compilación Gradle de RRO
  5. Para crear su objetivo RRO, presione el botón verde Reproducir para crear una compilación Gradle de su RRO en su emulador o dispositivo Android.

  6. Para verificar que su RRO esté instalado correctamente, ejecute:

    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 el RRO está instalado y listo para ser activado.
    • --- indica que el RRO está instalado pero contiene errores.
    • [X] significa que el RRO está instalado y activado.

    Si su RRO contiene errores, consulte Solucionar problemas de superposiciones de recursos de tiempo de ejecución antes de continuar.

  7. Para habilitar el RRO y verificar que esté habilitado:

    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
    

Su aplicación muestra la cadena "Hello World RRO".

¡Hola Mundo RRO!
Figura 4 : ¡Hola mundo RRO!

¡Felicidades! Ha creado su primer RRO.

Cuando utilice RRO, es posible que desee utilizar los indicadores de la herramienta de empaquetado de activos de Android (AAPT2) --no-resource-deduping y --no-resource-removal que se describen en Opciones de enlace . No es necesario agregar las banderas en este codelab, pero le sugerimos que las use en sus RRO para evitar la eliminación de recursos (y dolores de cabeza de depuración). Puedes agregarlos al archivo build.gradle de tu RRO de esta manera:

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

Para obtener más información sobre estas marcas, consulte Compilación del paquete y AAPT2 .

Modifique los componentes car-ui-lib usando RRO en su aplicación de Android

Esta página describe cómo puede utilizar una superposición de recursos de tiempo de ejecución (RRO) para modificar componentes de la biblioteca car-ui-lib en su aplicación de Android.

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. Agregue el siguiente valor a su aplicación RRO y configure el recurso en verde brillante ( #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 la configuración de un RRO, la barra de herramientas no cambia porque se apunta al valor incorrecto.

  2. En AndroidManifest.xml de su RRO, actualice targetName para que apunte a car-ui-lib :

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

    DEBE crear un nuevo paquete RRO para cada paquete de destino que desee RRO. Por ejemplo, al crear superposiciones para dos objetivos diferentes, debes crear dos aplicaciones de superposición.

  3. Cree, verifique, instale y habilite el RRO de la misma manera que antes.

Su aplicación se ve así:

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, creará una nueva aplicación similar a la aplicación que creó anteriormente. Esta aplicación permite superponer el diseño. Siga los mismos pasos que antes o modifique su aplicación existente.

  1. Asegúrese 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úrese de que activity_main.xml aparezca 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 su aplicación RRO, cree un res/layout/activity_main.xml y agregue 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. Actualice res/values/styles.xml para agregar nuestro estilo al 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. Cambie el targetName en AndroidManifest.xml para que apunte al nombre de su nueva aplicación:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. Agregue los recursos al archivo sample_overlay.xml en su 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. Cree e instale la aplicación y RRO de la misma manera que antes (botón verde Reproducir ). Asegúrese de habilitar su RRO.

La aplicación y RRO se representan de la siguiente manera. El texto Hello World RRO es verde y está centrado como se especifica en el diseño RRO.

Hola mundo RRO
Figura 6 : Hola mundo RRO

Agregue CarUiRecyclerView a su aplicación

Duración: 15 minutos

La interfaz CarUiRecyclerView proporciona API para acceder a un RecyclerView personalizado a través de recursos car-ui-lib . Por ejemplo, CarUiRecyclerView verifica una bandera en tiempo de ejecución para determinar si la barra de desplazamiento debe habilitarse o no y selecciona el diseño correspondiente.

CarUiRecyclerVerContenedor
Figura 7. Contenedor CarUiRecyclerView
  1. Para agregar un CarUiRecyclerView , agréguelo a sus archivos activity_main.xml y MainActivity.java . Puede crear una nueva aplicación desde cero o modificar la aplicación existente. Si modifica la aplicación existente, asegúrese de eliminar los recursos no declarados de 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"/>
    

    Puede aparecer el siguiente error, que puedes ignorar:

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

    Siempre que su clase esté escrita correctamente y haya agregado car-ui-lib como dependencia, puede crear y compilar su apk. Para eliminar el error, seleccione Archivo > Invalidar cachés y luego haga clic en Invalidar y reiniciar.

    Agregue 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. Cree e instale su aplicación como antes.

Ahora verá un CarUiRecyclerView :

CocheUiRecyclerVer
Figura 7 : CarUiRecyclerView

Utilice un RRO para eliminar la barra de desplazamiento

Duración: 10 minutos

Este ejercicio le muestra cómo utilizar un RRO para eliminar la barra de desplazamiento de CarUiRecyclerView .

  1. En su RRO, agregue y modifique 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 optimizada del automóvil con los botones Arriba y Abajo en CarUiRecyclerView está presente o no. Cuando se establece en false , CarUiRecyclerView actúa 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>
    

Cree e instale su aplicación como antes. La barra de desplazamiento ahora se elimina de CarUiRecyclerView :

CarUiRecyclerView sin barra de desplazamiento
Figura 8. CarUiRecyclerView sin barra de desplazamiento

Utilice un diseño para superponer la barra de desplazamiento de CarUiRecyclerView

Duración: 15 minutos

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

  1. Agregue y modifique los siguientes archivos en su aplicación 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, debe agregar todos los ID y atributos de espacio de nombres al overlay.xml de su RRO. Vea los archivos 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 práctica es declarar estos valores en dimens.xml y colors.xml o incluso designarlos como archivos de color en la carpeta res/color/ . Para obtener más información, consulte Estilo de código Java de AOSP para colaboradores .

  2. Cree e instale su aplicación como antes. Ha creado CarUiRecyclerView con una barra de desplazamiento azul y rieles grises.

¡Felicidades! Ambas flechas aparecen en la parte inferior de la barra de desplazamiento. Aplicó con éxito un RRO a un archivo de recursos de diseño car-ui-lib utilizando el sistema de compilación Gradle a través de 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 RRO

Duración: 15 minutos

Hasta este punto, ha aplicado una RRO a los componentes car-ui-lib utilizando componentes del marco (no AndroidX). Para usar componentes de AndroidX en un RRO, debes agregar las dependencias de ese componente tanto a la aplicación como al RRO build.gradle. También debe agregar attrs de ese componente a overlayable.xml en su aplicación, así como sample_overlay.xml en su RRO.

Nuestra biblioteca ( car-ui-lib ) usa ConstraintLayout así como otros componentes de AndroidX, por lo que su overlayable.xml podría verse así:

<?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. Cambie el diseño de los elementos de la lista en CarUiRecyclerView usando ConstraintLayout . Agregue o modifique los siguientes archivos en su 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 varios componentes/recursos que no están incluidos como dependencias de la aplicación. Estos son recursos car-ui-lib . Puedes solucionar este problema agregando car-ui-lib como una dependencia de tu aplicación 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 usando componentes de AndroidX ( ConstraintLayout ) cuando sus atributos estaban presentes en el archivo car-ui-lib llamado overlayable.xml así como en el RRO sample_overlay.xml . Es posible hacer algo similar en tu propia aplicación. Simplemente agregue todos los attrs correspondientes al overlayable.xml de su aplicación, similar a car-ui-lib .

Sin embargo, no es posible realizar RRO en una aplicación que utiliza componentes de AndroidX cuando la aplicación tiene car-ui-lib como dependencia en su build.gradle (cuando la aplicación usa componentes car-ui-lib ). Dado que las asignaciones de atributos ya estaban definidas en overlayable.xml de la biblioteca car-ui-lib , agregarlas al overlayable.xml de su aplicación con car-ui-lib como dependencia causaría un error mergeDebugResources como el que se muestra a continuación. 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'