Codelab: criar RROs com componentes car-ui-lib usando o sistema de compilação Gradle

Use a biblioteca car-ui-lib para iniciar sistemas de infoentretenimento (IVI) autoconsistentes no veículo. Este codelab apresenta o car-ui-lib e como você pode usar Runtime Resource Overlays (RROs) para personalizar componentes na biblioteca.

O que você aprenderá

Como:

  • Inclua componentes car-ui-lib em seu aplicativo Android.
  • Use o Gradle para criar aplicativos Android e RROs.
  • Use RROs com car-ui-lib .

Este codelab não detalha como os RROs funcionam. Consulte sobreposições de recursos de tempo de execução (RRO) e solução de problemas de sobreposições de recursos de tempo de execução para saber mais.

Antes que você comece

Pré-requisitos

Antes de começar, certifique-se de ter:

Criar um novo aplicativo Android

Duração: 15 minutos

Nesta seção, você cria um novo projeto do Android Studio.

  1. No Android Studio, crie um aplicativo com uma EmptyActivity .

    Criar uma atividade vazia
    Figura 1. Criar uma atividade vazia
  2. Nomeie o aplicativo CarUiCodelab e selecione a linguagem Java. Você também pode selecionar um local de arquivo, se desejar. Aceite os valores padrão para as configurações restantes.

    Nomeie seu aplicativo
    Figura 2. Nomeie seu aplicativo
  3. Substitua activity_main.xml pelo seguinte bloco 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 bloco de código exibe a string sample_text , que não está definida.

  4. Adicione a string de recurso sample_text e defina-a como “Hello World!” em seu arquivo strings.xml . Para abrir esse arquivo, selecione app > src > main > res > values ​​> 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 criar seu aplicativo, clique no botão verde Reproduzir no canto superior direito. Fazer isso instala automaticamente o apk em seu emulador ou dispositivo Android por meio do Gradle.

    Botão de reprodução

O novo aplicativo deve abrir automaticamente em seu emulador ou dispositivo Android. Caso contrário, abra o aplicativo CarUiCodelab no iniciador de aplicativos, que agora está instalado. Aparece assim:

Abra o novo aplicativo CarUiCodelab
Figura 3. Abra o novo aplicativo CarUiCodelab

Adicione car-ui-lib ao seu aplicativo Android

Duração: 15 minutos

Adicione car-ui-lib ao seu aplicativo:

  1. Para adicionar a dependência car-ui-lib ao arquivo build.gradle do seu projeto, selecione app > build.gradle . Suas dependências devem aparecer assim:

    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'
    }
    

Use componentes car-ui-lib em seu aplicativo Android

Agora que você tem car-ui-lib , adicione uma barra de ferramentas ao seu aplicativo.

  1. Em seu arquivo MainActivity.java , sobrescreva o 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. Certifique-se de importar ToolbarController :

    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.toolbar.ToolbarController;
    
  3. Para usar o tema Theme.CarUi.WithToolbar , selecione app > src > main > AndroidManifest.xml e atualize AndroidManifest.xml para aparecer da seguinte forma:

    <?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 criar o aplicativo, pressione o botão verde Reproduzir como antes.

    Crie o aplicativo

Adicione RROs ao seu aplicativo

Duração: 30 minutos

Se você estiver familiarizado com sobreposições de recursos de tempo de execução (RROs), vá para a próxima seção, Adicionar um controlador de permissão ao seu aplicativo . Caso contrário, para aprender os fundamentos dos RROs, consulte Alterar o valor dos recursos de um aplicativo em tempo de execução .

Adicione um controlador de permissão ao seu aplicativo

Para controlar quais recursos um pacote RRO sobrepõe, adicione um arquivo chamado overlayable.xml à pasta /res do seu aplicativo. Este arquivo serve como um controlador de permissão entre seu aplicativo (o alvo ) e seu pacote RRO (o overlay ).

  1. Adicione res/values/overlayable.xml ao seu aplicativo e copie o seguinte conteúdo em seu arquivo:

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

    Como a string sample_text deve ser sobreposta por um RRO, inclua o nome do recurso no overlayable.xml do aplicativo.

    Seu arquivo overlayable.xml DEVE residir em res/values/ . Caso contrário, o OverlayManagerService não poderá localizá-lo.

    Para saber mais sobre recursos que podem ser sobrepostos e como eles podem ser configurados, consulte Restringindo recursos que podem ser sobrepostos .

Criar um pacote RRO

