Codelab: создание RRO с компонентами car-ui-lib с использованием системы сборки Gradle

Используйте библиотеку car-ui-lib для запуска самосогласованных автомобильных информационно-развлекательных систем (IVI). В этом практическом занятии вы познакомитесь с библиотекой car-ui-lib и узнаете, как использовать наложения ресурсов во время выполнения (RRO) для настройки компонентов библиотеки.

Что вы узнаете

Как это сделать:

  • Включите компоненты car-ui-lib в ваше Android-приложение.
  • Используйте Gradle для сборки приложений Android и RRO.
  • Используйте RRO с car-ui-lib .

В этом практическом задании не описывается, как работают RRO (Return on Resources). Для получения дополнительной информации см. разделы «Изменение значений ресурсов приложения во время выполнения» и «Устранение неполадок с наложением ресурсов во время выполнения» .

Прежде чем начать

Предварительные требования

Прежде чем начать, убедитесь, что у вас есть:

  • Компьютер с командной строкой (машина под управлением Linux, Mac или Windows с подсистемой Windows для Linux).

  • Android Studio .

  • Подключите к компьютеру устройство Android или эмулятор. См. разделы «Загрузка исходного кода Android» и «Сборка Android» .

  • Базовые знания о RRO (организациях, занимающихся исследованиями и контролем качества).

Создайте новое приложение для Android.

Продолжительность: 15 минут

В этом разделе вы создадите новый проект Android Studio.

  1. В Android Studio создайте приложение с помощью EmptyActivity .

    Создайте пустое действие
    Рисунок 1. Создание пустой активности.
  2. Назовите приложение CarUiCodelab , а затем выберите язык Java. При желании вы также можете указать местоположение файла. Примите значения по умолчанию для остальных настроек.

    Назовите своё приложение
    Рисунок 2. Назовите свое приложение.
  3. Замените файл activity_main.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"
        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>
    

    В этом блоке кода отображается строка sample_text , которая не определена.

  4. Добавьте строку ресурса sample_text и установите для нее значение "Hello World!" в файл strings.xml . Чтобы открыть этот файл, выберите 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. Чтобы собрать приложение, нажмите зеленую кнопку «Play» в правом верхнем углу. Это автоматически установит APK-файл на ваш эмулятор или устройство Android через Gradle.

    Кнопка воспроизведения

Новое приложение должно автоматически открыться на вашем эмуляторе или устройстве Android. Если этого не произошло, откройте приложение CarUiCodelab из панели запуска приложений, которая теперь установлена. Оно выглядит примерно так:

Откройте новое приложение CarUiCodelab
Рисунок 3. Откройте новое приложение CarUiCodelab.

Добавьте car-ui-lib в ваше Android-приложение

Продолжительность: 15 минут

Добавьте car-ui-lib в ваше приложение:

  1. Чтобы добавить зависимость car-ui-lib в файл build.gradle вашего проекта, выберите 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'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    }
    

Используйте компоненты car-ui-lib в своем Android-приложении.

Теперь, когда у вас есть car-ui-lib , добавьте панель инструментов в ваше приложение.

  1. В файле MainActivity.java переопределите метод 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. Обязательно импортируйте ToolbarController :

    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.toolbar.ToolbarController;
    
  3. Чтобы использовать тему Theme.CarUi.WithToolbar , выберите app > src > main > AndroidManifest.xml , а затем обновите файл AndroidManifest.xml следующим образом:

    <?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. Чтобы собрать приложение, нажмите зеленую кнопку «Играть» , как и раньше.

    Создайте приложение

Добавьте RRO в ваше приложение

Продолжительность: 30 минут

Если вы знакомы с RRO, перейдите к следующему разделу « Добавление контроллера разрешений в ваше приложение» . В противном случае, чтобы узнать основы RRO, см. раздел «Изменение значений ресурсов приложения во время выполнения» .

