Codelab: Tạo RRO với các thành phần car-ui-lib bằng hệ thống xây dựng Gradle

Sử dụng thư viện car-ui-lib để khởi chạy các hệ thống thông tin giải trí (IVI) tự đồng nhất trên xe. Lớp học lập trình này giới thiệu cho bạn về car-ui-lib và cách bạn có thể sử dụng lớp phủ tài nguyên thời gian chạy (RRO) để tùy chỉnh các thành phần trong thư viện.

Bạn sẽ học được gì

Làm cách nào để:

  • Đưa các thành phần car-ui-lib vào ứng dụng Android của bạn.
  • Sử dụng Gradle để xây dựng ứng dụng Android và RRO.
  • Sử dụng RRO với car-ui-lib .

Lớp học lập trình này không nêu chi tiết cách hoạt động của RRO. Hãy xem Thay đổi giá trị tài nguyên của ứng dụng trong thời gian chạyKhắc phục sự cố lớp phủ tài nguyên thời gian chạy để tìm hiểu thêm.

Trước khi bạn bắt đầu

Điều kiện tiên quyết

Trước khi bắt đầu, hãy đảm bảo bạn có:

Tạo một ứng dụng Android mới

Thời lượng: 15 phút

Trong phần này, bạn tạo một dự án Android Studio mới.

  1. Trong Android Studio, hãy tạo một ứng dụng có EmptyActivity .

    Tạo một hoạt động trống
    Hình 1. Tạo một hoạt động trống
  2. Đặt tên cho ứng dụng CarUiCodelab rồi chọn ngôn ngữ Java. Bạn cũng có thể chọn vị trí tập tin nếu muốn. Chấp nhận các giá trị mặc định cho các cài đặt còn lại.

    Đặt tên cho ứng dụng của bạn
    Hình 2. Đặt tên cho ứng dụng của bạn
  3. Thay thế activity_main.xml bằng khối mã sau:

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

    Khối mã này hiển thị chuỗi sample_text , chuỗi này không được xác định.

  4. Thêm chuỗi tài nguyên sample_text và đặt thành "Xin chào thế giới!" trong tệp strings.xml của bạn. Để mở tệp này, hãy chọn app > src > main > res > value > 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. Để xây dựng ứng dụng của bạn, hãy nhấp vào nút Phát màu xanh lục ở trên cùng bên phải. Làm như vậy sẽ tự động cài đặt apk vào trình mô phỏng hoặc thiết bị Android của bạn thông qua Gradle.

    Nút phát

Ứng dụng mới sẽ tự động mở trên trình mô phỏng hoặc thiết bị Android của bạn. Nếu không, hãy mở ứng dụng CarUiCodelab từ trình khởi chạy ứng dụng hiện đã được cài đặt. Nó xuất hiện như thế này:

Mở ứng dụng CarUiCodelab mới
Hình 3. Mở ứng dụng CarUiCodelab mới

Thêm car-ui-lib vào ứng dụng Android của bạn

Thời lượng: 15 phút

Thêm car-ui-lib vào ứng dụng của bạn:

  1. Để thêm phần phụ thuộc car-ui-lib vào tệp build.gradle của dự án, hãy chọn app > build.gradle . Phần phụ thuộc của bạn sẽ xuất hiện như thế này:

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

Sử dụng các thành phần car-ui-lib trong ứng dụng Android của bạn

Bây giờ bạn đã có car-ui-lib , hãy thêm thanh công cụ vào ứng dụng của bạn.

  1. Trong tệp MainActivity.java của bạn, ghi đè phương thức 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. Đảm bảo nhập ToolbarController :

    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.toolbar.ToolbarController;
    
  3. Để sử dụng chủ đề Theme.CarUi.WithToolbar , hãy chọn app > src > main > AndroidManifest.xml rồi cập nhật AndroidManifest.xml để xuất hiện như sau:

    <?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. Để xây dựng ứng dụng, nhấn nút Play màu xanh lá cây như trước.

    Xây dựng ứng dụng

Thêm RRO vào ứng dụng của bạn

Thời lượng: 30 phút

Nếu bạn đã quen với RRO, hãy chuyển tới phần tiếp theo, Thêm bộ điều khiển quyền vào ứng dụng của bạn . Mặt khác, để tìm hiểu thông tin cơ bản về RRO, hãy xem Thay đổi giá trị tài nguyên của ứng dụng khi chạy .

Thêm bộ điều khiển quyền vào ứng dụng của bạn

