Ćwiczenie z programowania: tworzenie RRO z komponentami car-ui-lib przy użyciu systemu kompilacji Gradle

Użyj car-ui-lib, aby uruchamiać spójne systemy multimedialne w pojazdach. W tym ćwiczeniu dowiesz się, czym jest car-ui-lib i jak za pomocą nakładek zasobów środowiska wykonawczego (RRO) dostosowywać komponenty w bibliotece.

Czego się nauczysz

Instrukcje:

  • Umieść w aplikacji na Androida komponenty car-ui-lib.
  • Używaj Gradle do kompilowania aplikacji na Androida i nakładek RRO.
  • Używaj RRO z car-ui-lib.

W tym ćwiczeniu nie opisujemy szczegółowo działania nakładek RRO. Więcej informacji znajdziesz w artykułach Zmiana wartości zasobów aplikacji w czasie działaniaRozwiązywanie problemów z nakładkami zasobów w czasie działania.

Zanim rozpoczniesz

Wymagania wstępne

Zanim zaczniesz, upewnij się, że masz:

Tworzenie nowej aplikacji na Androida

Czas trwania: 15 minut

W tej sekcji utworzysz nowy projekt w Android Studio.

  1. W Android Studio utwórz aplikację z EmptyActivity.

    Tworzenie pustej aktywności
    Rysunek 1.Tworzenie pustej aktywności
  2. Nazwij aplikację CarUiCodelab, a potem wybierz język Java. W razie potrzeby możesz też wybrać lokalizację pliku. Zaakceptuj domyślne wartości pozostałych ustawień.

     Nazwij aplikację
    Rysunek 2. Nazwij aplikację
  3. Zastąp activity_main.xml tym blokiem kodu:

    <?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>
    

    Ten blok kodu wyświetla ciąg znaków sample_text, który nie jest zdefiniowany.

  4. Dodaj ciąg zasobu sample_text i ustaw go na „Hello World!” w pliku strings.xml. Aby otworzyć ten plik, wybierz 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. Aby utworzyć aplikację, w prawym górnym rogu kliknij zielony przycisk Odtwórz. Spowoduje to automatyczne zainstalowanie pliku APK w emulatorze lub na urządzeniu z Androidem za pomocą Gradle.

    Przycisk odtwarzania

Nowa aplikacja powinna otworzyć się automatycznie na emulatorze lub urządzeniu z Androidem. Jeśli nie, otwórz aplikację CarUiCodelab z menu z aplikacjami. Wygląda to tak:

Otwórz nową aplikację CarUiCodelab.
Rysunek 3. Otwórz nową aplikację CarUiCodelab

Dodawanie biblioteki car-ui-lib do aplikacji na Androida

Czas trwania: 15 minut

Dodaj car-ui-lib do aplikacji:

  1. Aby dodać zależność car-ui-lib do pliku build.gradle projektu, wybierz app > build.gradle. Zależności powinny wyglądać tak:

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

Używanie komponentów car-ui-lib w aplikacji na Androida

Teraz, gdy masz car-ui-lib, dodaj pasek narzędzi do aplikacji.

  1. W pliku MainActivity.java zastąp metodę 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. Pamiętaj, aby zaimportować ToolbarController:

    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.toolbar.ToolbarController;
    
  3. Aby użyć motywu Theme.CarUi.WithToolbar, wybierz app > src > main > AndroidManifest.xml, a następnie zaktualizuj AndroidManifest.xml, aby wyglądało to tak:

    <?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. Aby skompilować aplikację, naciśnij zielony przycisk Odtwórz, tak jak wcześniej.

    Tworzenie aplikacji

Dodawanie do aplikacji nakładek RRO

Czas trwania: 30 minut

Jeśli znasz nakładki RRO, przejdź do następnej sekcji: Dodawanie kontrolera uprawnień do aplikacji. Jeśli chcesz poznać podstawy nakładek RRO, przeczytaj artykuł Zmiana wartości zasobów aplikacji w czasie działania.

Dodawanie do aplikacji kontrolera uprawnień