Добавьте контроллер разрешений в ваше приложение.

Чтобы контролировать, какие ресурсы перекрываются пакетом RRO, добавьте файл с именем overlayable.xml в папку /res вашего приложения. Этот файл служит контроллером разрешений между вашим приложением ( целевым объектом ) и вашим пакетом RRO ( перекрывающимся объектом ).

  1. Добавьте файл res/values/overlayable.xml в ваше приложение и скопируйте в него следующее содержимое:

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

    Поскольку строковый sample_text должен быть доступен для наложения с помощью RRO, укажите имя ресурса в файле overlayable.xml приложения.

    Ваш файл overlayable.xml ДОЛЖЕН находиться в папке res/values/ . В противном случае OverlayManagerService не сможет его найти.

    Чтобы узнать больше о накладываемых ресурсах и способах их настройки, см. раздел «Ограничение накладываемых ресурсов» .

Создайте пакет RRO

В этом разделе вы создадите пакет RRO, чтобы изменить отображаемую выше строку с "Hello World!" на "Hello World RRO".

  1. Чтобы создать новый проект, выберите Файл > Создать > Новый проект . Обязательно выберите «Без активности» вместо «Пустая активность», поскольку пакеты RRO содержат только ресурсы.

    Ваши настройки выглядят примерно так, как показано ниже. Место их сохранения может отличаться:

  2. После создания нового проекта CarUiRRO объявите его как RRO, изменив файл 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>
    

    Это приводит к ошибке с @xml/sample_overlay . Файл resourcesMap сопоставляет имена ресурсов из целевого пакета с пакетом RRO. Установка флага hasCode в значение false обязательна для пакетов RRO. Кроме того, пакеты RRO не должны содержать файлы DEX.

  3. Скопируйте следующий блок кода в файл …/res/xml/sample_overlay.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
    </overlay>
    
  4. Добавьте sample_text в файл …/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>
    
    Создана сборка Gradle для RRO.
  5. Чтобы собрать целевой объект RRO, нажмите зеленую кнопку «Play» , чтобы создать сборку RRO с помощью Gradle на вашем эмуляторе или устройстве Android.

  6. Чтобы убедиться в правильности установки RRO, выполните следующую команду:

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

    Эта команда отображает полезную информацию о состоянии пакетов RRO в системе.

    • [ ] означает, что RRO установлен и готов к активации.
    • --- указывает на то, что RRO установлен, но содержит ошибки.
    • [X] означает, что RRO установлен и активирован.

    Если в вашем файле RRO обнаружены ошибки, перед продолжением ознакомьтесь с разделом «Устранение неполадок, связанных с наложением ресурсов среды выполнения» .

  7. Чтобы включить RRO и убедиться в его включении:

    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
    

Ваше приложение отображает строку "Hello World RRO".

Привет, мир RRO!
Рисунок 4 : Hello World RRO!

Поздравляем! Вы создали свой первый RRO.

При использовании RRO (Resource Resource Remover) может потребоваться использовать флаги --no-resource-deduping и --no-resource-removal инструмента Android Asset Packaging Tool (AAPT2), описанные в разделе «Параметры компоновки» . В этом примере добавлять эти флаги необязательно, но мы рекомендуем использовать их в ваших RRO, чтобы избежать удаления ресурсов (и проблем с отладкой). Вы можете добавить их в файл build.gradle вашего RRO следующим образом:

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

Чтобы узнать больше об этих флагах, см. разделы «Сборка пакета» и «AAPT2» .

Модифицируйте компоненты car-ui-lib с помощью RRO в вашем Android-приложении.

На этой странице описано, как использовать наложение ресурсов во время выполнения (RRO) для изменения компонентов из библиотеки car-ui-lib в вашем Android-приложении.

Установить цвет фона панели инструментов

Продолжительность: 15 минут

