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

Korzystaj z biblioteki car-ui-lib, aby uruchamiać spójne systemy multimedialne w samochodzie (IVI). W tym ćwiczeniu z programowania poznasz car-ui-lib oraz dowiesz się, jak używać nakładek zasobów w czasie wykonywania (RRO) do dostosowywania komponentów w bibliotece.

Czego się nauczysz

Instrukcje:

  • Uwzględnij komponenty car-ui-lib w aplikacji na Androida.
  • Używaj Gradle do kompilowania aplikacji i pakietów RRO na Androida.
  • Używaj RRO w przypadku car-ui-lib.

To ćwiczenie nie opisuje szczegółowo działania RRO. Aby dowiedzieć się więcej, przeczytaj artykuły Zmienianie wartości zasobów aplikacji w czasie wykonywaniaRozwiązywanie problemów z nakładkami zasobów w czasie wykonywania.

Zanim rozpoczniesz

Wymagania wstępne

Zanim zaczniesz, upewnij się, że:

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 pustego działania
    Rysunek 1. Tworzenie pustej aktywności
  2. Nazwij aplikację CarUiCodelab, a następnie 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 znaków zasobu sample_text i ustaw go na „Hello World!” w pliku strings.xml. Aby otworzyć ten plik, wybierz aplikacja > 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 na emulatorze lub 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 poziomu menu z aplikacjami, która jest teraz zainstalowana. Wygląda to tak:

Otwieranie nowej aplikacji 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 aplikacja > build.gradle. Zależność powinna 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'
    }
    

Korzystanie z komponentów z biblioteki car-ui-lib w aplikacji na Androida

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

  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 aplikacja > src > main > AndroidManifest.xml, a następnie zaktualizuj element AndroidManifest.xml, aby wyglądał 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ę, tak jak poprzednio naciśnij zielony przycisk Odtwórz.

    Kompilowanie aplikacji

Dodawanie funkcji RRO do aplikacji

Czas trwania: 30 minut

Jeśli znasz już podstawy RRO, przejdź do następnej sekcji Dodaj kontroler uprawnień do aplikacji. Jeśli nie, zapoznaj się z artykułem Zmiana wartości zasobów aplikacji w czasie wykonywania.

Dodawanie do aplikacji kontrolera uprawnień

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

  1. Dodaj do aplikacji res/values/overlayable.xml i skopiuj do pliku te treści:

    <?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 znaków sample_text musi być nakładany przez RRO, dlatego dodaj nazwę zasobu w pliku overlayable.xml aplikacji.

    Plik overlayable.xml MUSI znajdować się w folderze res/values/. Jeśli nie, OverlayManagerService nie będzie go w stanie znaleźć.

    Więcej informacji o zasobach z możliwością nakładania i o tym, jak je skonfigurować, znajdziesz w artykule Ograniczanie zasobów z możliwością nakładania.

Tworzenie pakietu RRO

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

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

    Twoje konfiguracje wyglądają podobnie do tych przedstawionych poniżej. Miejsce, w którym są one zapisywane, może się różnić:

  2. Po utworzeniu nowego projektu CarUiRRO oznaj go jako RRO, modyfikując plik 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 resourcesMapmapuje nazwy zasobów z docelowego pakietu do pakietu RRO. W przypadku pakietów RRO obowiązkowe jest ustawienie flagi hasCode na false. 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ę RRO w Gradle.
  5. Aby utworzyć wersję docelową RRO, naciśnij zielony przycisk Odtwórz, aby utworzyć wersję Gradle RRO na emulatorze lub urządzeniu z Androidem.

  6. Aby sprawdzić, czy usługa RRO jest prawidłowo zainstalowana, uruchom:

    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 RRO jest zainstalowany, ale zawiera błędy.
    • [X] oznacza, że RRO jest zainstalowany i aktywowany.

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

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

    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świetla ciąg tekstowy „Hello World RRO”.

Witaj, RRO!
Rysunek 4.: Hello World RRO

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

Jeśli używasz RRO, możesz użyć flag narzędzia Android Asset Packaging Tool (AAPT2) --no-resource-deduping i --no-resource-removal opisanych w sekcji Opcje linku. Nie musisz dodawać flag w tym ćwiczeniu z programowania, ale zalecamy używanie ich w Twoich 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 artykułach Tworzenie pakietuAAPT2.

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

