Laboratorium programistyczne: Twórz RRO za pomocą komponentów car-ui-lib, korzystając z systemu kompilacji Gradle

Skorzystaj z biblioteki car-ui-lib aby uruchomić spójne, pokładowe systemy informacyjno-rozrywkowe (IVI). W tym laboratorium z programowania zapoznasz się z car-ui-lib i zobaczysz, jak możesz używać nakładek zasobów wykonawczych (RRO) do dostosowywania komponentów w bibliotece.

Czego się dowiesz

Jak:

  • Dołącz komponenty car-ui-lib do swojej aplikacji na Androida.
  • Użyj Gradle do tworzenia aplikacji na Androida i RRO.
  • Użyj RRO z car-ui-lib .

To ćwiczenie z programowania nie opisuje szczegółowo, jak działają RRO. Aby dowiedzieć się więcej, zobacz Zmienianie wartości zasobów aplikacji w czasie wykonywania i Rozwiązywanie problemów z nakładkami zasobów w czasie wykonywania .

Zanim zaczniesz

Warunki wstępne

Zanim zaczniesz, upewnij się, że masz:

Utwórz nową aplikację na Androida

Czas trwania: 15 minut

W tej sekcji utworzysz nowy projekt Android Studio.

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

    Utwórz puste działanie
    Rysunek 1. Utwórz puste działanie
  2. Nazwij aplikację CarUiCodelab , a następnie wybierz język Java. W razie potrzeby możesz także wybrać lokalizację pliku. Zaakceptuj wartości domyślne pozostałych ustawień.

    Nazwij swoją aplikację
    Rysunek 2. Nazwij swoją aplikację
  3. Zastąp plik activity_main.xml następującym 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 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 zbudować aplikację, kliknij zielony przycisk Odtwórz w prawym górnym rogu. Spowoduje to automatyczną instalację aplikacji na emulatorze lub urządzeniu z systemem Android za pośrednictwem Gradle.

    Przycisk odtwarzania

Nowa aplikacja powinna otworzyć się automatycznie na Twoim emulatorze lub urządzeniu z Androidem. Jeśli nie, otwórz aplikację CarUiCodelab w programie uruchamiającym aplikacje, który jest teraz zainstalowany. Wygląda to tak:

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

Dodaj car-ui-lib do swojej aplikacji na Androida

Czas trwania: 15 minut

Dodaj car-ui-lib do swojej aplikacji:

  1. Aby dodać zależność car-ui-lib do pliku build.gradle projektu, wybierz aplikacja > build.gradle . Twoje zależności powinny wyglądać następująco:

    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żyj komponentów car-ui-lib w swojej aplikacji na Androida

Teraz, gdy masz już car-ui-lib , dodaj pasek narzędzi do swojej 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 aplikację > src > main > AndroidManifest.xml , a następnie zaktualizuj AndroidManifest.xml tak, aby wyglądał następująco:

    <?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 zbudować aplikację, naciśnij zielony przycisk Play , tak jak poprzednio.

    Zbuduj aplikację

Dodaj RRO do swojej aplikacji

Czas trwania: 30 minut

Jeśli znasz RRO, przejdź do następnej sekcji, Dodaj kontroler uprawnień do swojej aplikacji . W przeciwnym razie, aby poznać podstawy RRO, zobacz temat Zmiana wartości zasobów aplikacji w czasie wykonywania .

Dodaj kontroler uprawnień do swojej aplikacji

Aby kontrolować, które zasoby są nakładane przez pakiet RRO, dodaj plik o nazwie overlayable.xml do folderu /res aplikacji. Ten plik służy jako kontroler uprawnień między Twoją aplikacją ( celem ) a pakietem RRO ( nakładką ).

  1. Dodaj res/values/overlayable.xml do swojej aplikacji i skopiuj następującą zawartość do swojego pliku:

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

    Ponieważ ciąg sample_text musi nadawać się do nałożenia przez RRO, dołącz nazwę zasobu do pliku overlayable.xml aplikacji.

    Twój plik overlayable.xml MUSI znajdować się w res/values/ . Jeśli nie, OverlayManagerService nie będzie w stanie go zlokalizować.

    Aby dowiedzieć się więcej o zasobach, które można nakładać, oraz o tym, jak można je skonfigurować, zobacz temat Ograniczanie zasobów, które można nakładać .

