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

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

أهداف الدورة التعليمية

كيفية:

  • تضمين مكوّنات car-ui-lib في تطبيق Android
  • استخدام Gradle لإنشاء تطبيقات Android وتراكبات الموارد في وقت التشغيل
  • استخدام تراكبات الموارد في وقت التشغيل مع 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 من خلال Gradle.

    الزرّ &quot;تشغيل&quot;

يجب أن يفتح التطبيق الجديد تلقائيًا على المحاكي أو جهاز 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 دقيقة

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

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

للتحكّم في الموارد التي يغطيها حزمة تراكبات الموارد في وقت التشغيل، أضِف ملفًا باسم overlayable.xml إلى مجلد /res في تطبيقك. يعمل هذا الملف كوحدة تحكّم في الأذونات بين تطبيقك (الهدف) وحزمة تراكبات الموارد في وقت التشغيل (التراكب).

  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 قابلة للتراكب من خلال تراكبات الموارد في وقت التشغيل، أدرِج اسم المورد في ملف overlayable.xml الخاص بالتطبيق.

    يجب أن يكون ملف overlayable.xml في res/values/. إذا لم يكن كذلك، لا يمكن لـ OverlayManagerService العثور عليه.

    لمزيد من المعلومات حول الموارد القابلة للتراكب وكيفية ضبطها، يُرجى الاطّلاع على مقالة تقييد الموارد القابلة للتراكب.

إنشاء حزمة تراكبات الموارد في وقت التشغيل

في هذا القسم، ستنشئ حزمة تراكبات الموارد في وقت التشغيل لتغيير السلسلة المعروضة أعلاه من "Hello World!" إلى "Hello World RRO".

  1. لإنشاء مشروع جديد، انقر على ملف > جديد > مشروع جديد. احرص على اختيار بدون نشاط بدلاً من "نشاط فارغ" لأنّ حزم تراكبات الموارد في وقت التشغيل لا تحتوي إلا على موارد.

    ستظهر الإعدادات بشكل مشابه لتلك الموضّحة أدناه. قد يختلف الموقع الذي يتم حفظها فيه:

  2. بعد إنشاء مشروع CarUiRRO الجديد، عليك الإعلان عن المشروع على أنّه تراكب موارد في وقت التشغيل من خلال تعديل 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 أسماء الموارد من الحزمة المستهدَفة بحزمة تراكبات الموارد في وقت التشغيل. يجب ضبط العلامة hasCode على false لحزم تراكبات الموارد في وقت التشغيل. بالإضافة إلى ذلك، لا يُسمح لحزم تراكبات الموارد في وقت التشغيل باحتواء ملفات 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. لإنشاء هدف تراكب الموارد في وقت التشغيل، انقر على الزر الأخضر تشغيل لإنشاء إصدار Gradle من تراكب الموارد في وقت التشغيل على المحاكي أو جهاز Android.

  6. للتحقّق من تثبيت تراكب الموارد في وقت التشغيل بشكل صحيح، شغِّل ما يلي:

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

    يعرض هذا الأمر معلومات مفيدة حول حالة حزم تراكبات الموارد في وقت التشغيل على النظام.

    • [ ] يشير إلى أنّ تراكب الموارد في وقت التشغيل مثبَّت وجاهز للتفعيل.
    • --- يشير إلى أنّ تراكب الموارد في وقت التشغيل مثبَّت ولكنّه يحتوي على أخطاء.
    • [X] يعني أنّ تراكب الموارد في وقت التشغيل مثبَّت ومفعَّل.

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

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

تراكب الموارد في وقت التشغيل (RRO) &quot;مرحبًا بالعالم&quot;
الشكل 4: Hello World RRO!

تهانينا! لقد أنشأت أول تراكب موارد في وقت التشغيل.

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

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

لمزيد من المعلومات حول هاتَين العلامتَين، يُرجى الاطّلاع على مقالتَي إنشاء الحزمة و AAPT2.

تعديل مكوّنات car-ui-lib باستخدام تراكبات الموارد في وقت التشغيل في تطبيق Android

توضّح هذه الصفحة كيفية استخدام تراكب موارد في وقت التشغيل لتعديل المكوّنات من مكتبة car-ui-lib في تطبيق Android.

ضبط لون خلفية شريط الأدوات

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

لتغيير لون خلفية شريط الأدوات:

  1. أضِف القيمة التالية إلى تطبيق تراكب الموارد في وقت التشغيل، واضبط المورد على اللون الأخضر الفاتح (#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 لتراكب الموارد في وقت التشغيل، عدِّل targetName للإشارة إلى car-ui-lib:

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

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

  3. أنشئ تراكب الموارد في وقت التشغيل وتحقَّق منه وثبِّته وفعِّله بالطريقة نفسها كما فعلت سابقًا.

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

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

    <?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 في تراكب الموارد في وقت التشغيل:

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

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

تراكب الموارد في الوقت الفعلي (RRO) لبرنامج Hello World
الشكل 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

استخدام تراكب موارد في وقت التشغيل لإزالة شريط التمرير

المدة: 10 دقائق

يوضّح لك هذا التمرين كيفية استخدام تراكب موارد في وقت التشغيل لإزالة شريط التمرير من CarUiRecyclerView.

  1. في تراكب الموارد في وقت التشغيل، أضِف الملفات التالية وعدِّلها:

    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. أضِف الملفات التالية وعدِّلها في تطبيق تراكب الموارد في وقت التشغيل.

    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 في تراكب الموارد في وقت التشغيل. يُرجى الاطّلاع على الملفات أدناه.

    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 مع شريط تمرير أزرق وقضبان رمادية.

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

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

عناصر قائمة تراكبات الموارد في وقت التشغيل

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

حتى الآن، طبّقت تراكب موارد في وقت التشغيل على مكوّنات car-ui-lib باستخدام مكوّنات إطار العمل (وليس AndroidX). لاستخدام مكوّنات AndroidX في تراكب موارد في وقت التشغيل، يجب إضافة تبعيات هذا المكوّن إلى كل من التطبيق وملف تراكب الموارد في وقت التشغيل build.gradle. يجب أيضًا إضافة attrs لهذا المكوّن إلى overlayable.xml في تطبيقك، بالإضافة إلى sample_overlay.xml في تراكب الموارد في وقت التشغيل.

تستخدم مكتبتنا (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. أضِف الملفات التالية أو عدِّلها في تراكب الموارد في وقت التشغيل:

    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 كملحق لتطبيق تراكب الموارد في وقت التشغيل في 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. العنوان والنص بمحاذاة إلى اليمين

لم نطبّق تراكب موارد في وقت التشغيل على car-ui-lib إلا باستخدام مكوّنات AndroidX (ConstraintLayout) عندما كانت سماته متوفرة في ملف car-ui-lib باسم overlayable.xml بالإضافة إلى 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'