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. Дополнительные сведения см. в разделах Изменение значения ресурсов приложения во время выполнения и Устранение неполадок с наложением ресурсов во время выполнения .

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

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

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

Создайте новое приложение для 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. Чтобы создать свое приложение, нажмите зеленую кнопку «Воспроизвести» в правом верхнем углу. При этом 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 , выберите приложение > 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.

  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, нажмите зеленую кнопку «Воспроизвести» , чтобы создать сборку Gradle вашего RRO на вашем эмуляторе или устройстве 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».

Привет, мир РРО!
Рисунок 4. Привет, мир, RRO!

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

При использовании RRO вы можете использовать флаги Android Asset Packaging Tool (AAPT2) --no-resource-deduping и --no-resource-removal описанные в разделе «Параметры ссылки» . Нет необходимости добавлять флаги в эту кодовую лабораторию, но мы предлагаем вам использовать их в своих 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.

Привет, мир РРО
Рисунок 6 : Привет, мир, 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 :

АвтомобильUiRecyclerПросмотр
Рисунок 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 действует как 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>
    

Создайте и установите свое приложение, как и раньше. Полоса прокрутки теперь удалена из 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/ . Дополнительные сведения см. в разделе Стиль кода Java AOSP для участников .

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

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

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

РРО Элементы списка

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

К этому моменту вы применили RRO к компонентам car-ui-lib , используя компоненты платформы (не AndroidX). Чтобы использовать компоненты AndroidX в RRO, необходимо добавить зависимости этого компонента как в приложение, так и в 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, если приложение имеет car-ui-lib в качестве зависимости в своем build.gradle (когда приложение использует компоненты 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'