Utwórz pakiet RRO

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

  1. Aby utworzyć nowy projekt, wybierz Plik > Nowy > Nowy projekt . Pamiętaj, aby wybrać opcję Brak działania zamiast Puste działanie, ponieważ pakiety RRO zawierają tylko zasoby.

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

  2. Po utworzeniu nowego projektu CarUiRRO zadeklaruj projekt 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>
    

    Wykonanie tej czynności powoduje błąd z @xml/sample_overlay . Plik resourcesMap odwzorowuje nazwy zasobów z pakietu docelowego na pakiet RRO.

  3. Skopiuj następujący blok kodu do …/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 wersję Gradle RRO
  5. Aby zbudować swój cel RRO, naciśnij zielony przycisk Odtwórz , aby utworzyć kompilację Gradle swojego RRO na emulatorze lub urządzeniu z systemem Android.

  6. Aby sprawdzić, czy RRO jest poprawnie zainstalowane, 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 zainstalowane i gotowe do aktywacji.
    • --- wskazuje, że RRO jest zainstalowane, ale zawiera błędy.
    • [X] oznacza, że ​​RRO jest zainstalowane i aktywowane.

    Jeśli RRO zawiera błędy, zanim przejdziesz dalej, zobacz Rozwiązywanie problemów z nakładkami zasobów środowiska wykonawczego .

  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
    

Twoja aplikacja wyświetla ciąg „Hello World RRO”.

Witaj świecie RRO!
Rysunek 4 : Witaj świecie RRO!

Gratulacje! Stworzyłeś swoje pierwsze RRO.

Podczas korzystania z RRO możesz użyć flag narzędzia Android Asset Packaging Tool (AAPT2) --no-resource-deduping i --no-resource-removal opisanych w opcjach łącza . Dodawanie flag w tym ćwiczeniu z programowania nie jest konieczne, ale sugerujemy użycie ich w RRO, aby uniknąć usuwania zasobów (i problemów związanych z debugowaniem). Możesz dodać je do pliku build.gradle swojego RRO w następujący sposób:

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

Aby dowiedzieć się więcej na temat tych flag, zobacz Kompilowanie pakietu i AAPT2 .

Modyfikuj komponenty car-ui-lib za pomocą RRO w aplikacji na Androida

Na tej stronie opisano, jak używać nakładki zasobów wykonawczych (RRO) do modyfikowania komponentów z biblioteki car-ui-lib w aplikacji na Androida.

Ustaw kolor tła paska narzędzi

Czas trwania: 15 minut

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

  1. Dodaj następującą wartość do swojej 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ż docelowa jest niewłaściwa wartość.

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

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

    MUSISZ utworzyć nowy pakiet RRO dla każdego pakietu docelowego, który chcesz RRO. Na przykład podczas tworzenia nakładek dla dwóch różnych obiektów docelowych należy utworzyć dwie aplikacje nakładek.

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

Twoja aplikacja wygląda następująco:

Nowy kolor tła paska narzędzi
Rysunek 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 aplikacji utworzonej wcześniej. Ta aplikacja umożliwia nakładanie układu. Wykonaj te same kroki co poprzednio lub zmodyfikuj istniejącą aplikację.

  1. Pamiętaj, aby dodać następujące 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. Upewnij się, że activity_main.xml wygląda następująco:

    <?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 plik res/layout/activity_main.xml i dodaj następujące elementy:

    <?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ń targetName w AndroidManifest.xml , aby wskazywała nazwę nowej aplikacji:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. Dodaj zasoby do pliku sample_overlay.xml w swoim 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 poprzednio (zielony przycisk Odtwórz ). Pamiętaj, aby włączyć RRO.

Aplikacja i RRO renderują się w następujący sposób. Tekst Hello World RRO jest zielony i wyśrodkowany, jak określono w układzie RRO.

Witaj świecie RRO
Rysunek 6 : Hello World RRO

Dodaj CarUiRecyclerView do swojej aplikacji

Czas trwania: 15 minut

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

Kontener CarUiRecyclerView
Rysunek 7. Kontener CarUiRecyclerViewContainer
  1. Aby dodać CarUiRecyclerView , dodaj go do plików activity_main.xml i MainActivity.java . Możesz utworzyć nową aplikację od zera lub zmodyfikować istniejącą aplikację. Jeśli zmodyfikujesz istniejącą aplikację, pamiętaj o usunięciu niezadeklarowanych zasobów z 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ę następujący błąd, który możesz zignorować:

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

    Tak długo, jak twoja klasa jest napisana poprawnie i dodałeś car-ui-lib jako zależność, możesz zbudować i skompilować swoją apk. Aby usunąć błąd, wybierz opcję Plik > Unieważnij pamięć podręczną, a następnie kliknij Unieważnij i uruchom ponownie.

    Dodaj następujący tekst do 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. Kompiluj i instaluj aplikację tak jak poprzednio.

Teraz widzisz CarUiRecyclerView :

Widok CarUiRecycler
Rysunek 7 : CarUiRecyclerView

Użyj RRO, aby usunąć pasek przewijania

Czas trwania: 10 minut

W tym ćwiczeniu pokazano, jak używać RRO do usuwania paska przewijania z CarUiRecyclerView .

  1. W swoim RRO dodaj i zmodyfikuj następujące 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 kontroluje, czy pasek przewijania zoptymalizowany pod kątem samochodu z przyciskami w górę i w dół w CarUiRecyclerView jest obecny, czy nie. Po ustawieniu wartości 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>
    

Kompiluj i instaluj aplikację tak jak poprzednio. Pasek przewijania został teraz 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 następujące pliki w swojej 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 pliku overlay.xml swojego 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>
    

    Sugeruje się sprawdzenie interakcji tych plików.

    Dla uproszczenia wymiary i kolory są zakodowane na stałe. Jednak najlepszą praktyką jest deklarowanie tych wartości w dimens.xml colors.xml lub nawet oznaczanie ich jako plików kolorów w folderze res/color/ . Aby dowiedzieć się więcej, zobacz Styl kodu Java AOSP dla autorów .

  2. Kompiluj i instaluj aplikację tak jak poprzednio. Zbudowałeś CarUiRecyclerView z niebieskim paskiem przewijania i szarymi szynami.

Gratulacje! Obie strzałki pojawiają się na dole paska przewijania. Pomyślnie zastosowałeś RRO do pliku zasobów układu car-ui-lib przy użyciu systemu kompilacji Gradle w Android Studio.

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

Elementy listy RRO

Czas trwania: 15 minut

Do tego momentu zastosowałeś RRO do komponentów car-ui-lib przy użyciu komponentów frameworka (nie AndroidX). Aby używać komponentów AndroidX w RRO, musisz dodać zależności tego komponentu zarówno do aplikacji, jak i do pliku RRO build.gradle. Musisz także dodać attrs tego komponentu do overlayable.xml w swojej aplikacji, a także sample_overlay.xml w swoim RRO.

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

<?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 przy użyciu ConstraintLayout . Dodaj lub zmodyfikuj następujące pliki w swoim 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. plik car_ui_list_item.xml zawiera kilka odniesień do kilku komponentów/zasobów, które nie są uwzględnione jako zależności aplikacji. To są zasoby car-ui-lib . Możesz to naprawić, dodając 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 strony

Zastosowaliśmy RRO do car-ui-lib przy użyciu komponentów AndroidX ( ConstraintLayout ), gdy jego atrybuty były obecne w pliku car-ui-lib o nazwie overlayable.xml , a także RRO sample_overlay.xml . Można zrobić coś podobnego we własnej aplikacji. Po prostu dodaj wszystkie odpowiednie attrs do pliku overlayable.xml swojej aplikacji, podobnie jak w przypadku car-ui-lib .

Nie jest jednak możliwe wykonanie RRO aplikacji przy użyciu komponentów AndroidX, jeśli aplikacja ma zależność car-ui-lib w swoim build.gradle (kiedy aplikacja korzysta z komponentów car-ui-lib ). Ponieważ mapowania atrybutów zostały już zdefiniowane w pliku overlayable.xml biblioteki car-ui-lib , dodanie ich do pliku overlayable.xml aplikacji z car-ui-lib jako zależnością spowodowałoby błąd mergeDebugResources podobny do poniższego. Dzieje się tak, ponieważ te atrybuty są obecne w wielu plikach overlayable.xml :

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