Aby określić, które zasoby ma zastępować pakiet RRO, dodaj do folderu /res aplikacji plik o nazwie overlayable.xml. Ten plik pełni funkcję kontrolera uprawnień między aplikacją (elementem docelowym) a pakietem RRO (nakładką).

  1. Dodaj res/values/overlayable.xml do aplikacji i skopiuj do pliku te wiersze:

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

    Ciąg sample_text musi być nakładany przez RRO, więc umieść nazwę zasobu w pliku overlayable.xml aplikacji.

    Plik overlayable.xml MUSI znajdować się w katalogu res/values/. W przeciwnym razie OverlayManagerService nie będzie w stanie go znaleźć.

    Więcej informacji o zasobach, które można nakładać, i sposobach ich konfigurowania znajdziesz w artykule Ograniczanie zasobów, które można nakładać.

Tworzenie pakietu RRO

W tej sekcji utworzysz pakiet RRO, aby zmienić ciąg znaków wyświetlany powyżej z „Hello World!” na „Hello World RRO”.

  1. Aby utworzyć nowy projekt, kliknij Plik > Nowy > Nowy projekt. Pamiętaj, aby wybrać Brak aktywności zamiast Pusta aktywność, ponieważ pakiety RRO zawierają tylko zasoby.

    Konfiguracje będą wyglądać podobnie do tych na ilustracji poniżej. Miejsce, w którym są zapisywane, może się różnić:

  2. Po utworzeniu nowego projektu CarUiRRO zadeklaruj go jako RRO, modyfikując 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>
    

    Spowoduje to błąd w usłudze @xml/sample_overlay. Plik resourcesMap mapuje nazwy zasobów z pakietu docelowego na pakiet RRO. Ustawienie flagi hasCode na false jest obowiązkowe w przypadku pakietów RRO. Ponadto pakiety RRO nie mogą zawierać plików DEX.

  3. Skopiuj ten blok kodu do pliku …/res/xml/sample_overlay.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
    </overlay>
    
  4. Dodaj sample_text do …/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>
    
    Utworzono kompilację Gradle RRO
  5. Aby utworzyć docelowy RRO, naciśnij zielony przycisk Odtwórz, aby utworzyć kompilację Gradle RRO na emulatorze lub urządzeniu z Androidem.

  6. Aby sprawdzić, czy RRO jest prawidłowo zainstalowany, uruchom to polecenie:

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

    To polecenie wyświetla przydatne informacje o stanie pakietów RRO w systemie.

    • [ ] oznacza, że RRO jest zainstalowany i gotowy do aktywacji.
    • --- oznacza, że nakładka RRO jest zainstalowana, ale zawiera błędy.
    • [X] oznacza, że RRO jest zainstalowany i aktywny.

    Jeśli RRO zawiera błędy, przed kontynuowaniem zapoznaj się z artykułem Rozwiązywanie problemów z nakładkami zasobów w czasie działania.

  7. Aby włączyć RRO i sprawdzić, czy jest włączony:

    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
    

Aplikacja wyświetli ciąg znaków „Hello World RRO”.

Hello World RRO!
Rysunek 4.: RRO „Hello World”

Gratulacje! Udało Ci się utworzyć pierwszy RRO.

Podczas korzystania z RRO możesz używać flag narzędzia AAPT2 (Android Asset Packaging Tool) --no-resource-deduping--no-resource-removal opisanych w sekcji Opcje linkowania. W tym ćwiczeniu z programowania nie musisz dodawać tych flag, ale zalecamy ich używanie w przypadku nakładek RRO, aby uniknąć usuwania zasobów (i problemów z debugowaniem). Możesz je dodać do pliku build.gradle RRO w ten sposób:

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

Więcej informacji o tych flagach znajdziesz w sekcjach Tworzenie pakietuAAPT2.

Modyfikowanie car-ui-lib komponentów za pomocą nakładek RRO w aplikacji na Androida

Na tej stronie dowiesz się, jak używać nakładki zasobów w czasie działania (RRO) do modyfikowania komponentów z biblioteki car-ui-lib w aplikacji na Androida.

Ustawianie koloru tła paska narzędzi

Czas trwania: 15 minut

