Codelab: إنشاء RROs باستخدام مكونات car-ui-lib باستخدام نظام بناء Gradle

استخدم مكتبة car-ui-lib لإطلاق أنظمة المعلومات والترفيه (IVI) المتسقة ذاتيًا داخل السيارة. يقدم لك هذا الدرس التطبيقي حول التعليمات البرمجية car-ui-lib وكيف يمكنك استخدام تراكبات موارد وقت التشغيل (RROs) لتخصيص المكونات في المكتبة.

ما ستتعلمه

كيف:

  • قم بتضمين مكونات car-ui-lib في تطبيق Android الخاص بك.
  • استخدم Gradle لإنشاء تطبيقات Android وRROs.
  • استخدم RROs مع car-ui-lib .

لا يقدم هذا الدرس التطبيقي حول التعليمات البرمجية تفاصيل حول كيفية عمل RROs. راجع تغيير قيمة موارد التطبيق في وقت التشغيل واستكشاف أخطاء تراكبات موارد وقت التشغيل وإصلاحها لمعرفة المزيد.

قبل ان تبدا

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

قبل البدء، تأكد من أن لديك:

  • جهاز كمبيوتر مزود بسطر الأوامر (جهاز Linux أو Mac أو جهاز يعمل بنظام Windows مع نظام Windows الفرعي لنظام التشغيل Linux).

  • بيئة تطوير أندرويد .

  • جهاز Android أو المحاكي المتصل بجهازك. راجع تنزيل مصدر Android وإنشاء Android .

  • المعرفة الأساسية بـ RROs.

إنشاء تطبيق أندرويد جديد

المدة: 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 الخاص بك. لفتح هذا الملف، حدد app > src > main > res > value > strings.xml .

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

    زر التشغيل

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

    قم ببناء التطبيق

أضف RROs إلى تطبيقك

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

إذا كنت معتادًا على RROs، فانتقل إلى القسم التالي، إضافة وحدة تحكم الأذونات إلى تطبيقك . بخلاف ذلك، للتعرف على أساسيات RROs، راجع تغيير قيمة موارد التطبيق في وقت التشغيل .

أضف وحدة تحكم الأذونات إلى تطبيقك

للتحكم في الموارد التي تغطيها حزمة 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.

  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 على النظام.

    • [ ] يشير إلى أن 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 الخاص بك.

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

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

لمعرفة المزيد حول هذه العلامات، راجع إنشاء الحزمة و AAPT2 .

قم بتعديل مكونات car-ui-lib باستخدام RROs في تطبيق 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 وتثبيته بنفس الطريقة السابقة (زر التشغيل الأخضر). تأكد من تمكين RRO الخاص بك.

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

مرحبًا بالعالم RRO
الشكل 6 : Hello World RRO

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

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

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

    أضف ما يلي إلى 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/ . لمعرفة المزيد، راجع نمط كود Java AOSP للمساهمين .

  2. قم ببناء وتثبيت التطبيق الخاص بك كما كان من قبل. لقد قمت بإنشاء CarUiRecyclerView بشريط تمرير أزرق وقضبان رمادية.

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

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

عناصر قائمة RRO

المدة: 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 .

ومع ذلك، ليس من الممكن 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'