درس تطبيقي حول الترميز: إنشاء قوائم تسجيل الموارد (RRO) باستخدام مكونات Car-ui-lib باستخدام نظام إصدار Gradle

استخدِم مكتبة car-ui-lib لتشغيل أنظمة الترفيه والمعلومات (IVI) داخل المركبات ذاتية الاكتفاء. يعرّفك هذا الدرس التطبيقي حول الترميز على car-ui-lib وكيفية استخدام تراكب الموارد أثناء التشغيل (RRO) لتخصيص المكوّنات في المكتبة.

المُعطيات

كيفية إجراء ذلك:

  • أدرِج car-ui-lib مكوّنًا في تطبيق Android.
  • استخدام Gradle لإنشاء تطبيقات Android وتطبيقات RRO
  • استخدِم طلبات إعادة النظر في الموافقة مع car-ui-lib.

لا يوضّح هذا الدليل التعليمي كيفية عمل طلبات الحصول على إذن الوصول إلى مساحة التخزين المؤقت. اطّلِع على مقالتَي تغيير قيمة موارد التطبيق أثناء التشغيل وتحديد المشاكل وحلّها في تراكب موارد وقت التشغيل لمزيد من المعلومات.

قبل البدء

المتطلّبات الأساسية

قبل البدء، تأكَّد من توفّر ما يلي:

إنشاء تطبيق Android جديد

المدة: 15 دقيقة

في هذا القسم، يمكنك إنشاء مشروع جديد في "استوديو Android".

  1. في "استوديو Android"، أنشئ تطبيقًا يتضمّن 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. لفتح هذا الملف، اختَر app > src > main > res > values > strings.xml.

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">CarUiCodelab</string>
        <string name="sample_text">Hello World!</string>
    </resources>
    
  5. لإنشاء تطبيقك، انقر على الزر الأخضر تشغيل في أعلى يسار الصفحة. يؤدي إجراء ذلك إلى تثبيت حزمة APK تلقائيًا على المحاكي أو جهاز Android من خلال IDE.

    زر التشغيل

من المفترض أن يتم فتح التطبيق الجديد تلقائيًا على المحاكي أو جهاز Android. إذا لم يكن الأمر كذلك، افتح تطبيق CarUiCodelab من مشغّل التطبيقات المثبَّت الآن. يظهر على النحو التالي:

فتح تطبيق CarUiCodelab الجديد
الشكل 3. افتح تطبيق CarUiCodelab الجديد
.

إضافة car-ui-lib إلى تطبيق Android

المدة: 15 دقيقة

أضِف car-ui-lib إلى تطبيقك باتّباع الخطوات التالية:

  1. لإضافة الاعتمادية على car-ui-lib إلى ملف build.gradle في مشروعك، اختَر 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'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    }
    

استخدام مكونات car-ui-lib في تطبيق Android

بعد أن أصبح لديك car-ui-lib، أضِف شريط أدوات إلى تطبيقك.

  1. في ملف MainActivity.java، استبدِل طريقة 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. لإنشاء التطبيق، اضغط على الزر الأخضر تشغيل كما في السابق.

    إنشاء التطبيق

إضافة عمليات شراء داخل التطبيق إلى تطبيقك

المدة: 30 دقيقة

إذا كنت على دراية بـ RRO، انتقِل إلى القسم التالي، وهو إضافة وحدة تحكّم في الأذونات إلى تطبيقك. وإذا لم تكن على دراية بـ 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، يجب تضمين اسم المورد في overlayable.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. إنّ ضبط العلامة hasCode على false إلزامي لحِزم RRO. بالإضافة إلى ذلك، لا يُسمح لحِزم RRO بالاحتواء على ملفات DEX.

  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 المستهدف، اضغط على الزر الأخضر تشغيل لإنشاء إصدار Gradle من ملف RRO على المحاكي أو جهاز Android.

  6. للتأكّد من تثبيت RRO بشكل صحيح، يمكنك تنفيذ ما يلي:

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

    يعرض هذا الأمر معلومات مفيدة عن حالة حِزم RRO على النظام.

    • يشير الرمز [ ] إلى أنّ التطبيق المُعدّ للطرح على الرفّ قد تم تثبيته وأصبح جاهزًا للتنشيط.
    • يشير الرمز --- إلى أنّ التطبيق المزوّد برمز التشغيل القابل للإزالة مثبَّت ولكنّه يحتوي على أخطاء.
    • يشير الرمز [X] إلى أنّ التطبيق المزوّد بإذن الوصول إلى مساحة التخزين المؤقت (RRO) مثبَّت ومفعَّل.

    إذا كان ملف RRO يتضمّن أخطاء، اطّلِع على تحديد مشاكل تراكب الموارد أثناء التشغيل وحلّها قبل المتابعة.

  7. لتفعيل ميزة "الاستبدال على مستوى الجهاز" والتأكّد من تفعيلها:

    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".

