Codelab: יצירת RROs עם רכיבי car-ui-lib באמצעות מערכת ה-build של Gradle

אפשר להשתמש בספרייה car-ui-lib כדי להפעיל מידע ובידור באופן עצמאי ברכב ו-IVI). ה-Codelab הזה מציג את car-ui-lib ואיך אפשר להשתמש בשכבות-על של משאבים בזמן ריצה (RRO) כדי להתאים אישית את הרכיבים בספרייה.

מה תלמדו

איך עושים את זה:

  • הכללת רכיבי car-ui-lib באפליקציה שלך ל-Android.
  • ניתן להשתמש ב-Gradle כדי ליצור אפליקציות ל-Android והודעות RRO.
  • אפשר להשתמש ב-RRO עם car-ui-lib.

ה-Codelab הזה לא מפרט איך פועלים ה-RRO. צפייה לשנות את הערך של המשאבים של אפליקציה בזמן הריצה וגם פתרון בעיות בשכבות-על של משאבים בזמן ריצה למידע נוסף.

לפני שמתחילים

דרישות מוקדמות

לפני שמתחילים, חשוב לוודא שיש לכם:

איך יוצרים אפליקציה חדשה ל-Android

משך זמן: 15 דקות

בקטע הזה יוצרים פרויקט Android Studio חדש.

  1. ב-Android Studio, יוצרים אפליקציה באמצעות EmptyActivity.

    יצירת פעילות ריקה
    איור 1.יצירת פעילות ריקה
  2. נותנים לאפליקציה את השם CarUiCodelab ובוחרים את שפת ה-Java. אפשר אם יהיה צורך, גם לבחור מיקום לקובץ. מאשרים את ערכי ברירת המחדל של שאר ההגדרות.

     מתן שם לאפליקציה
    איור 2. נותנים שם לאפליקציה
  3. מחליפים את activity_main.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"
        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>
    

    בלוק הקוד הזה מציג את המחרוזת sample_text, שאינה מוגדרת.

  4. מוסיפים את מחרוזת המשאב sample_text ומגדירים אותה ל-"Hello World! " ב- קובץ strings.xml. כדי לפתוח את הקובץ הזה, בוחרים באפשרות אפליקציה > מקור > ראשי > res > ערכים > string.xml.

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">CarUiCodelab</string>
        <string name="sample_text">Hello World!</string>
    </resources>
    
  5. כדי לבנות את האפליקציה שלכם, לוחצים על לחצן ההפעלה הירוק בפינה השמאלית העליונה. אם עושים את זה יתקין את ה-APK באופן אוטומטי לאמולטור או למכשיר Android שלך באמצעות גרדה.

    לחצן ההפעלה

האפליקציה החדשה אמורה להיפתח באופן אוטומטי באמולטור או במכשיר Android שלכם. אם המיקום לא, צריך לפתוח את האפליקציה CarUiCodelab במרכז האפליקציות, שמותקן עכשיו. כך הוא נראה:

פתיחת אפליקציה חדשה של CarUiCodelab
איור 3. פתיחת אפליקציה חדשה של CarUiCodelab

הוספת car-ui-lib לאפליקציה ל-Android

משך זמן: 15 דקות

הוספה של car-ui-lib לאפליקציה:

  1. כדי להוסיף את התלות car-ui-lib לקובץ build.gradle של הפרויקט, בוחרים באפשרות אפליקציה > 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'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    }
    

שימוש ברכיבים של car-ui-lib באפליקציה ל-Android

עכשיו, אחרי שיש לך car-ui-lib, צריך להוסיף סרגל כלים לאפליקציה.

  1. בקובץ MainActivity.java, מחליפים את ה-method 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. צריך לייבא את ToolbarController:

    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.toolbar.ToolbarController;
    
  3. כדי להשתמש בעיצוב Theme.CarUi.WithToolbar, צריך לבחור app > src > main > AndroidManifest.xml ואז עדכון AndroidManifest.xml כדי להופיע כך:

    <?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. כדי לבנות את האפליקציה, לוחצים על לחצן ההפעלה הירוק כמו קודם.

    יצירת האפליקציה