Na tej stronie opisano, jak za pomocą nakładki zasobów w czasie wykonywania (RRO) modyfikować komponenty 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 tę wartość do aplikacji RRO i ustaw zasób na jasnozielony (#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. Gdy ten zasób jest zawarty w konfiguracji RRO, pasek narzędzi nie zmienia się, ponieważ jest kierowany na nieprawidłową wartość.

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

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

    W przypadku każdego docelowego pakietu, który chcesz przekazać do RRO, MUSISZ utworzyć nowy pakiet RRO. Jeśli na przykład tworzysz nakładki dla 2 różnych grup docelowych, musisz utworzyć 2 apki nakładki.

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

Aplikacja będzie wyglądać tak:

Nowy kolor tła paska narzędzi
Rysunek 5.: nowy kolor tła paska narzędzi

Układy i style reklam typu RRO

Czas trwania: 15 minut

W tym ćwiczeniu utworzysz nową aplikację podobną do tej, którą utworzyliśmy wcześniej. Ta aplikacja umożliwia nakładanie układu. Wykonaj te same czynności jak poprzednio lub zmodyfikuj już zainstalowaną aplikację.

  1. Dodaj do pliku overlayable.xml te wiersze:

    <?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. Upewnij się, że 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 plik 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 pliku 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. Utwórz i zainstaluj aplikację oraz RRO w taki sam sposób jak wcześniej (zielony przycisk Graj). Pamiętaj, aby włączyć RRO.

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

Hello World RRO
Rysunek 6. Hello World RRO

Dodawanie widoku CarUiRecyclerView do aplikacji

Czas trwania: 15 minut

Interfejs CarUiRecyclerView udostępnia interfejsy API umożliwiające dostęp do RecyclerView, który jest dostosowywany za pomocą zasobów car-ui-lib. Na przykład funkcja CarUiRecyclerViewsprawdza flagę w czasie wykonywania, aby określić, czy pasek przewijania powinien być włączony, i wybrać 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ę, pamiętaj, aby usunąć niezadeklarowane zasoby z elementu 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"/>
    

    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 zapisana poprawnie i dodasz car-ui-lib jako zależność, możesz skompilować plik APK. Aby usunąć błąd, wybierz Plik > Nieważne pamięci podręcznej, a następnie kliknij Nieważne i uruchom ponownie.

    Dodaj te informacje do pliku 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. Utwórz i zainstaluj aplikację jak dotąd.

Teraz zobaczysz CarUiRecyclerView:

CarUiRecyclerView
Rysunek 7. CarUiRecyclerView

Usuwanie paska przewijania za pomocą RRO

Czas trwania: 10 minut

To ćwiczenie pokazuje, jak za pomocą RRO usunąć suwak z poziomuCarUiRecyclerView.

  1. W pliku 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 logiczny car-ui-lib, który określa, czy suwak z przyciskami w górę i w dół zoptymalizowany pod kątem samochodów jest obecny w CarUiRecyclerView. Gdy ustawisz wartość false, CarUiRecyclerView będzie 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>
    

Utwórz i zainstaluj aplikację jak dotąd. Suwak został usunięty z CarUiRecyclerView:

CarUiRecyclerView bez paska przewijania
Rysunek 8. CarUiRecyclerView bez paska przewijania

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

Czas trwania: 15 minut

W tym ćwiczeniu zmodyfikujesz układ suwaka 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 overlay.xml w 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>
    

    Zalecamy sprawdzenie, jak te pliki ze sobą współdziałają.

    Ze względu na prostotę wymiary i kolory są zakodowane na stałe. Najlepszym rozwiązaniem jest jednak zadeklarowanie tych wartości w elementach dimens.xmlcolors.xml lub nawet w plikach kolorów w folderze res/color/. Więcej informacji znajdziesz w stylu kodu Java AOSP dla autorów.

  2. Utwórz i zainstaluj aplikację jak dotąd. Udało Ci się utworzyć CarUiRecyclerViewz niebieskim paskiem przewijania i szarymi szynami.

Gratulacje! Obie strzałki pojawiają się u dołu suwaka. Oznacza to, że RRO został zastosowany do pliku zasobu układu car-ui-lib za pomocą systemu kompilacji Gradle w Android Studio.

CarUiRecyclerView z niebieskim paskiem przewijania z szarymi szynami
Rysunek 9. CarUiRecyclerView z niebieskim paskiem przewijania z szarymi szynami

RRO – elementy listy

Czas trwania: 15 minut

Do tego momentu zastosowałeś/zaaplikowałeś RRO do komponentów car-ui-lib za pomocą komponentów frameworku (a nie AndroidX). Aby używać komponentów AndroidX w pakiecie RRO, musisz dodać zależności tego komponentu zarówno do aplikacji, jak i do pakietu RRO build.gradle.. Musisz też dodać attrs tego komponentu do overlayable.xml w aplikacji oraz sample_overlay.xml w pakiecie 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 sekcji CarUiRecyclerView za pomocą opcji ConstraintLayout. Dodaj lub zmodyfikuj te pliki w pliku 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 naprawić, dodaj car-ui-lib jako zależność aplikacji RRO w pliku 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 tekst są teraz wyrównane do prawej, a nie do lewej.

Wyrównanie tytułu i tekstu do prawej
Rysunek 10. Tytuł i tekst wyrównane do prawej

RRO zastosowaliśmy tylko do car-ui-lib korzystającego z komponentów AndroidX (ConstraintLayout), gdy jego atrybuty były obecne w pliku car-ui-lib o nazwie overlayable.xml, a także w pliku RRO sample_overlay.xml. W swojej aplikacji możesz zrobić coś podobnego. Wystarczy, że dodasz wszystkie odpowiednie attrs do overlayable.xml aplikacji, podobnie jak w przypadku car-ui-lib.

Nie można jednak przekształcać aplikacji korzystającej z komponentów AndroidX, jeśli w jej pliku build.gradle (gdy aplikacja używa komponentów car-ui-lib) występuje zależność car-ui-lib. Mapowania atrybutów zostały już zdefiniowane w bibliotece car-ui-lib (overlayable.xml), więc dodanie ich do overlayable.xml aplikacji z zależnością car-ui-lib spowoduje błąd mergeDebugResources podobny do tego poniżej. Dzieje się tak, ponieważ te atrybuty występują w kilku plikach overlayable.xml:

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