مرحبًا بكم في ميزة &quot;تراكب الموارد في الوقت الفعلي&quot; (RRO)
الشكل 4: مرحبًا بك في واجهة برمجة التطبيقات لنظام التشغيل Android.

تهانينا! لقد أنشأت أول طلب إعادة نظر في المراجعة.

عند استخدام ملفات RRO، قد تحتاج إلى استخدام علامتَي Android Asset Packaging Tool ‏ (AAPT2) --no-resource-deduping و--no-resource-removal الموضّحتَين في خيارات الربط. ليس من الضروري إضافة العلامات في هذا الدليل التعليمي حول الرموز البرمجية، ولكننا نقترح عليك استخدامها في تطبيقاتك التي تستخدم ميزات الوصول المحدود إلى الموارد لتجنُّب إزالة الموارد (وتجنُّب المشاكل المتعلّقة بتصحيح الأخطاء). يمكنك إضافتها إلى ملف build.gradle الخاص بتطبيقك المتوافق مع مبادرة "متجر التطبيقات في نظام التشغيل Android" على النحو التالي:

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. عندما يكون هذا المورد مضمّنًا في إعدادات عنصر عرض إعلانات ديناميكية على شبكة البحث، لا يتغيّر شريط الأدوات لأنّه يتم استهداف القيمة الخاطئة.

  2. في AndroidManifest.xml لتطبيقك المتوافق مع سياسة RRO، عدِّل targetName ليشير إلى car-ui-lib:

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

    يجب إنشاء حزمة جديدة لإعادة الإصدار والنشر لكل حزمة مستهدَفة تريد إعادة إصدارها ونشرها. على سبيل المثال، عند إنشاء تراكب لهدفَين مختلفَين، عليك إنشاء حِزم apk تراكبَين.

  3. أنشئ رمز RRO وأثبِت صحته وثبِّته وفعِّله بالطريقة نفسها التي استخدمتها في السابق.

سيظهر تطبيقك على النحو التالي:

لون خلفية شريط الأدوات الجديد
الشكل 5: لون خلفية شريط الأدوات الجديد

تنسيقات وتصاميم الإعلانات التي تظهر على الشاشة أثناء التشغيل

المدة: 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 وثبِّتهما بالطريقة نفسها التي استخدمتها سابقًا (الزر الأخضر تشغيل ). تأكَّد من تفعيل ميزة "الوصول المحدود للتطبيقات".

يتم عرض التطبيق ورمز RRO على النحو التالي. نص RRO الخاص بتطبيق Hello World أخضر ومائل ومتوسط كما هو محدّد في ملف RRO الخاص بالتنسيق.

Hello World RRO
الشكل 6: Hello World RRO

إضافة CarUiRecyclerView إلى تطبيقك

المدة: 15 دقيقة

توفّر واجهة CarUiRecyclerView واجهات برمجة تطبيقات للوصول إلى RecyclerView التي تم تخصيصها من خلال موارد car-ui-lib. على سبيل المثال، يتحقق CarUiRecyclerView من علامة أثناء التشغيل لتحديد ما إذا كان يجب تفعيل شريط التمرير أم لا، ويختار 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 وتجميعها. لإزالة الخطأ، اختَر ملف > إلغاء صلاحية ذاكرات التخزين المؤقت ثم انقر على إلغاء الصلاحية وإعادة التشغيل.

    إضافة ما يلي إلى 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

استخدام طلب إعادة النظر في المراجعة لإزالة شريط التمرير

المدة: 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 مثل RecyclerView في AndroidX.

    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/. لمزيد من المعلومات، اطّلِع على أسلوب رمز Java في AOSP للمشاركين.

  2. أنشئ تطبيقك وثبِّته كالمعتاد. لقد أنشأت CarUiRecyclerView باستخدام شريط تمرير أزرق وشريطَي جانب رماديَين.

تهانينا! يظهر كلا السهام في أسفل شريط التمرير، ما يعني أنّك طبّقت بنجاح ملف RRO على ملف موارد تنسيق car-ui-lib باستخدام نظام Gradle لإنشاء التطبيقات من خلال "استوديو Android".

CarUiRecyclerView مع شريط تمرير أزرق مع قضبان رمادية
الشكل 9. CarUiRecyclerView مع شريط تمرير أزرق مع قضبان رمادية

عناصر قائمة "التداخل في الوقت الفعلي"

المدة: 15 دقيقة

حتى هذه المرحلة، تكون قد طبّقت ملف RRO على مكونات car-ui-lib باستخدام مكونات الإطار العمل (وليس 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.

ومع ذلك، لا يمكن إعادة ترخيص تطبيق يستخدم مكونات 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'