הוספת RROs לאפליקציה שלך

משך זמן: 30 דקות

אם אתם מכירים את ה-RRO, יש לעבור סעיף, מוסיפים לאפליקציה בקר הרשאות. לחלופין, כדי לקבל מידע בסיסי על RRO, ניתן לעיין שינוי הערך של משאבים של אפליקציה בזמן ריצה

הוספה של בקר הרשאות לאפליקציה

כדי לקבוע לאילו משאבים שכבות-על של חבילת RRO, צריך להוסיף קובץ בשם overlayable.xml לתיקייה /res של האפליקציה. הקובץ הזה משמש כהרשאה בין האפליקציה (היעד) לבין חבילת ה-RRO (שכבת העל).

  1. צריך להוסיף את res/values/overlayable.xml לאפליקציה ולהעתיק את התוכן הבא לקובץ שלך:

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

    מכיוון שהמחרוזת sample_text חייבת להיות ניתנת לשכבת-על באמצעות RRO, צריך לכלול את שם המשאב בקובץ Sitelinkable.xml של האפליקציה.

    הקובץ overlayable.xml חייב להיות ממוקם ב-res/values/. אם לא, ל-OverlayManagerService אין אפשרות לאתר אותו.

    מידע נוסף על משאבים שניתן ליצור שכבות-על ועל דרכים שבהן מוגדר, ראו הגבלת שכבות-על משאבים.

יצירת חבילת RRO

בקטע הזה, יוצרים חבילת RRO כדי לשנות את המחרוזת שמוצגת למעלה מ"Hello World! " ל-"Hello World RRO".

  1. כדי ליצור פרויקט חדש, בוחרים באפשרות קובץ > חדש > פרויקט חדש. חשוב בחרו באפשרות ללא פעילות במקום באפשרות 'פעילות ריקה', כי חבילות ה-RRO מכילות משאבים בלבד.

    ההגדרות האישיות שלך נראות דומות לאלו שמתוארות למטה. המיקום שבו הם שמורים עשוי להיות שונה:

  2. אחרי יצירת פרויקט CarUiRRO החדש, צריך להצהיר על הפרויקט בתור RRO על ידי שינוי של 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>
    

    פעולה זו יוצרת שגיאה ב-@xml/sample_overlay. resourcesMap קובץ ממפה שמות משאבים מחבילת היעד לחבילת RRO.

  3. מעתיקים את בלוק הקוד הבא אל …/res/xml/sample_overlay.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
    </overlay>
    
  4. הוספה של sample_text אל …/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>
    
    נוצרה גרסת Gradle של RRO
  5. כדי ליצור יעד RRO, לוחצים על לחצן Play הירוק כדי ליצור Gradle או גרסת ה-RRO שלך באמולטור או במכשיר Android.

  6. כדי לוודא שה-RRO שלכם מותקן כמו שצריך, מריצים את:

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

    בפקודה הזו מוצג מידע שימושי על המצב של חבילות ה-RRO במערכת.

    • [ ] מציין שה-RRO מותקן ומוכן להפעלה.
    • --- מציין שה-RRO מותקן אבל מכיל שגיאות.
    • המשמעות של [X] היא שה-RRO מותקן ומופעל.

    אם ה-RRO מכיל שגיאות, יש לעיין פתרון בעיות בשכבות-על של משאבים בסביבת זמן ריצה לפני שתמשיכו.

  7. כדי להפעיל את ה-RRO ולוודא שהיא מופעלת:

    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
    

באפליקציה מוצגת המחרוזת "Hello World RRO".

שלום עולם RRO!
איור 4: שלום עולם RRO!

מזל טוב! יצרת את ה-RRO הראשון שלך.

כשמשתמשים בשגיאות RRO, כדאי להשתמש בכלי לאריזת הנכסים של Android (AAPT2) דגלים --no-resource-deduping ו---no-resource-removal שמתוארים ב אפשרויות קישור. אין צורך להוסיף את הדגלים ב-Codelab הזה, אבל מומלץ להשתמש בהם ב-RRO שלך כדי למנוע הסרה של משאבים (וניפוי באגים). שלך יכול להוסיף אותם לקובץ build.gradle של ה-RRO שלך, באופן הבא:

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

למידע נוסף על הדגלים האלה אפשר לעיין במאמר בונים את החבילה AAPT2.

שינוי רכיבים של car-ui-lib באמצעות מזהי RRO באפליקציה שלך ל-Android

בדף הזה נסביר איך להשתמש בשכבת-על של משאבים בזמן ריצה (RRO) כדי: לשנות רכיבים מהספרייה car-ui-lib באפליקציה ל-Android.

הגדרת צבע הרקע של סרגל הכלים

משך זמן: 15 דקות

כדי לשנות את צבע הרקע של סרגל הכלים:

  1. צריך להוסיף את הערך הבא לאפליקציית RRO, ולהגדיר את המשאב לבהירות ירוק (#0F0):

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

    הספרייה car-ui-lib מכילה משאב בשם car_ui_toolbar_background. כשהמשאב הזה כלול הגדרה אישית של RRO, סרגל הכלים לא משתנה מפני שהערך הלא נכון מטורגט.

  2. בAndroidManifest.xml של RRO, צריך לעדכן את targetName כך שיפנה אל car-ui-lib:

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

    צריך ליצור חבילת RRO חדשה לכל חבילת יעד שרוצים להגדיר ב-RRO. לדוגמה, כשיוצרים שכבות-על לשני יעדים שונים, צריך ליצור שתי חבילות APK של שכבת-על.

  3. יוצרים, מאמתים, מתקינים ומפעילים את ה-RRO בדיוק כמו קודם.

האפליקציה שלך נראית כך:

צבע רקע חדש של סרגל הכלים
איור 5: צבע רקע חדש של סרגל הכלים

פריסות וסגנונות של RRO

משך זמן: 15 דקות

בתרגיל הזה תיצרו אפליקציה חדשה שדומה לאפליקציה שבניתם קודם. הזה מאפשרת להציג את הפריסה כשכבת-על. צריך לחזור על השלבים הקודמים, או לשנות את ההגדרות באפליקציה הקיימת שלכם.

  1. חשוב להוסיף את השורות הבאות ל-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. מוודאים שהשדה activity_main.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"
        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. באפליקציה RRO, צריך ליצור res/layout/activity_main.xml ולהוסיף את הבאים:

    <?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. מעדכנים את res/values/styles.xml כדי להוסיף את הסגנון שלנו ל-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. יש לשנות את targetName ב-AndroidManifest.xml כך שיצביע על השם של האפליקציה החדשה שלך:

    …
    android:targetName="CarUiCodelab"
    …
    
  6. מוסיפים את המשאבים לקובץ sample_overlay.xml ב-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. בונים ומתקינים את האפליקציה ומתקינים אותה ב-RRO בדיוק כמו קודם (הפעלה ירוקה של Play ). חשוב להקפיד להפעיל את ה-RRO.

האפליקציה וה-RRO מעובדים באופן הבא. טקסט RRO של Hello World הוא ירוק ממרכזת כפי שמצוין ב-RRO של הפריסה.

שלום עולם RRO
איור 6: שלום עולם RRO

הוספת CarUiRecyclerView לאפליקציה

משך זמן: 15 דקות

הממשק של CarUiRecyclerView מספק ממשקי API כדי לגשת ל-RecyclerView שמותאם אישית באמצעות משאבים של car-ui-lib. לדוגמה, CarUiRecyclerView. בודק דגל בזמן ריצה כדי לקבוע אם סרגל הגלילה צריך להיות מופעל או לא ובוחר את הפריסה המתאימה.

CarUiRecyclerViewContainer
איור 7. CarUiRecyclerViewContainer
  1. כדי להוסיף CarUiRecyclerView, צריך להוסיף אותו לactivity_main.xml ול קובצי MainActivity.java. אתם יכולים ליצור אפליקציה חדשה מאפס או לשנות את האפליקציה הקיימת. אם את משנה את האפליקציה הקיימת, חשוב להסיר משאבים לא מוצהרים מ-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"/>
    

    ייתכן שתופיע השגיאה הבאה, ותוכלו להתעלם ממנה:

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

    כל עוד הכיתה מאויתת נכון והוספת car-ui-lib גם או יחסי תלות, תוכלו ליצור ולהרכיב את ה-APK שלכם. כדי להסיר את השגיאה, בוחרים באפשרות File > (קובץ >) מבטלים את המטמון ואז לוחצים על ביטול והפעלה מחדש.

    הוספת הפריטים הבאים אל 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. יוצרים ומתקינים את האפליקציה כמו קודם.

עכשיו אתם רואים CarUiRecyclerView:

CarUiRecyclerView
איור 7 : CarUiRecyclerView

שימוש ב-RRO כדי להסיר את סרגל הגלילה

משך זמן: 10 דקות

התרגיל הזה מראה איך להשתמש ב-RRO כדי להסיר את סרגל הגלילה CarUiRecyclerView

  1. ב-RRO, מוסיפים ושינוי את הקבצים הבאים:

    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>
    

    המשאב car_ui_scrollbar_enable הוא משאב בוליאני car-ui-lib, שקובעת אם סרגל הגלילה עבר אופטימיזציה באמצעות הלחצנים 'למעלה' ו'למטה' ב-CarUiRecyclerView קיים או לא. כשהערך הוא false, CarUiRecyclerView פועל כמו 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>
    

יוצרים ומתקינים את האפליקציה כמו קודם. סרגל הגלילה הוסר עכשיו מ: CarUiRecyclerView:

CarUiRecyclerView ללא סרגל גלילה
איור 8. CarUiRecyclerView ללא סרגל גלילה

שימוש בפריסה כדי להוסיף שכבת-על לסרגל הגלילה של CarUiRecyclerView

משך זמן: 15 דקות

בתרגיל הזה, תשנו את הפריסה של סרגל הגלילה CarUiRecyclerView.

  1. עליך להוסיף ולשנות את הקבצים הבאים באפליקציית ה-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>
    

    כדי ליצור שכבת-על של קובץ פריסה, צריך להוסיף את כל המזהים ומרחב השמות ל-overlay.xml של ה-RRO שלך. לעיון בקבצים שבהמשך.

    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>
    

    מומלץ לבדוק את האינטראקציה בין הקבצים האלה.

    כדי לשמור על פשטות, המידות והצבעים מפורטות בתוך הקוד. אבל מומלץ היא להצהיר על הערכים האלה ב-dimens.xml וב-colors.xml, או אפילו הוגדרו כקובצי צבע בתיקייה res/color/. למידע נוסף, לראות סגנון קוד AOSP Java לתורמים.

  2. יוצרים ומתקינים את האפליקציה כמו קודם. יצרת את CarUiRecyclerView עם סרגל גלילה כחול ומסילות אפורות.

מזל טוב! שני החיצים מופיעים לאורך החלק התחתון של סרגל הגלילה, החילו בהצלחה RRO על קובץ משאבים לפריסה ב-car-ui-lib באמצעות גרסת ה-build של Gradle במערכת באמצעות Android Studio.

CarUiRecyclerView עם סרגל גלילה כחול ומסילות אפורות
איור 9. CarUiRecyclerView עם סרגל גלילה כחול ומסילות אפורות

פריטי רשימה של RRO

משך זמן: 15 דקות

עד עכשיו, החלתם RRO על רכיבים של car-ui-lib באמצעות framework רכיבים (לא AndroidX). כדי להשתמש ברכיבי AndroidX ב-RRO, צריך להוסיף את יחסי התלות של הרכיב הזה גם באפליקציה וגם ב-RRO build.gradle. חייב להוסיף באפליקציה את ה-attrs של הרכיב הזה ל-overlayable.xml, וגם ל-sample_overlay.xml ב-RRO שלך.

הספרייה שלנו (car-ui-lib) משתמשת ב-ConstraintLayout וגם במכשירים אחרים של AndroidX ולכן ה-overlayable.xml שלו עשוי להיראות כך:

<?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. אפשר לשנות את הפריסה של הפריטים ברשימה בCarUiRecyclerView באמצעות ConstraintLayout. מוסיפים או משנים את הקבצים הבאים ב-RRO:

    res/xml/sample_overlay.xml

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
       <item target="id/car_ui_list_item_touch_interceptor" value="@id/car_ui_list_item_touch_interceptor"/>
       <item target="id/car_ui_list_item_reduced_touch_interceptor" value="@id/car_ui_list_item_reduced_touch_interceptor"/>
       <item target="id/car_ui_list_item_start_guideline" value="@id/car_ui_list_item_start_guideline"/>
       <item target="id/car_ui_list_item_icon_container" value="@id/car_ui_list_item_icon_container"/>
       <item target="id/car_ui_list_item_icon" value="@id/car_ui_list_item_icon"/>
       <item target="id/car_ui_list_item_content_icon" value="@id/car_ui_list_item_content_icon"/>
       <item target="id/car_ui_list_item_avatar_icon" value="@id/car_ui_list_item_avatar_icon"/>
       <item target="id/car_ui_list_item_title" value="@id/car_ui_list_item_title"/>
       <item target="id/car_ui_list_item_body" value="@id/car_ui_list_item_body"/>
       <item target="id/car_ui_list_item_action_container_touch_interceptor" value="@id/car_ui_list_item_action_container_touch_interceptor"/>
       <item target="id/car_ui_list_item_action_container" value="@id/car_ui_list_item_action_container"/>
       <item target="id/car_ui_list_item_action_divider" value="@id/car_ui_list_item_action_divider"/>
       <item target="id/car_ui_list_item_switch_widget" value="@id/car_ui_list_item_switch_widget"/>
       <item target="id/car_ui_list_item_checkbox_widget" value="@id/car_ui_list_item_checkbox_widget"/>
       <item target="id/car_ui_list_item_radio_button_widget" value="@id/car_ui_list_item_radio_button_widget"/>
       <item target="id/car_ui_list_item_supplemental_icon" value="@id/car_ui_list_item_supplemental_icon"/>
       <item target="id/car_ui_list_item_end_guideline" value="@id/car_ui_list_item_end_guideline"/>
       <item target="attr/layout_constraintBottom_toBottomOf" value="@attr/layout_constraintBottom_toBottomOf"/>
       <item target="attr/layout_constraintBottom_toTopOf" value="@attr/layout_constraintBottom_toTopOf"/>
       <item target="attr/layout_constraintEnd_toEndOf" value="@attr/layout_constraintEnd_toEndOf"/>
       <item target="attr/layout_constraintEnd_toStartOf" value="@attr/layout_constraintEnd_toStartOf"/>
       <item target="attr/layout_constraintGuide_begin" value="@attr/layout_constraintGuide_begin"/>
       <item target="attr/layout_constraintGuide_end" value="@attr/layout_constraintGuide_end"/>
       <item target="attr/layout_constraintHorizontal_bias" value="@attr/layout_constraintHorizontal_bias"/>
       <item target="attr/layout_constraintLeft_toLeftOf" value="@attr/layout_constraintLeft_toLeftOf"/>
       <item target="attr/layout_constraintLeft_toRightOf" value="@attr/layout_constraintLeft_toRightOf"/>
       <item target="attr/layout_constraintRight_toLeftOf" value="@attr/layout_constraintRight_toLeftOf"/>
       <item target="attr/layout_constraintRight_toRightOf" value="@attr/layout_constraintRight_toRightOf"/>
       <item target="attr/layout_constraintStart_toEndOf" value="@attr/layout_constraintStart_toEndOf"/>
       <item target="attr/layout_constraintStart_toStartOf" value="@attr/layout_constraintStart_toStartOf"/>
       <item target="attr/layout_constraintTop_toBottomOf" value="@attr/layout_constraintTop_toBottomOf"/>
       <item target="attr/layout_constraintTop_toTopOf" value="@attr/layout_constraintTop_toTopOf"/>
       <item target="attr/layout_goneMarginBottom" value="@attr/layout_goneMarginBottom"/>
       <item target="attr/layout_goneMarginEnd" value="@attr/layout_goneMarginEnd"/>
       <item target="attr/layout_goneMarginLeft" value="@attr/layout_goneMarginLeft"/>
       <item target="attr/layout_goneMarginRight" value="@attr/layout_goneMarginRight"/>
       <item target="attr/layout_goneMarginStart" value="@attr/layout_goneMarginStart"/>
       <item target="attr/layout_goneMarginTop" value="@attr/layout_goneMarginTop"/>
       <item target="attr/layout_constraintVertical_chainStyle" value="@attr/layout_constraintVertical_chainStyle"/>
       <item target="layout/car_ui_list_item" value="@layout/car_ui_list_item"/>
    </overlay>
    

    res/layout/car_ui_list_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:tag="carUiListItem"
        android:minHeight="@dimen/car_ui_list_item_height">
    
        <!-- The following touch interceptor views are sized to encompass the specific sub-sections of
        the list item view to easily control the bounds of a background ripple effects. -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <!-- This touch interceptor does not include the action container -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_reduced_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@id/car_ui_list_item_action_container"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/car_ui_list_item_start_guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="@dimen/car_ui_list_item_start_inset" />
    
        <FrameLayout
            android:id="@+id/car_ui_list_item_icon_container"
            android:layout_width="@dimen/car_ui_list_item_icon_container_width"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@+id/car_ui_list_item_start_guideline"
            app:layout_constraintTop_toTopOf="parent">
    
            <ImageView
                android:id="@+id/car_ui_list_item_icon"
                android:layout_width="@dimen/car_ui_list_item_icon_size"
                android:layout_height="@dimen/car_ui_list_item_icon_size"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_content_icon"
                android:layout_width="@dimen/car_ui_list_item_content_icon_width"
                android:layout_height="@dimen/car_ui_list_item_content_icon_height"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_avatar_icon"
                android:background="@drawable/car_ui_list_item_avatar_icon_outline"
                android:layout_width="@dimen/car_ui_list_item_avatar_icon_width"
                android:layout_height="@dimen/car_ui_list_item_avatar_icon_height"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
        </FrameLayout>
    
        <CarUiTextView
            android:id="@+id/car_ui_list_item_title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/car_ui_list_item_text_start_margin"
            android:singleLine="@bool/car_ui_list_item_single_line_title"
            android:textAppearance="@style/TextAppearance.CarUi.ListItem"
            android:layout_gravity="right"
            android:gravity="right"
            android:textAlignment="viewEnd"
            app:layout_constraintBottom_toTopOf="@+id/car_ui_list_item_body"
            app:layout_constraintEnd_toStartOf="@+id/car_ui_list_item_action_container"
            app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_chainStyle="packed"
            app:layout_goneMarginStart="@dimen/car_ui_list_item_text_no_icon_start_margin" />
        <CarUiTextView
            android:id="@+id/car_ui_list_item_body"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/car_ui_list_item_text_start_margin"
            android:textAppearance="@style/TextAppearance.CarUi.ListItem.Body"
            android:layout_gravity="right"
            android:gravity="right"
            android:textAlignment="viewEnd"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/car_ui_list_item_action_container"
            app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
            app:layout_constraintTop_toBottomOf="@+id/car_ui_list_item_title"
            app:layout_goneMarginStart="@dimen/car_ui_list_item_text_no_icon_start_margin" />
    
        <!-- This touch interceptor is sized and positioned to encompass the action container   -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_action_container_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="@id/car_ui_list_item_action_container"
            app:layout_constraintEnd_toEndOf="@id/car_ui_list_item_action_container"
            app:layout_constraintStart_toStartOf="@id/car_ui_list_item_action_container"
            app:layout_constraintTop_toTopOf="@id/car_ui_list_item_action_container" />
    
        <FrameLayout
            android:id="@+id/car_ui_list_item_action_container"
            android:layout_width="wrap_content"
            android:minWidth="@dimen/car_ui_list_item_icon_container_width"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@+id/car_ui_list_item_end_guideline"
            app:layout_constraintTop_toTopOf="parent">
    
            <View
                android:id="@+id/car_ui_list_item_action_divider"
                android:layout_width="@dimen/car_ui_list_item_action_divider_width"
                android:layout_height="@dimen/car_ui_list_item_action_divider_height"
                android:layout_gravity="start|center_vertical"
                android:background="@drawable/car_ui_list_item_divider" />
    
            <Switch
                android:id="@+id/car_ui_list_item_switch_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <CheckBox
                android:id="@+id/car_ui_list_item_checkbox_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <RadioButton
                android:id="@+id/car_ui_list_item_radio_button_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_supplemental_icon"
                android:layout_width="@dimen/car_ui_list_item_supplemental_icon_size"
                android:layout_height="@dimen/car_ui_list_item_supplemental_icon_size"
                android:layout_gravity="center"
                android:scaleType="fitCenter" />
        </FrameLayout>
    
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/car_ui_list_item_end_guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_end="@dimen/car_ui_list_item_end_inset" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. car_ui_list_item.xml מפנה לכמה רכיבים/ משאבים שלא נכללים כיחסי תלות של האפליקציה. אלו מקורות מידע בנושא car-ui-lib. כדי לתקן את הבעיה, צריך להוסיף את car-ui-lib בתור תלות באפליקציית RRO שלך ב-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'
    }
    