Để kiểm soát những tài nguyên nào mà gói RRO xếp chồng lên, hãy thêm tệp có tên overlayable.xml vào thư mục /res của ứng dụng của bạn. Tệp này đóng vai trò là bộ điều khiển quyền giữa ứng dụng của bạn ( mục tiêu ) và gói RRO của bạn ( lớp phủ ).

  1. Thêm res/values/overlayable.xml vào ứng dụng của bạn và sao chép nội dung sau vào tệp của bạn:

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

    Vì chuỗi sample_text phải được RRO có thể phủ lên nên hãy bao gồm tên tài nguyên trong tệp có thể phủ được.xml của ứng dụng.

    Tệp lớp overlayable.xml của bạn PHẢI nằm trong res/values/ . Nếu không, OverlayManagerService không thể định vị được nó.

    Để tìm hiểu thêm về các tài nguyên có thể che phủ và cách cấu hình chúng, hãy xem Hạn chế tài nguyên có thể che phủ .

Tạo gói RRO

Trong phần này, bạn tạo gói RRO để thay đổi chuỗi hiển thị ở trên từ "Hello World!" thành "Xin chào thế giới RRO".

  1. Để tạo một dự án mới, chọn Tệp > Mới > Dự án mới . Đảm bảo chọn Không có hoạt động thay vì Hoạt động trống vì các gói RRO chỉ chứa tài nguyên.

    Cấu hình của bạn xuất hiện tương tự như cấu hình được minh họa bên dưới. Vị trí mà chúng được lưu có thể khác nhau:

  2. Sau khi bạn tạo dự án CarUiRRO mới, hãy khai báo dự án là RRO bằng cách sửa đổi 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>
    

    Việc làm này sẽ tạo ra lỗi với @xml/sample_overlay . Tệp bản đồ tài resourcesMap xạ tên tài nguyên từ gói đích sang gói RRO.

  3. Sao chép khối mã sau vào …/res/xml/sample_overlay.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
    </overlay>
    
  4. Thêm sample_text vào …/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>
    
    Đã tạo bản dựng Gradle của RRO
  5. Để xây dựng mục tiêu RRO của bạn, hãy nhấn nút Phát màu xanh lục để tạo bản dựng Gradle của RRO trên trình mô phỏng hoặc thiết bị Android của bạn.

  6. Để xác minh RRO của bạn đã được cài đặt đúng cách, hãy chạy:

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

    Lệnh này hiển thị thông tin hữu ích về trạng thái của các gói RRO trên hệ thống.

    • [ ] chỉ định rằng RRO đã được cài đặt và sẵn sàng kích hoạt.
    • --- chỉ ra rằng RRO đã được cài đặt nhưng có lỗi.
    • [X] có nghĩa là RRO đã được cài đặt và kích hoạt.

    Nếu RRO của bạn có lỗi, hãy xem Khắc phục sự cố lớp phủ tài nguyên thời gian chạy trước khi bạn tiếp tục.

  7. Để bật RRO và xác minh rằng nó đã được bật:

    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
    

Ứng dụng của bạn hiển thị chuỗi "Xin chào thế giới RRO".

Xin chào RRO thế giới!
Hình 4 : Xin chào thế giới RRO!

Chúc mừng! Bạn đã tạo RRO đầu tiên của mình.

Khi sử dụng RRO, bạn có thể muốn sử dụng cờ Công cụ đóng gói tài sản Android (AAPT2) --no-resource-deduping--no-resource-removal được mô tả trong Tùy chọn liên kết . Bạn không cần phải thêm cờ trong lớp học lập trình này nhưng chúng tôi khuyên bạn nên sử dụng chúng trong RRO của mình để tránh xóa tài nguyên (và gây đau đầu khi gỡ lỗi). Bạn có thể thêm chúng vào tệp build.gradle của RRO như thế này:

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

Để tìm hiểu thêm về các cờ này, hãy xem Xây dựng góiAAPT2 .

Sửa đổi các thành phần car-ui-lib bằng RRO trong ứng dụng Android của bạn

Trang này mô tả cách bạn có thể sử dụng lớp phủ tài nguyên thời gian chạy (RRO) để sửa đổi các thành phần từ thư viện car-ui-lib trong ứng dụng Android của mình.

Đặt màu nền cho thanh công cụ

Thời lượng: 15 phút