Nesta seção, você cria um pacote RRO para alterar a string exibida acima de “Hello World!” para “Olá Mundo RRO”.

  1. Para criar um novo projeto, selecione Arquivo > Novo > Novo projeto . Certifique-se de selecionar Sem atividade em vez de Atividade vazia, pois os pacotes RRO contêm apenas recursos.

    Suas configurações são semelhantes às ilustradas abaixo. O local onde eles são salvos pode ser diferente:

  2. Depois de criar o novo projeto CarUiRRO , declare o projeto como um 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>
    

    Fazer isso cria um erro com @xml/sample_overlay . O arquivo resourcesMap mapeia nomes de recursos do pacote de destino para o pacote RRO.

  3. Copie o seguinte bloco de código em …/res/xml/sample_overlay.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
    </overlay>
    
  4. Adicione 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>
    
    Compilação Gradle criada de RRO
  5. Para criar seu destino RRO, pressione o botão verde Reproduzir para criar uma compilação Gradle de seu RRO em seu emulador ou dispositivo Android.

  6. Para verificar se o seu RRO está instalado corretamente, execute:

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

    Este comando exibe informações úteis sobre o estado dos pacotes RRO no sistema.

    • [ ] designa que o RRO está instalado e pronto para ser ativado.
    • --- indica que o RRO está instalado, mas contém erros.
    • [X] significa que o RRO está instalado e ativado.

    Se o seu RRO contiver erros, consulte Solução de problemas de sobreposições de recursos de tempo de execução antes de continuar.

  7. Para habilitar o RRO e verificar se ele 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
    

Seu aplicativo exibe a string "Hello World RRO".

Olá, Mundo RRO!
Figura 4 : Olá, mundo RRO!

Parabéns! Você criou seu primeiro RRO.

Ao usar RROs, convém usar os sinalizadores da Android Asset Packaging Tool (AAPT2) --no-resource-deduping e --no-resource-removal descritos em Opções de link . Não é necessário adicionar os sinalizadores neste codelab, mas sugerimos que você os use em seus RROs para evitar a remoção de recursos (e dores de cabeça com depuração). Você pode adicioná-los ao arquivo build.gradle do seu RRO assim:

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

Para saber mais sobre esses sinalizadores, consulte Construindo o pacote e AAPT2 .

Modifique os componentes car-ui-lib usando RROs em seu aplicativo Android

Esta página descreve como você pode usar uma sobreposição de recursos de tempo de execução (RRO) para modificar componentes da biblioteca car-ui-lib em seu aplicativo Android.

Definir a cor de fundo da barra de ferramentas

Duração: 15 minutos

Para alterar a cor de fundo da barra de ferramentas:

  1. Adicione o seguinte valor ao seu aplicativo RRO e defina o recurso como verde brilhante ( #0F0 ):

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

    A biblioteca car-ui-lib contém um recurso denominado car_ui_toolbar_background . Quando este recurso está contido na configuração de um RRO, a Barra de Ferramentas não muda porque o valor errado é direcionado.

  2. No AndroidManifest.xml para seu RRO, atualize targetName para apontar para car-ui-lib :

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

    Você DEVE criar um novo pacote RRO para cada pacote de destino que deseja RRO. Por exemplo, ao criar sobreposições para dois destinos diferentes, você deve criar dois apks de sobreposição.

  3. Construa, verifique, instale e habilite o RRO da mesma forma que antes.

Seu aplicativo aparece assim:

Nova cor de fundo da barra de ferramentas
Figura 5 : Nova cor de fundo da barra de ferramentas

Layouts e estilos RRO

Duração: 15 minutos

Neste exercício, você cria um novo aplicativo semelhante ao aplicativo criado anteriormente. Este aplicativo permite que o layout seja sobreposto. Siga as mesmas etapas de antes ou modifique seu aplicativo existente.

  1. Certifique-se de adicionar as seguintes linhas ao 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. Certifique-se de activity_main.xml apareça da seguinte forma:

    <?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. Em seu aplicativo RRO, crie um res/layout/activity_main.xml e adicione o seguinte:

    <?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. Atualize res/values/styles.xml para adicionar nosso estilo ao 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. Altere o targetName em AndroidManifest.xml para apontar para o nome do seu novo aplicativo:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. Adicione os recursos ao arquivo sample_overlay.xml em seu 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. Crie e instale o aplicativo e o RRO da mesma forma que antes (botão verde Reproduzir ). Certifique-se de habilitar seu RRO.

O aplicativo e o RRO são renderizados da seguinte maneira. O texto Hello World RRO é verde e centralizado conforme especificado no layout RRO.

Alô mundo RRO
Figura 6 : Olá, Mundo RRO

Adicione CarUiRecyclerView ao seu aplicativo

Duração: 15 minutos

A interface CarUiRecyclerView fornece APIs para acessar um RecyclerView personalizado por meio de recursos car-ui-lib . Por exemplo, CarUiRecyclerView verifica um sinalizador em tempo de execução para determinar se a barra de rolagem deve ser habilitada ou não e seleciona o layout correspondente.

CarUiRecyclerViewContainer
Figura 7. CarUiRecyclerViewContainer
  1. Para adicionar um CarUiRecyclerView , adicione-o aos arquivos activity_main.xml e MainActivity.java . Você pode criar um novo aplicativo do zero ou modificar o aplicativo existente. Se você modificar o aplicativo existente, certifique-se de remover recursos não 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"/>
    

    O seguinte erro pode aparecer, que você pode ignorar:

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

    Contanto que sua classe esteja escrita corretamente e você tenha adicionado car-ui-lib como uma dependência, você pode construir e compilar seu apk. Para remover o erro, selecione Arquivo > Invalidar Caches e clique em Invalidar e Reiniciar.

    Adicione o seguinte 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. Crie e instale seu aplicativo como antes.

Agora você vê um CarUiRecyclerView :

CarUiRecyclerViewName
Figura 7 : CarUiRecyclerView

Use um RRO para remover a barra de rolagem

Duração: 10 minutos

Este exercício mostra como usar um RRO para remover a barra de rolagem de CarUiRecyclerView .

  1. No seu RRO, adicione e modifique os seguintes arquivos:

    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>
    

    O recurso car_ui_scrollbar_enable é um recurso booleano car-ui-lib , que controla se a barra de rolagem otimizada do carro com botões para cima e para baixo no CarUiRecyclerView está presente ou não. Quando definido como false , CarUiRecyclerView age como um AndroidX RecyclerView .

    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>
    

Crie e instale seu aplicativo como antes. A barra de rolagem agora foi removida do CarUiRecyclerView :

CarUiRecyclerView sem barra de rolagem
Figura 8. CarUiRecyclerView sem barra de rolagem

Use um layout para sobrepor a barra de rolagem CarUiRecyclerView

Duração: 15 minutos

Neste exercício, você modificará o layout da barra de rolagem CarUiRecyclerView .

  1. Adicione e modifique os seguintes arquivos em seu aplicativo 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 sobrepor um arquivo de layout, você deve adicionar todos os IDs e atributos de namespace ao overlay.xml de seu RRO. Veja os arquivos abaixo.

    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>
    

    Sugere-se examinar como esses arquivos interagem.

    Para simplificar, as dimensões e cores são codificadas. No entanto, uma prática recomendada é declarar esses valores em dimens.xml e colors.xml ou até mesmo designá-los como arquivos de cores na pasta res/color/ . Para saber mais, consulte Estilo de código Java AOSP para colaboradores .

  2. Crie e instale seu aplicativo como antes. Você construiu CarUiRecyclerView com uma barra de rolagem azul e trilhos cinza.

Parabéns! Ambas as setas aparecem na parte inferior da barra de rolagem. Você aplicou com êxito um RRO a um arquivo de recurso de layout car-ui-lib usando o sistema de compilação Gradle por meio do Android Studio.

CarUiRecyclerView com uma barra de rolagem azul com trilhos cinza
Figura 9. CarUiRecyclerView com uma barra de rolagem azul com trilhos cinza

Itens da lista de RRO

Duração: 15 minutos

Até este ponto, você aplicou um RRO aos componentes car-ui-lib usando componentes de estrutura (não AndroidX). Para usar componentes do AndroidX em um RRO, você deve adicionar as dependências desse componente ao aplicativo e ao RRO build.gradle. Você também deve adicionar attrs desse componente a overlayable.xml em seu aplicativo, bem como o sample_overlay.xml em seu RRO.

Nossa biblioteca ( car-ui-lib ) usa ConstraintLayout , bem como outros componentes do AndroidX, então seu overlayable.xml pode ter esta aparência:

<?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. Altere o layout dos itens da lista no CarUiRecyclerView usando ConstraintLayout . Adicione ou modifique os seguintes arquivos em seu 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 faz referência a várias referências a vários componentes/recursos que não estão incluídos como dependências do aplicativo. Estes são recursos car-ui-lib . Você pode corrigir isso adicionando car-ui-lib como uma dependência ao seu aplicativo RRO em 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'
    }
    

O título e o corpo agora estão alinhados à direita em vez de alinhados à esquerda.

Título e corpo alinhados à direita
Figura 10. Título e corpo alinhados à direita

Apenas aplicamos um RRO a car-ui-lib usando componentes AndroidX ( ConstraintLayout ) quando seus atributos estavam presentes no arquivo car-ui-lib denominado overlayable.xml , bem como no RRO sample_overlay.xml . É possível fazer algo semelhante em seu próprio aplicativo. Basta adicionar todos os attrs correspondentes ao overlayable.xml do seu aplicativo, semelhante a car-ui-lib .

No entanto, não é possível RRO de um aplicativo usando componentes AndroidX quando o aplicativo tem car-ui-lib como uma dependência em seu build.gradle (quando o aplicativo usa componentes car-ui-lib ). Como os mapeamentos de atributos já foram definidos no overlayable.xml da biblioteca car-ui-lib , adicioná-los ao overlayable.xml do seu aplicativo com car-ui-lib como uma dependência causaria um erro mergeDebugResources como o abaixo. Isso ocorre porque esses atributos estão presentes em vários arquivos overlayable.xml :

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