עכשיו הכותרת והגוף מיושרים לימין במקום ליישור לשמאל.

כותרת וגוף מיושרים לימין
איור 10. כותרת וגוף מיושרים לימין

החלנו RRO רק על car-ui-lib באמצעות רכיבי AndroidX (ConstraintLayout) כשהמאפיינים שלו היו קיימים בcar-ui-lib בשם overlayable.xml וגם RRO sample_overlay.xml. זו לבצע פעולה דומה באפליקציה שלכם. פשוט צריך להוסיף את כל attrs לoverlayable.xml של האפליקציה, בדומה ל-car-ui-lib.

אבל לא ניתן לבצע RRO של אפליקציה שמשתמשת ברכיבי AndroidX כאשר לאפליקציה יש car-ui-lib כתלות ב-build.gradle (כשהאפליקציה משתמשת car-ui-lib רכיבים). מאחר שמיפויי המאפיינים כבר הוגדרו את overlayable.xml של הספרייה car-ui-lib, ומוסיפים אותם אל overlayable.xml של האפליקציה עם ה-car-ui-lib כתוצאה כתלות, יגרום שגיאה mergeDebugResources כמו זו למטה. הסיבה לכך היא שהמאפיינים האלה נמצאים בכמה קובצי overlayable.xml:

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