Để thay đổi màu nền của thanh công cụ:

  1. Thêm giá trị sau vào ứng dụng RRO của bạn và đặt tài nguyên thành màu xanh lục sáng ( #0F0 ):

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

    Thư viện car-ui-lib chứa tài nguyên có tên car_ui_toolbar_background . Khi tài nguyên này được chứa trong cấu hình của RRO, thanh công cụ không thay đổi do nhắm mục tiêu sai giá trị.

  2. Trong AndroidManifest.xml cho RRO của bạn, hãy cập nhật targetName để trỏ đến car-ui-lib :

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

    Bạn PHẢI tạo gói RRO mới cho từng gói mục tiêu mà bạn muốn RRO. Ví dụ: khi tạo lớp phủ cho hai mục tiêu khác nhau, bạn phải tạo hai gói ứng dụng lớp phủ.

  3. Xây dựng, xác minh, cài đặt và kích hoạt RRO theo cách tương tự như trước.

Ứng dụng của bạn xuất hiện như thế này:

Màu nền Thanh công cụ mới
Hình 5 : Màu nền thanh công cụ mới

Bố cục và kiểu RRO

Thời lượng: 15 phút

Trong bài tập này, bạn xây dựng một ứng dụng mới tương tự như ứng dụng bạn đã tạo trước đó. Ứng dụng này cho phép bố cục được phủ lên. Thực hiện theo các bước tương tự như trước hoặc sửa đổi ứng dụng hiện có của bạn.

  1. Hãy chắc chắn rằng bạn thêm các dòng sau vào 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. Đảm bảo activity_main.xml xuất hiện như sau:

    <?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. Trong ứng dụng RRO của bạn, hãy tạo res/layout/activity_main.xml và thêm thông tin sau:

    <?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. Cập nhật res/values/styles.xml để thêm kiểu của chúng tôi vào 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. Thay đổi targetName trong AndroidManifest.xml để trỏ đến tên ứng dụng mới của bạn:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. Thêm tài nguyên vào tệp sample_overlay.xml trong RRO của bạn:

    <?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. Xây dựng và cài đặt ứng dụng và RRO theo cách tương tự như trước (nút Play màu xanh lá cây). Hãy chắc chắn kích hoạt RRO của bạn.

Ứng dụng và RRO hiển thị như sau. Văn bản Hello World RRO có màu xanh lục và được căn giữa như được chỉ định trong RRO bố cục.

Xin chào thế giới RRO
Hình 6 : Hello World RRO

Thêm CarUiRecyclerView vào ứng dụng của bạn

Thời lượng: 15 phút

Giao diện CarUiRecyclerView cung cấp các API để truy cập RecyclerView được tùy chỉnh thông qua tài nguyên car-ui-lib . Ví dụ: CarUiRecyclerView kiểm tra cờ trong thời gian chạy để xác định xem có nên bật thanh cuộn hay không và chọn bố cục tương ứng.

CarUiRecyclerViewContainer
Hình 7. CarUiRecyclerViewContainer
  1. Để thêm CarUiRecyclerView , hãy thêm nó vào tệp activity_main.xmlMainActivity.java của bạn. Bạn có thể tạo ứng dụng mới từ đầu hoặc sửa đổi ứng dụng hiện có. Nếu bạn sửa đổi ứng dụng hiện có, hãy nhớ xóa các tài nguyên không được khai báo khỏi 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"/>
    

    Lỗi sau có thể xuất hiện mà bạn có thể bỏ qua:

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

    Miễn là lớp của bạn viết đúng chính tả và bạn đã thêm car-ui-lib làm phần phụ thuộc, bạn có thể xây dựng và biên dịch gói ứng dụng của mình. Để xóa lỗi, hãy chọn Tệp > Bộ nhớ đệm không hợp lệ , sau đó nhấp vào Không hợp lệ và khởi động lại.

    Thêm phần sau vào 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. Xây dựng và cài đặt ứng dụng của bạn như trước đây.

Bây giờ bạn thấy CarUiRecyclerView :

CarUiRecyclerXem
Hình 7 : CarUiRecyclerView

Sử dụng RRO để xóa thanh cuộn

Thời lượng: 10 phút

Bài tập này chỉ cho bạn cách sử dụng RRO để xóa thanh cuộn khỏi CarUiRecyclerView .

  1. Trong RRO của bạn, thêm và sửa đổi các tệp sau:

    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>
    

    Tài nguyên car_ui_scrollbar_enable là tài nguyên boolean car-ui-lib , kiểm soát xem thanh cuộn được tối ưu hóa cho ô tô với các nút Lên và Xuống trong CarUiRecyclerView có hiện diện hay không. Khi được đặt thành false , CarUiRecyclerView hoạt động giống như 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>
    

Xây dựng và cài đặt ứng dụng của bạn như trước đây. Thanh cuộn hiện đã bị xóa khỏi CarUiRecyclerView :

CarUiRecyclerView không có thanh cuộn
Hình 8. CarUiRecyclerView không có thanh cuộn

Sử dụng bố cục để phủ thanh cuộn CarUiRecyclerView

Thời lượng: 15 phút

Trong bài tập này, bạn sửa đổi bố cục thanh cuộn CarUiRecyclerView .

  1. Thêm và sửa đổi các tệp sau trong ứng dụng RRO của bạn.

    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>
    

    Để phủ một tệp bố cục, bạn phải thêm tất cả các thuộc tính ID và vùng tên vào tệp overlay.xml của RRO của mình. Xem các tập tin dưới đây.

    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>
    

    Bạn nên kiểm tra cách các tệp này tương tác.

    Để đơn giản, kích thước và màu sắc được mã hóa cứng. Tuy nhiên, cách tốt nhất là khai báo các giá trị này trong dimens.xmlcolors.xml hoặc thậm chí được chỉ định là tệp màu trong thư mục res/color/ . Để tìm hiểu thêm, hãy xem Kiểu mã Java AOSP dành cho người đóng góp .

  2. Xây dựng và cài đặt ứng dụng của bạn như trước đây. Bạn đã xây dựng CarUiRecyclerView với thanh cuộn màu xanh lam và đường ray màu xám.

Chúc mừng! Cả hai mũi tên đều xuất hiện dọc theo cuối thanh cuộn. Bạn đã áp dụng thành công RRO cho tệp tài nguyên bố cục car-ui-lib bằng hệ thống xây dựng Gradle thông qua Android Studio.

CarUiRecyclerView với thanh cuộn màu xanh lam với các đường ray màu xám
Hình 9. CarUiRecyclerView với thanh cuộn màu xanh lam với các đường ray màu xám

Các mục danh sách RRO

Thời lượng: 15 phút

Đến thời điểm này, bạn đã áp dụng RRO cho các thành phần car-ui-lib bằng cách sử dụng các thành phần khung (không phải AndroidX). Để sử dụng các thành phần AndroidX trong RRO, bạn phải thêm các phần phụ thuộc của thành phần đó vào cả ứng dụng và RRO build.gradle. Bạn cũng phải thêm attrs của thành phần đó vào overlayable.xml trong ứng dụng của mình, cũng như sample_overlay.xml trong RRO của bạn.

Thư viện của chúng tôi ( car-ui-lib ) sử dụng ConstraintLayout cũng như các thành phần AndroidX khác, do đó, overlayable.xml của nó có thể trông như thế này:

<?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. Thay đổi bố cục của các mục danh sách trong CarUiRecyclerView bằng cách sử dụng ConstraintLayout . Thêm hoặc sửa đổi các tệp sau trong RRO của bạn:

    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 tham chiếu một số tham chiếu đến một số thành phần/tài nguyên không được đưa vào dưới dạng phần phụ thuộc của ứng dụng. Đây là những tài nguyên car-ui-lib . Bạn có thể khắc phục điều này bằng cách thêm car-ui-lib làm phần phụ thuộc cho ứng dụng RRO của mình trong 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'
    }
    