Чтобы изменить цвет фона панели инструментов:

  1. Добавьте следующее значение в ваше приложение RRO и установите для ресурса ярко-зеленый цвет ( #0F0 ):

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

    Библиотека car-ui-lib содержит ресурс с именем car_ui_toolbar_background . Если этот ресурс включен в конфигурацию RRO, панель инструментов не изменяется, поскольку используется неверное значение.

  2. В файле AndroidManifest.xml для вашего RRO обновите targetName , указав путь к car-ui-lib :

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

    Для каждого целевого пакета, который вы хотите подвергнуть RRO, ОБЯЗАТЕЛЬНО необходимо создать новый пакет RRO. Например, при создании наложений для двух разных целевых устройств необходимо создать два APK-файла наложений.

  3. Сборку, проверку, установку и активацию RRO следует производить тем же способом, что и раньше.

Ваше приложение выглядит следующим образом:

Новый цвет фона панели инструментов
Рисунок 5 : Новый цвет фона панели инструментов

Макеты и стили RRO

Продолжительность: 15 минут

В этом упражнении вы создадите новое приложение, аналогичное тому, которое вы создали ранее. Это приложение позволяет накладывать элементы интерфейса друг на друга. Выполните те же шаги, что и раньше, или модифицируйте существующее приложение.

  1. Обязательно добавьте следующие строки в 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. Убедитесь, что activity_main.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"
        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. В вашем приложении RRO создайте файл res/layout/activity_main.xml и добавьте в него следующее:

    <?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. Обновите файл res/values/styles.xml , чтобы добавить наш стиль в 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. Измените значение targetName в файле AndroidManifest.xml , указав в нем имя вашего нового приложения:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. Добавьте ресурсы в файл sample_overlay.xml в вашем 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. Соберите и установите приложение и RRO тем же способом, что и раньше (зеленая кнопка «Играть» ). Обязательно включите RRO.

Приложение и RRO отображаются следующим образом. Текст RRO «Hello World» зелёный и центрирован, как указано в макете RRO.

Привет, мир RRO
Рисунок 6 : Hello World RRO

Добавьте CarUiRecyclerView в ваше приложение.

Продолжительность: 15 минут

Интерфейс CarUiRecyclerView предоставляет API для доступа к RecyclerView , настраиваемому с помощью ресурсов car-ui-lib . Например, CarUiRecyclerView проверяет флаг во время выполнения, чтобы определить, следует ли включать полосу прокрутки или нет, и выбирает соответствующий макет.

CarUiRecyclerViewContainer
Рисунок 7. CarUiRecyclerViewContainer
  1. Чтобы добавить CarUiRecyclerView , добавьте его в файлы activity_main.xml и MainActivity.java . Вы можете либо создать новое приложение с нуля, либо изменить существующее. Если вы изменяете существующее приложение, обязательно удалите необъявленные ресурсы из 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"/>
    

    Может появиться следующая ошибка, которую вы можете проигнорировать:

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

    Если название вашего класса написано правильно и вы добавили car-ui-lib в качестве зависимости, вы можете собрать и скомпилировать свой APK-файл. Чтобы устранить ошибку, выберите Файл > Очистить кэш, затем нажмите Очистить и перезапустить.

    Добавьте следующее в 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. Соберите и установите приложение, как и раньше.

Теперь вы видите CarUiRecyclerView :

CarUiRecyclerView
Рисунок 7 : CarUiRecyclerView

Используйте RRO для удаления полосы прокрутки.

Продолжительность: 10 минут

В этом упражнении показано, как использовать RRO для удаления полосы прокрутки из CarUiRecyclerView .

  1. В вашем файле RRO добавьте и измените следующие файлы:

    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>
    

    Ресурс car_ui_scrollbar_enable — это логический ресурс из библиотеки car-ui-lib , который определяет, присутствует ли оптимизированная для автомобилей полоса прокрутки с кнопками «Вверх» и «Вниз» в CarUiRecyclerView . Если установлено значение false , CarUiRecyclerView работает как RecyclerView 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>
    

Соберите и установите приложение, как и раньше. Полоса прокрутки теперь удалена из CarUiRecyclerView :

CarUiRecyclerView без полосы прокрутки
Рисунок 8. CarUiRecyclerView без полосы прокрутки.

Используйте макет для наложения полосы прокрутки CarUiRecyclerView.

Продолжительность: 15 минут

В этом упражнении вы изменяете расположение полосы прокрутки CarUiRecyclerView .

  1. Добавьте и измените следующие файлы в вашем приложении 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>
    

    Для наложения файла макета необходимо добавить все идентификаторы и атрибуты пространства имен в файл overlay.xml вашего RRO. См. файлы ниже.

    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>
    

    Рекомендуется изучить, как эти файлы взаимодействуют друг с другом.

    Для простоты размеры и цвета заданы жестко. Однако рекомендуется объявлять эти значения в файлах dimens.xml и colors.xml или даже указывать их в качестве файлов цветов в папке res/color/ . Более подробную информацию можно найти в разделе «Стиль кода AOSP Java для участников проекта» .

  2. Соберите и установите приложение, как и раньше. Вы создали CarUiRecyclerView с синей полосой прокрутки и серыми направляющими.

Поздравляем! Обе стрелки появляются внизу полосы прокрутки. Вы успешно применили RRO к файлу ресурсов макета car-ui-lib с помощью системы сборки Gradle через Android Studio.

CarUiRecyclerView с синей полосой прокрутки и серыми направляющими
Рисунок 9. CarUiRecyclerView с синей полосой прокрутки и серыми направляющими.

Пункты списка RRO

Продолжительность: 15 минут

До этого момента вы применяли RRO к компонентам car-ui-lib используя компоненты фреймворка (а не AndroidX). Чтобы использовать компоненты AndroidX в RRO, необходимо добавить зависимости этого компонента как в приложение, так и в файл build.gradle. Также необходимо добавить attrs этого компонента в файл overlayable.xml вашего приложения, а также в файл sample_overlay.xml вашего RRO.

Наша библиотека ( car-ui-lib ) использует ConstraintLayout , а также другие компоненты AndroidX, поэтому её overlayable.xml может выглядеть примерно так:

<?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. Измените расположение элементов списка в CarUiRecyclerView с помощью ConstraintLayout . Добавьте или измените следующие файлы в вашем 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 содержатся ссылки на несколько компонентов/ресурсов, которые не включены в качестве зависимостей приложения. Это ресурсы car-ui-lib . Вы можете исправить это, добавив car-ui-lib в качестве зависимости к вашему RRO-приложению в 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'
    }
    

Теперь заголовок и основной текст выравниваются по правому краю, а не по левому.

Заголовок и основной текст выровнены по правому краю.
Рисунок 10. Заголовок и основной текст, выровненные по правому краю.

Мы применяли RRO к car-ui-lib с использованием компонентов AndroidX ( ConstraintLayout ) только в том случае, если его атрибуты присутствовали в файле car-ui-lib с именем overlayable.xml , а также в файле RRO sample_overlay.xml . Аналогичное можно сделать и в вашем приложении. Просто добавьте все соответствующие attrs в overlayable.xml вашего приложения, как это сделано в car-ui-lib .

Однако, невозможно выполнить RRO для приложения, использующего компоненты AndroidX, если в build.gradle приложения указана зависимость car-ui-lib (когда приложение использует компоненты car-ui-lib ). Поскольку сопоставление атрибутов уже определено в overlayable.xml библиотеки car-ui-lib , добавление их в файл overlayable.xml вашего приложения с зависимостью от car-ui-lib приведет к ошибке mergeDebugResources подобной приведенной ниже. Это связано с тем, что эти атрибуты присутствуют в нескольких файлах overlayable.xml :

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