Aby zmienić kolor tła paska narzędzi:

  1. Dodaj do aplikacji RRO tę wartość i ustaw zasób na jasny zielony (#0F0):

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

    Biblioteka car-ui-lib zawiera zasób o nazwie car_ui_toolbar_background. Jeśli ten zasób znajduje się w konfiguracji RRO, pasek narzędzi nie zmienia się, ponieważ jest kierowany na nieprawidłową wartość.

  2. AndroidManifest.xml dla RRO zaktualizuj targetName, aby wskazywał car-ui-lib:

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

    Dla każdego pakietu docelowego, który chcesz poddać RRO, MUSISZ utworzyć nowy pakiet RRO. Na przykład podczas tworzenia nakładek dla 2 różnych miejsc docelowych musisz utworzyć 2 pliki APK nakładek.

  3. Zbuduj, zweryfikuj, zainstaluj i włącz RRO w taki sam sposób jak wcześniej.

Aplikacja będzie wyglądać tak:

Kolor tła nowego paska narzędzi
Ilustracja 5. Nowy kolor tła paska narzędzi

Układy i style RRO

Czas trwania: 15 minut

W tym ćwiczeniu utworzysz nową aplikację podobną do tej, którą utworzono wcześniej. Ta aplikacja umożliwia nakładanie układu. Wykonaj te same czynności co wcześniej lub zmodyfikuj istniejącą aplikację.

  1. Pamiętaj, aby dodać te wiersze do pliku 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. Sprawdź, czy activity_main.xml wygląda tak:

    <?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. W aplikacji RRO utwórz res/layout/activity_main.xml i dodaj te informacje:

    <?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. Zaktualizuj res/values/styles.xml, aby dodać nasz styl do 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. Zmień targetNameAndroidManifest.xml, aby wskazywało nazwę nowej aplikacji:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. Dodaj zasoby do pliku sample_overlay.xml w nakładce 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. Zbuduj i zainstaluj aplikację oraz RRO w taki sam sposób jak wcześniej (zielony przycisk Play). Pamiętaj, aby włączyć RRO.

Aplikacja i RRO są renderowane w ten sposób: Tekst RRO „Hello World” jest zielony i wyśrodkowany zgodnie z określeniem w RRO układu.

Hello World RRO
Rysunek 6.: RRO Hello World

Dodawanie CarUiRecyclerView do aplikacji

Czas trwania: 15 minut

Interfejs CarUiRecyclerView udostępnia interfejsy API umożliwiające dostęp do RecyclerView dostosowanego za pomocą zasobów car-ui-lib. Na przykład CarUiRecyclerView sprawdza w czasie działania flagę, aby określić, czy pasek przewijania powinien być włączony, czy nie i wybiera odpowiedni układ.

CarUiRecyclerViewContainer
Rysunek 7. CarUiRecyclerViewContainer
  1. Aby dodać CarUiRecyclerView, dodaj go do plików activity_main.xml i MainActivity.java. Możesz utworzyć nową aplikację od podstaw lub zmodyfikować istniejącą. Jeśli zmodyfikujesz istniejącą aplikację, usuń z overlayable.xml niezadeklarowane zasoby.

    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"/>
    

    Może pojawić się ten błąd, który możesz zignorować:

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

    Jeśli nazwa klasy jest poprawna i car-ui-lib została dodana jako zależność, możesz skompilować plik APK. Aby usunąć błąd, wybierz File > Invalidate Caches (Plik > Unieważnij pamięć podręczną), a następnie kliknij Invalidate and Restart (Unieważnij i uruchom ponownie).

    Dodać te boty do pokoju „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. Zbuduj i zainstaluj aplikację jak wcześniej.

Zobaczysz teraz CarUiRecyclerView:

CarUiRecyclerView
Rysunek 7. CarUiRecyclerView

Usuwanie paska przewijania za pomocą RRO

Czas trwania: 10 minut

W tym ćwiczeniu pokazujemy, jak za pomocą nakładki zasobów środowiska wykonawczego usunąć pasek przewijania z CarUiRecyclerView.

  1. W nakładce RRO dodaj i zmodyfikuj te pliki:

    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>
    

    Zasób car_ui_scrollbar_enable to zasób car-ui-lib typu logicznego, który określa, czy w CarUiRecyclerView jest obecny zoptymalizowany pod kątem samochodów pasek przewijania z przyciskami w górę i w dół. Gdy ta opcja jest ustawiona na false,CarUiRecyclerView działa jak 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>
    

Zbuduj i zainstaluj aplikację jak wcześniej. Pasek przewijania został usunięty z CarUiRecyclerView:

CarUiRecyclerView bez paska przewijania
Rysunek 8. CarUiRecyclerView bez paska przewijania

Użyj układu, aby nałożyć pasek przewijania CarUiRecyclerView

Czas trwania: 15 minut

W tym ćwiczeniu zmodyfikujesz układ paska przewijania CarUiRecyclerView.

  1. Dodaj i zmodyfikuj te pliki w aplikacji 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>
    

    Aby nałożyć plik układu, musisz dodać wszystkie identyfikatory i atrybuty przestrzeni nazw do elementu overlay.xml w nakładce RRO. Zobacz pliki poniżej.

    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>
    

    Warto sprawdzić, jak te pliki ze sobą współpracują.

    Dla uproszczenia wymiary i kolory są zakodowane na stałe. Zalecamy jednak deklarowanie tych wartości w dimens.xmlcolors.xml lub nawet oznaczanie ich jako plików kolorów w folderze res/color/. Więcej informacji znajdziesz w artykule AOSP Java code style for contributors (w języku angielskim).

  2. Zbuduj i zainstaluj aplikację jak wcześniej. Masz już CarUiRecyclerView z niebieskim paskiem przewijania i szarymi szynami.

Gratulacje! Obie strzałki pojawiają się u dołu paska przewijania. RRO został zastosowany do pliku zasobu układu car-ui-lib za pomocą systemu kompilacji Gradle w Androidzie Studio.

CarUiRecyclerView z niebieskim paskiem przewijania i szarymi prowadnicami
Rysunek 9. CarUiRecyclerView z niebieskim paskiem przewijania i szarymi prowadnicami.

Elementy listy RRO

Czas trwania: 15 minut

Do tej pory stosowano RRO do car-ui-lib komponentów za pomocą komponentów platformy (nie AndroidX). Aby używać komponentów AndroidX w RRO, musisz dodać zależności tego komponentu zarówno do aplikacji, jak i do RRO.build.gradle. Musisz też dodać attrs tego komponentu do overlayable.xml w aplikacji, a także sample_overlay.xml w RRO.

Nasza biblioteka (car-ui-lib) korzysta z ConstraintLayout oraz innych komponentów AndroidX, więc jej overlayable.xml może wyglądać tak:

<?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. Zmień układ elementów listy w CarUiRecyclerView za pomocą ConstraintLayout. Dodaj lub zmień te pliki w 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 odwołuje się do kilku komponentów lub zasobów, które nie są uwzględnione jako zależności aplikacji. Są to zasoby car-ui-lib. Aby to zrobić, dodaj car-ui-lib jako zależność do aplikacji RRO w 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'
    }
    

Tytuł i treść są teraz wyrównane do prawej, a nie do lewej.

Tytuł i treść wyrównane do prawej
Rysunek 10. Tytuł i treść wyrównane do prawej

RRO zastosowaliśmy tylko do car-ui-lib przy użyciu komponentów AndroidXConstraintLayout, gdy jego atrybuty były obecne w car-ui-lib pliku o nazwie overlayable.xml, a także w RRO sample_overlay.xml. Podobną funkcję możesz dodać do swojej aplikacji. Wystarczy, że dodasz wszystkie odpowiednie attrs do overlayable.xml aplikacji, podobnie jak w przypadku car-ui-lib.

Nie można jednak zastosować RRO do aplikacji korzystającej z komponentów AndroidX, jeśli w build.gradle (gdy aplikacja korzysta z komponentów car-ui-lib) ma ona car-ui-lib jako zależność. Mapowania atrybutów zostały już zdefiniowane w car-ui-lib biblioteki overlayable.xml, więc dodanie ich do overlayable.xml aplikacji z car-ui-lib jako zależności spowoduje błąd mergeDebugResources, taki jak poniżej. Dzieje się tak, ponieważ te atrybuty występują w wielu plikach overlayable.xml:

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