Tiêu đề và Nội dung bây giờ được căn phải thay vì căn trái.

Tiêu đề và nội dung được căn phải
Hình 10. Tiêu đề và nội dung căn phải

Chúng tôi chỉ áp dụng RRO cho car-ui-lib bằng cách sử dụng các thành phần AndroidX ( ConstraintLayout ) khi các thuộc tính của nó có trong tệp car-ui-lib có tên overlayable.xml cũng như RRO sample_overlay.xml . Bạn có thể làm điều gì đó tương tự trong ứng dụng của riêng mình. Chỉ cần thêm tất cả attrs tương ứng vào overlayable.xml của ứng dụng của bạn, tương tự như car-ui-lib .

Tuy nhiên, không thể RRO một ứng dụng sử dụng các thành phần AndroidX khi ứng dụng đó có car-ui-lib làm phần phụ thuộc trong build.gradle của nó (khi ứng dụng sử dụng các thành phần car-ui-lib ). Vì ánh xạ thuộc tính đã được xác định trong overlayable.xml của thư viện car-ui-lib nên việc thêm chúng vào overlayable.xml của ứng dụng của bạn với car-ui-lib làm phần phụ thuộc sẽ gây ra lỗi mergeDebugResources như bên dưới. Điều này là do các thuộc tính này hiện diện trong nhiều tệp overlayable.xml :

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