Dùng thư viện car-ui-lib
để chạy tính năng thông tin giải trí tự nhất quán trong xe
(IVI). 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) để tuỳ chỉnh các thành phần trong thư viện.
Kiến thức bạn sẽ học được
Cách:
- Đưa các thành phần
car-ui-lib
vào ứng dụng Android của bạn. - Sử dụng Gradle để tạo các ứ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 trình bày chi tiết về cách hoạt động của RRO. Xem Thay đổi giá trị của tài nguyên của ứng dụng trong thời gian chạy và Khắc phục sự cố về lớp phủ tài nguyên trong thời gian chạy để tìm hiểu thêm.
Trước khi 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ó:
Máy tính có dòng lệnh (máy Linux, máy Mac hoặc máy Windows có Hệ thống con Windows dành cho Linux).
Thiết bị Android hoặc trình mô phỏng kết nối với máy của bạn. Xem Tải nguồn Android xuống và Xây dựng Android.
Kiến thức cơ bản về RRO.
Tạo ứng dụng Android mới
Thời lượng: 15 phút
Ở phần này, bạn sẽ tạo một dự án Android Studio mới.
Trong Android Studio, hãy tạo một ứng dụng bằng
EmptyActivity
.Hình 1.Tạo một Hoạt động trống Đặt tên cho ứng dụng là
CarUiCodelab
, sau đó chọn ngôn ngữ Java. Bạn có thể chọn vị trí tệp nếu muốn. Chấp nhận các giá trị mặc định cho thuộc tính các chế độ cài đặt còn lại.Hình 2. Đặt tên cho ứng dụng 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 sẽ hiện chuỗi
sample_text
, chuỗi này không được xác định.Thêm chuỗi tài nguyên
sample_text
rồi đặt thành "Hello World!" trongstrings.xml
. Để mở tệp này, hãy chọn ứng dụng > src > chính > độ phân giải > giá trị > string.xml.<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">CarUiCodelab</string> <string name="sample_text">Hello World!</string> </resources>
Để tạo ứng dụng, hãy nhấp vào nút Play (Phát) màu xanh lục ở trên cùng bên phải. Đang thực hiện 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.
Ứ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
qua trình chạy ứng dụng (hiện đã được cài đặt).
Ứng dụng sẽ hiển thị như sau:
![Mở ứng dụng CarUiCodelab mới](https://source.android.com/static/docs/automotive/images/codelab_04.png?hl=vi)
Thêm car-ui-lib vào ứng dụng Android
Thời lượng: 15 phút
Thêm car-ui-lib
vào ứng dụng của bạn:
Cách thêm phần phụ thuộc
car-ui-lib
vào tệpbuild.gradle
của dự án: chọn ứng dụng > build.gradle. Các phần phụ thuộc của bạn sẽ có dạng như sau: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
Giờ bạn đã có car-ui-lib
, hãy thêm thanh công cụ vào ứng dụng.
Trong tệp
MainActivity.java
, hãy ghi đè phương thứconCreate
:@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); }
Hãy nhớ nhập
ToolbarController
:import com.android.car.ui.core.CarUi; import com.android.car.ui.toolbar.ToolbarController;
Để sử dụng giao diện
Theme.CarUi.WithToolbar
, hãy chọn ứng dụng > src > chính > AndroidManifest.xml rồi cập nhậtAndroidManifest.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>
Để tạo ứng dụng, hãy nhấn nút Play (Phát) màu xanh lục như trước đây.
Thêm RRO vào ứng dụng của bạn
Thời lượng: 30 phút
Nếu bạn đã quen thuộc với RRO, hãy chuyển đến phần tiếp theo Thêm trình kiểm soát quyền vào ứng dụng của bạn. Nếu không, để 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 trong thời gian chạy.
Thêm trình kiểm soát quyền vào ứng dụng
Để kiểm soát những tài nguyên mà lớp phủ gói RRO, hãy thêm một tệp có tên
overlayable.xml
vào thư mục /res
của ứng dụng. Tệp này đóng vai trò là quyền
đơn vị kiểm soát giữa ứng dụng (mục tiêu) và gói RRO của bạn (lớp phủ).
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ó thể phủ lên được bằng RRO, hãy thêm tham số tên tài nguyên trong lớp Layerable.xml của ứng dụng.Tệp
overlayable.xml
của bạn PHẢI nằm trongres/values/
. Nếu không,OverlayManagerService
không tìm được thiết bị này.Để tìm hiểu thêm về tài nguyên có thể phủ và cách chúng có thể đã định cấu hình, hãy xem phần Hạn chế có thể phủ .
Tạo gói RRO
Trong phần này, bạn sẽ tạo một gói RRO để thay đổi chuỗi hiển thị ở trên từ "Hello World!" thành "Hello World RRO".
Để tạo một dự án mới, hãy chọn Tệp > Mới > Dự án mới. Hãy nhớ chọn No Activity (Không có hoạt động) thay vì Empty Activity (Hoạt động trống) vì các gói RRO chứa tài nguyên.
Cấu hình của bạn xuất hiện tương tự như các cấu hình được minh hoạ bên dưới. Chiến lược phát hành đĩa đơn vị trí mà các tệp này được lưu có thể khác nhau:
Sau khi bạn tạo dự án
CarUiRRO
mới, hãy khai báo dự án dưới dạng một RRO bằng cách sửa đổiAndroidManifest.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>
Thao tác này sẽ tạo ra lỗi với
@xml/sample_overlay
.resourcesMap
tệp ánh xạ tên tài nguyên từ gói mục tiêu đến gói RRO.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>
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 mục tiêu RRO, hãy nhấn nút Play (Phát) màu xanh lục để tạo Gradle bản dựng của RRO trên trình mô phỏng hoặc thiết bị Android của bạn.
Để 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ông tin hữu ích về trạng thái của các gói RRO trên hệ thống.
[ ]
chỉ định RRO đã được cài đặt và sẵn sàng để kích hoạt.---
cho biết 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ố về lớp phủ tài nguyên trong thời gian chạy trước khi tiếp tục.
Cách bật RRO và xác minh rằng RRO đã đượ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 chuỗi "Hello World RRO".
![Chào World RRO!](https://source.android.com/static/docs/automotive/images/codelab_09.png?hl=vi)
Xin chúc mừng! Bạn đã tạo RRO đầu tiên.
Khi sử dụng RRO, bạn có thể muốn sử dụng Công cụ đóng gói tài nguyên Android (AAPT2)
cờ --no-resource-deduping
và --no-resource-removal
được mô tả trong
Lựa chọn về đường liên kết.
Bạn không cần thêm cờ vào lớp học lập trình này, nhưng bạn nên sử dụng
trong RRO của bạn để tránh việc xoá tài nguyên (và gỡ lỗi khó khăn). Bạn
có thể thêm chúng vào tệp build.gradle
của RRO như sau:
android {
…
aaptOptions {
additionalParameters "--no-resource-deduping", "--no-resource-removal"
}
}
Để tìm hiểu thêm về những cờ này, hãy xem Xây dựng gói và AAPT2.
Sửa đổi các thành phần car-ui-lib
bằng RRO trong ứng dụng Android
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.
Đặt màu nền của thanh công cụ
Thời lượng: 15 phút
Cách thay đổi màu nền của thanh công cụ:
Thêm giá trị sau vào ứng dụng RRO và đặt tài nguyên thành sáng xanh lục (
#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 một tài nguyên có têncar_ui_toolbar_background
Khi tài nguyên này được đưa vào trong cấu hình của một RRO, thanh công cụ không thay đổi vì giá trị không chính xác được nhắm mục tiêu.Trong
AndroidManifest.xml
cho RRO của bạn, hãy cập nhậttargetName
để trỏ đếncar-ui-lib
:… android:targetName="car-ui-lib" …
Bạn PHẢI tạo một 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 tệp APK lớp phủ.
Tạo, xác minh, cài đặt và bật RRO theo cách tương tự như trước đây.
Ứng dụng của bạn sẽ có dạng như sau:
![Màu nền mới của Thanh công cụ](https://source.android.com/static/docs/automotive/images/codelab_10.png?hl=vi)
Bố cục và kiểu RRO
Thời lượng: 15 phút
Trong bài tập này, bạn sẽ tạo một ứng dụng mới tương tự như ứng dụng mà bạn đã tạo trước đó. Chiến dịch này cho phép bố cục được phủ lên trên. Làm theo các bước tương tự như trước hoặc sửa đổi ứng dụng hiện tại của bạn.
Hãy nhớ 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>
Đả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>
Trong ứng dụng RRO, hãy tạo một
res/layout/activity_main.xml
rồi thêm tham số 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>
Cập nhật
res/values/styles.xml
để thêm kiểu 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>
Thay đổi
targetName
trongAndroidManifest.xml
để trỏ đến tên của ứng dụng mới của bạn:… android:targetName="CarUiCodelab" …
Thêm tài nguyên vào tệp
sample_overlay.xml
trong 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>
Tạo và cài đặt ứng dụng và RRO theo cách tương tự như trước (màu xanh lục Play ). Hãy nhớ bật RRO của bạn.
Ứng dụng và RRO hiển thị như sau. Văn bản RRO Hello World có màu xanh lục và căn giữa như chỉ định trong RRO bố cục.
![Hello World RRO](https://source.android.com/static/docs/automotive/images/codelab_11.png?hl=vi)
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 vào RecyclerView
được tuỳ chỉnh thông qua các tài nguyên car-ui-lib
. Ví dụ: CarUiRecyclerView
kiểm tra một cờ trong thời gian chạy để xác định xem có nên bật thanh cuộn hay không
rồi chọn bố cục tương ứng.
![Vùng chứa CarUiRecyclerView](https://source.android.com/static/docs/automotive/images/codelab_12.png?hl=vi)
Để thêm
CarUiRecyclerView
, hãy thêm nó vàoactivity_main.xml
của bạn vàMainActivity.java
tệp. Bạn có thể tạo một ứng dụng mới từ đầu hoặc sửa đổi ứng dụng hiện tại. Nếu bạn sửa đổi ứng dụng hiện có, hãy nhớ xoá tài nguyên chưa được khai báo khỏioverlayable.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 đây có thể xuất hiện. Bạn có thể bỏ qua lỗi này:
Cannot resolve class com.android.car.ui.recyclerview.CarUiRecyclerView
Miễn là lớp học của bạn đúng chính tả và bạn đã thêm
car-ui-lib
làm phần phụ thuộc, bạn có thể tạo và biên dịch tệp APK. Cách xoá lỗi: chọn Tệp > Vô hiệu hoá bộ nhớ đệm, sau đó nhấp vào Vô hiệu hoá và khởi động lại.Thêm đoạn mã 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; }
Tạo bản dựng và cài đặt ứng dụng của bạn như trước đây.
Giờ đây, bạn sẽ thấy một CarUiRecyclerView
:
![CarUiRecyclerView](https://source.android.com/static/docs/automotive/images/codelab_13.png?hl=vi)
Dùng RRO để loại bỏ thanh cuộn
Thời lượng: 10 phút
Bài tập này hướng dẫn bạn cách sử dụng RRO để xoá thanh cuộn
CarUiRecyclerView
.
Trong RRO, hãy 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à một tài nguyên booleancar-ui-lib
, nút này kiểm soát việc thanh cuộn được tối ưu hoá cho ô tô có các nút Lên và Xuống hay không trongCarUiRecyclerView
có tồn tại hay không. Khi đặt thànhfalse
,CarUiRecyclerView
hoạt động như một AndroidXRecyclerView
.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>
Tạo bản dựng và cài đặt ứng dụng của bạn như trước đây. Thanh cuộn hiện đã bị xoá khỏi
CarUiRecyclerView
:
![CarUiRecyclerView không có thanh cuộn](https://source.android.com/static/docs/automotive/images/codelab_14.png?hl=vi)
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ẽ sửa đổi bố cục thanh cuộn CarUiRecyclerView
.
Thêm và sửa đổi các tệp sau trong ứng dụng 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>
Để phủ một tệp bố cục, bạn phải thêm tất cả mã nhận dạng và không gian tên vào
overlay.xml
của RRO của bạn. Xem các tệp bên dưới.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 cố định giá trị trong mã. Tuy nhiên, tốt nhất bạn nên khai báo các giá trị này trong
dimens.xml
vàcolors.xml
hoặc ngay cả khi được chỉ định dưới dạng tệp màu trong thư mụcres/color/
. Để tìm hiểu thêm, xem Kiểu mã Java AOSP cho người đóng góp.Tạo bản dựng và cài đặt ứng dụng của bạn như trước đây. Bạn đã xây dựng
CarUiRecyclerView
có thanh cuộn màu xanh dương và thanh dọc màu xám.
Xin chúc mừng! Cả hai mũi tên đều xuất hiện dọc theo phía dưới cùng của thanh cuộn. Bạn đã
đã áp dụng thành công một RRO cho tệp tài nguyên bố cục car-ui-lib
bằng bản dựng Gradle
thông qua Android Studio.
![CarUiRecyclerView có thanh cuộn màu xanh dương với các dải màu xám](https://source.android.com/static/docs/automotive/images/codelab_15.png?hl=vi)
Các mục trong danh sách RRO
Thời lượng: 15 phút
Đến nay, bạn đã áp dụng RRO cho các thành phần car-ui-lib
bằng khung
thành phần (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 đó đối với cả ứng dụng lẫn RRO build.gradle.
Bạn
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
dưới dạng 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 AndroidX khác
thành phần nên overlayable.xml
của nó sẽ có dạng như sau:
<?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>
Thay đổi bố cục của các mục danh sách trong
CarUiRecyclerView
bằng cách sử dụngConstraintLayout
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>
car_ui_list_item.xml
tham chiếu một số tham chiếu đến một số thành phần/ các 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à các tài nguyêncar-ui-lib
. Bạn có thể khắc phục vấn đề này bằng cách thêmcar-ui-lib
làm phần phụ thuộc vào ứng dụng RRO của bạn trongapp/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 hiện được căn phải thay vì căn trái.
![Tiêu đề và nội dung được căn phải](https://source.android.com/static/docs/automotive/images/codelab_16.png?hl=vi)
Chúng tôi chỉ áp dụng RRO cho car-ui-lib
bằng các thành phần AndroidX
(ConstraintLayout
) khi các thuộc tính liên quan đã có trong car-ui-lib
tệp có tên overlayable.xml
cũng như RRO sample_overlay.xml
. Bây giờ
có thể làm điều gì đó tương tự trong ứng dụng của mình. Chỉ cần thêm tất cả các
attrs
với overlayable.xml
của ứng dụng, tương tự như car-ui-lib
.
Tuy nhiên, bạn không thể RRO một ứng dụng bằng các thành phần AndroidX khi
ứng dụng có car-ui-lib
là phần phụ thuộc trong build.gradle
(khi ứng dụng sử dụng
car-ui-lib
). Vì việc liên kết thuộc tính đã được xác định trong
overlayable.xml
của thư viện car-ui-lib
, thêm chúng vào
overlayable.xml
của ứng dụng với car-ui-lib
làm phần phụ thuộc sẽ gây ra
mergeDebugResources
lỗi tương tự như dưới đây. Đó là vì các thuộc tính này
có trong nhiều tệp overlayable.xml
:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugResources'