التنقُّل بالإيماءات والتجربة الشاملة

1. مقدمة

بالنسبة إلى الإصدار 10 من نظام Android أو الإصدارات الأحدث، يتم دعم إيماءات التنقل كوضع جديد. يتيح هذا الإجراء لتطبيقك استخدام الشاشة بأكملها وتقديم تجربة عرض أكثر تفاعلية. عندما يمرِّر المستخدم سريعًا من الحافة السفلية للشاشة، ينقله إلى شاشة Android الرئيسية. عند التمرير سريعًا للداخل من الحواف اليسرى أو اليمنى، يتم نقل المستخدم إلى الشاشة السابقة.

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

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

المعلومات التي ستتعلّمها

  • كيفية استخدام أدوات معالجة الأحداث الداخلية في المشاهدات
  • طريقة استخدام واجهة برمجة تطبيقات استبعاد الإيماءة
  • طريقة عمل الوضع المجسم عندما تكون الإيماءات نشطة

يهدف هذا الدرس التطبيقي إلى جعل تطبيقك متوافقًا مع "إيماءات النظام". يتم تنقيح المفاهيم غير ذات الصلة وكتل الرموز وإتاحة نسخها ولصقها.

ما الذي ستنشئه

يعد Universal Android Music Player (UAMP) مثالًا على تطبيق مشغّل الموسيقى لنظام التشغيل Android المكتوب بلغة Kotlin. ستتمكن من إعداد UAMP للتنقل بالإيماءات.

  • استخدِم الأجزاء الداخلية لإبعاد عناصر التحكّم عن مناطق الإيماءات.
  • يمكنك استخدام واجهة برمجة تطبيقات استبعاد الإيماءة لإيقاف إيماءة الرجوع في بداية ظهور عناصر التحكّم المتعارضة.
  • استخدام الإصدارات لاستكشاف التغييرات في سلوك الوضع المجسم مع ميزة "التنقّل بالإيماءات"

المتطلبات

  • جهاز أو محاكي يعمل بنظام التشغيل Android 10 أو إصدار أحدث
  • استوديو Android

2. نظرة عامة على التطبيق

Universal Android Music Player (UAMP) هو نموذج لتطبيق مشغّل الموسيقى لنظام التشغيل Android مكتوب بلغة Kotlin. ويتوافق مع ميزات تشمل التشغيل في الخلفية، والتعامل مع التركيز على الصوت، ودمج "مساعد Google"، ومنصات متعدّدة، مثل Wear وTV وAuto.

الشكل 1: التدفق في UAMP

يحمّل UAMP كتالوج موسيقى من خادم بعيد ويسمح للمستخدم بتصفح الألبومات والأغاني. ينقر المستخدم على أغنية ويتم تشغيلها من خلال مكبّرات صوت أو سماعات رأس متصلة. التطبيق غير مصمم للعمل مع "إيماءات النظام". لذلك، ستواجه في البداية بعض المشاكل عند تشغيل UAMP على جهاز يعمل بنظام التشغيل Android 10 أو إصدار أحدث.

3- الإعداد

للحصول على نموذج التطبيق، استنسِخ المستودع من GitHub وبدِّل إلى فرع المبتدئ:

$  git clone https://github.com/googlecodelabs/android-gestural-navigation/

ويمكنك بدلاً من ذلك تنزيل المستودع كملف ZIP وفك ضغطه وفتحه في "استوديو Android".

أكمِل الخطوات التالية:

  1. افتح التطبيق وأنشئه في "استوديو Android".
  2. أنشِئ جهازًا افتراضيًا جديدًا واختَر المستوى 29 لواجهة برمجة التطبيقات. بدلاً من ذلك، يمكنك توصيل جهاز حقيقي يعمل بالمستوى 29 من واجهة برمجة التطبيقات أو المستويات الأحدث.
  3. شغِّل التطبيق. تعمل القائمة التي تظهر على تجميع الأغاني فيها ضمن الاختيارات المقترَحة والألبومات.
  4. النقر على الفيديوهات المقترَحة واختيار أغنية من قائمة الأغاني
  5. يبدأ التطبيق تشغيل الأغنية.

تفعيل "التنقّل بالإيماءات"

في حال تشغيل مثيل محاكي جديد بالمستوى 29 لواجهة برمجة التطبيقات، قد لا تكون ميزة "التنقل بالإيماءات" مفعَّلة تلقائيًا. لتفعيل ميزة "التنقل بالإيماءات"، اختَر إعدادات النظام >. النظام > تنقل النظام > التنقُّل بالإيماءات

تشغيل التطبيق باستخدام ميزة "التنقّل بالإيماءات"

إذا شغّلت التطبيق وكان "التنقّل بالإيماءات" مفعّلاً وبدأت تشغيل أغنية، قد تلاحظ أنّ عناصر التحكّم في المشغّل قريبة جدًا من منطقتَي إيماءة "الصفحة الرئيسية" و"رجوع".

4. نفِّذ عملاً شاملاً

ما المقصود بالعرض التقديمي من الحافة إلى الحافة؟

يمكن للتطبيقات التي تعمل بنظام التشغيل Android 10 أو الإصدارات الأحدث تقديم تجربة شاشة شاملة، بغض النظر عمّا إذا تم تفعيل الإيماءات أو الأزرار للتنقّل. لتقديم تجربة شاملة، يجب أن تظهر تطبيقاتك خلف أشرطة التنقّل الشفافة والحالة.

الرسم خلف شريط التنقّل

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

لتغيير لون شريط التنقل وشريط الحالة، نفِّذ الخطوات التالية:

  1. شريط التنقل: افتح res/values-29/styles.xml واضبط navigationBarColor على color/transparent.
  2. شريط الحالة: اضبط statusBarColor على color/transparent بالمثل.

راجِع نموذج الرمز التالي من res/values-29/styles.xml:

<!-- change navigation bar color -->
<item name="android:navigationBarColor">
    @android:color/transparent
</item>

<!-- change status bar color -->
<item name="android:statusBarColor">
    @android:color/transparent
</item>

علامات إذن الوصول إلى واجهة مستخدم النظام

عليك أيضًا ضبط علامات ظهور واجهة مستخدم النظام لتوجيه النظام إلى وضع التطبيق أسفل أشرطة النظام. تتيح واجهات برمجة تطبيقات systemUiVisibility في الفئة View ضبط مجموعة متنوعة من العلامات. نفِّذ الخطوات التالية:

  1. افتح الفئة MainActivity.kt وابحث عن الطريقة onCreate(). الحصول على نسخة افتراضية من fragmentContainer
  2. اضبط ما يلي على content.systemUiVisibility:

راجِع نموذج الرمز التالي من MainActivity.kt:

  val content: FrameLayout = findViewById(R.id.fragmentContainer)
  content.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

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

  1. شغِّل التطبيق وانتقِل إلى شاشة المشغّل واختَر أغنية لتشغيلها.
  2. التحقق من رسم عناصر التحكم في المشغّل أسفل شريط التنقل، مما يجعل من الصعب الوصول إليها:

  1. انتقِل إلى "إعدادات النظام"، ثم ارجع إلى وضع "التنقّل باستخدام ثلاثة أزرار"، ثم ارجع إلى التطبيق.
  2. تحقَّق من أنّ استخدام عناصر التحكّم أكثر صعوبة مع شريط التنقّل الذي يحتوي على ثلاثة أزرار: لاحِظ أنّ الرمز SeekBar مخفي خلف شريط التنقّل وأنّ شريط التنقّل تشغيل/إيقاف مؤقت يغطيه في الغالب.
  3. استكشف وجرِّب بعض الشيء. بعد الانتهاء، انتقِل إلى "إعدادات النظام" وعُد مرة أخرى إلى "التنقل بالإيماءات":

741ef664e9be5e7f.gif

أصبح التطبيق يرسم التفاصيل الآن، ولكن هناك مشاكل متعلقة بسهولة الاستخدام وعناصر تحكُّم في التطبيق بشأن التعارضات والتداخلات ويجب حلّها.

5- داخلية

يعمل الرمز WindowInsets على إعلام التطبيق بالمكان الذي تظهر فيه واجهة مستخدم النظام أعلى المحتوى، بالإضافة إلى مناطق الشاشة التي تحظى بالأولوية "إيماءات النظام" على الإيماءات داخل التطبيق. يتم تمثيل المجموعات الداخلية من خلال الفئة WindowInsets والفئة WindowInsetsCompat في Jetpack. ننصح بشدة باستخدام WindowInsetsCompat للحصول على سلوك متسق على جميع مستويات واجهات برمجة التطبيقات.

العناصر الداخلية للنظام والإضافات الإلزامية للنظام

تعتبر واجهات برمجة التطبيقات الداخلية التالية أكثر أنواع الإدخالات شيوعًا في الاستخدام:

  • الإعدادات الداخلية لنوافذ النظام: تخبرك بمكان عرض واجهة مستخدم النظام فوق تطبيقك. ونناقش كيفية استخدام مساحات داخلية للنظام لنقل عناصر التحكّم بعيدًا عن أشرطة النظام.
  • وحدات إيماءة النظام: تعرض جميع مناطق الإيماءات. يمكن أن تؤدي أي عناصر تحكُّم في التمرير السريع داخل التطبيق في هذه المناطق إلى تشغيل "إيماءات النظام" عن طريق الخطأ.
  • وحدات الإيماءات الإلزامية: هي مجموعة فرعية من مجموعات الإيماءات الخاصة بالنظام ولا يمكن تجاوزها. فهي تخبرك بمناطق الشاشة التي سيكون فيها لسلوك "إيماءات النظام" الأولوية دائمًا على الإيماءات داخل التطبيق.

استخدام المساحات الداخلية لنقل عناصر التحكّم في التطبيقات

والآن بعد أن عرفت المزيد عن واجهات برمجة التطبيقات المضمنة، يمكنك إصلاح عناصر التحكم في التطبيق، كما هو موضح في الخطوات التالية:

  1. يمكنك الحصول على مثيل playerLayout من مثيل الكائن view.
  2. إضافة OnApplyWindowInsetsListener إلى playerView
  3. تحريك العرض بعيدًا عن منطقة الإيماءات: ابحث عن القيمة الداخلية للنظام في الجزء السفلي واعمل على زيادة المساحة المتروكة للعرض بمقدار هذا المقدار. لتعديل المساحة المتروكة في العرض وفقًا لذلك، إلى [القيمة المرتبطة بالمساحة المتروكة السفلية في التطبيق]، أضِف [القيمة المرتبطة بالقيمة الداخلية المضمّنة في النظام].

راجِع نموذج الرمز التالي من NowPlayingFragment.kt:

playerView = view.findViewById(R.id.playerLayout)
playerView.setOnApplyWindowInsetsListener { view, insets ->
   view.updatePadding(
      bottom = insets.systemWindowInsetBottom + view.paddingBottom
   )
   insets
}
  1. شغِّل التطبيق واختَر أغنية. يُرجى ملاحظة أنّه لم يحدث أي تغيير في عناصر التحكّم في المشغّل. إذا أضفت نقطة توقّف وشغّلت التطبيق في عملية تصحيح الأخطاء، سيظهر لك أنّه لا يتم استدعاء المستمع.
  2. لحلّ هذه المشكلة، يُرجى الانتقال إلى FragmentContainerView الذي يعالج هذه المشكلة تلقائيًا. افتح activity_main.xml وغيِّر FrameLayout إلى FragmentContainerView.

راجِع نموذج الرمز التالي من activity_main.xml:

<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/fragmentContainer"
    tools:context="com.example.android.uamp.MainActivity"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
  1. شغِّل التطبيق مرة أخرى وانتقِل إلى شاشة المشغّل. تم إبعاد عناصر تحكّم المشغّل السفلي عن منطقة الإيماءات السفلية.

تعمل عناصر التحكم في التطبيق الآن مع "التنقل بالإيماءات"، لكن عناصر التحكم تتحرك أكثر من المتوقع. يجب حل هذه المشكلة.

الحفاظ على المساحة المتروكة والهوامش الحالية

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

ويرجع ذلك إلى أنّ التطبيق يشغِّل requestApplyInsets() في كل مرة يبدأ فيها النشاط. حتى بدون هذه المكالمة، يمكن إرسال WindowInsets عدة مرات في أي وقت أثناء دورة حياة المشاهدة.

تعمل قيمة InsetListener الحالية على playerView بشكل مثالي في المرة الأولى عند إضافة مبلغ القيمة السفلية الداخلية إلى قيمة المساحة المتروكة السفلية للتطبيق التي تم تعريفها في activity_main.xml. ومع ذلك، ستستمر المكالمات اللاحقة في إضافة القيمة الداخلية السفلية إلى المساحة المتروكة السفلية للعرض الذي تم تعديله مسبقًا.

لحلّ هذه المشكلة، اتّبِع الخطوات التالية:

  1. سجِّل قيمة المساحة المتروكة للعرض الأولي. أنشِئ قيمة جديدة واحتفِظ بقيمة المساحة المتروكة للعرض الأولي التي تبلغ playerView، قبل رمز المستمع مباشرةً.

راجِع نموذج الرمز التالي من NowPlayingFragment.kt:

   val initialPadding = playerView.paddingBottom
  1. استخدِم هذه القيمة الأولية لتعديل المساحة المتروكة السفلية في العرض، ما يتيح لك تجنُّب استخدام قيمة المساحة المتروكة السفلية الحالية للتطبيق.

راجِع نموذج الرمز التالي من NowPlayingFragment.kt:

   playerView.setOnApplyWindowInsetsListener { view, insets ->
            view.updatePadding(bottom = insets.systemWindowInsetBottom + initialPadding)
            insets
        }
  1. شغِّل التطبيق مرة أخرى. تنقَّل بين التطبيقات ثم انتقِل إلى الشاشة الرئيسية. عند إرجاع التطبيق، تكون عناصر تحكم المشغّل في مكانها الصحيح فوق منطقة الإيماءات.

إعادة تصميم عناصر التحكّم في التطبيقات

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

يتيح لك استخدام الأجزاء الداخلية إصلاح تعارضات الإيماءات، ولكن في بعض الأحيان مع تغييرات بسيطة في التصميم، يمكنك تجنب تعارض الإيماءات تمامًا. لإعادة تصميم عناصر التحكم في المشغّل لتجنُّب تعارضات الإيماءات، عليك اتّباع الخطوات التالية:

  1. فتح "fragment_nowplaying.xml" بدِّل إلى عرض التصميم واختَر SeekBar في أسفل الصفحة:

74918dec3926293f.png

  1. التبديل إلى عرض الرمز
  2. لنقل SeekBar إلى أعلى playerLayout، غيِّر layout_constraintTop_toBottomOf في شريط البحث إلى parent.
  3. لفرض قيود على العناصر الأخرى في playerView أسفل SeekBar، عليك تغيير layout_constraintTop_toTopOf من العنصر الرئيسي إلى @+id/seekBar في media_button وtitle وposition.

راجِع نموذج الرمز التالي من fragment_nowplaying.xml:

<androidx.constraintlayout.widget.ConstraintLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:padding="8dp"
   android:layout_gravity="bottom"
   android:background="@drawable/media_overlay_background"
   android:id="@+id/playerLayout">

   <ImageButton
       android:id="@+id/media_button"
       android:layout_width="@dimen/exo_media_button_width"
       android:layout_height="@dimen/exo_media_button_height"
       android:background="?attr/selectableItemBackground"
       android:scaleType="centerInside"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintTop_toTopOf="@+id/seekBar"
       app:srcCompat="@drawable/ic_play_arrow_black_24dp"
       tools:ignore="ContentDescription" />

   <TextView
       android:id="@+id/title"
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:layout_marginStart="@dimen/text_margin"
       android:layout_marginEnd="@dimen/text_margin"
       android:ellipsize="end"
       android:maxLines="1"
       android:textAppearance="@style/TextAppearance.Uamp.Title"
       app:layout_constraintTop_toTopOf="@+id/seekBar"
       app:layout_constraintLeft_toRightOf="@id/media_button"
       app:layout_constraintRight_toLeftOf="@id/position"
       tools:text="Song Title" />

   <TextView
       android:id="@+id/subtitle"
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:layout_marginStart="@dimen/text_margin"
       android:layout_marginEnd="@dimen/text_margin"
       android:ellipsize="end"
       android:maxLines="1"
       android:textAppearance="@style/TextAppearance.Uamp.Subtitle"
       app:layout_constraintTop_toBottomOf="@+id/title"
       app:layout_constraintLeft_toRightOf="@id/media_button"
       app:layout_constraintRight_toLeftOf="@id/position"
       tools:text="Artist" />

   <TextView
       android:id="@+id/position"
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:layout_marginStart="@dimen/text_margin"
       android:layout_marginEnd="@dimen/text_margin"
       android:ellipsize="end"
       android:maxLines="1"
       android:textAppearance="@style/TextAppearance.Uamp.Title"
       app:layout_constraintTop_toTopOf="@+id/seekBar"
       app:layout_constraintRight_toRightOf="parent"
       tools:text="0:00" />

   <TextView
       android:id="@+id/duration"
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:layout_marginStart="@dimen/text_margin"
       android:layout_marginEnd="@dimen/text_margin"
       android:ellipsize="end"
       android:maxLines="1"
       android:textAppearance="@style/TextAppearance.Uamp.Subtitle"
       app:layout_constraintTop_toBottomOf="@id/position"
       app:layout_constraintRight_toRightOf="parent"
       tools:text="0:00" />

   <SeekBar
       android:id="@+id/seekBar"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  1. شغِّل التطبيق وتفاعَل مع المشغّل وشريط التمرير.

تؤدي هذه التغييرات البسيطة في التصميم إلى تحسين التطبيق بشكل كبير.

6- واجهة برمجة تطبيقات استبعاد الإيماءات

يتم إصلاح عناصر التحكّم في المشغّل المتعلّقة بالتعارضات في الإيماءات في منطقة الإيماءات على الشاشة الرئيسية. ويمكن أن تؤدي منطقة إيماءة الرجوع أيضًا إلى التعارض مع عناصر التحكّم في التطبيق. توضّح لقطة الشاشة التالية أنّ شريط تمرير المشغّل يتوفّر حاليًا في منطقتَي إيماءة الجهة اليمنى واليسرى:

e6d98e94dcf83dde.png

يعالج SeekBar تلقائيًا التعارضات بين الإيماءات. ومع ذلك، قد تحتاج إلى استخدام مكونات أخرى في واجهة المستخدم تؤدي إلى حدوث تعارضات في الإيماءات. في هذه الحالات، يمكنك استخدام Gesture Exclusion API لإيقاف إيماءة الرجوع بشكل جزئي.

استخدام واجهة برمجة تطبيقات استبعاد الإيماءة

لإنشاء منطقة استبعاد الإيماءات، يمكنك الاتصال بالرقم setSystemGestureExclusionRects() في العرض مع إضافة قائمة بكائنات rect. يتم ربط عناصر rect هذه بإحداثيات مناطق المستطيلات المستبعدة. يجب أن يتم إجراء هذا الاستدعاء من خلال طريقة العرض onLayout() أو onDraw(). لإجراء ذلك، يُرجى اتّباع الخطوات التالية:

  1. أنشِئ حزمة جديدة باسم "view".
  2. لطلب بيانات من واجهة برمجة التطبيقات هذه، أنشِئ فئة جديدة باسم MySeekBar ووسِّع نطاق AppCompatSeekBar.

راجِع نموذج الرمز التالي من MySeekBar.kt:

class MySeekBar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = android.R.attr.seekBarStyle
) : androidx.appcompat.widget.AppCompatSeekBar(context, attrs, defStyle) {

}
  1. أنشئ طريقة جديدة باسم updateGestureExclusion().

راجِع نموذج الرمز التالي من MySeekBar.kt:

private fun updateGestureExclusion() {

}
  1. أضِف علامة اختيار لتخطّي هذا الطلب في المستوى 28 من واجهة برمجة التطبيقات أو المستويات الأدنى.

راجِع نموذج الرمز التالي من MySeekBar.kt:

private fun updateGestureExclusion() {
        // Skip this call if we're not running on Android 10+
        if (Build.VERSION.SDK_INT < 29) return
}
  1. يجب استبعاد الإبهام من شريط التمرير فقط نظرًا إلى أنّ الحد الأقصى المسموح به لواجهة برمجة تطبيقات استبعاد الإيماءة هو 200 وحدة بكسل مستقلة الكثافة (dp). احصل على نسخة من حدود شريط التمرير وأضِف كل عنصر إلى قائمة قابلة للتغيير.

راجِع نموذج الرمز التالي من MySeekBar.kt:

private val gestureExclusionRects = mutableListOf<Rect>()

private fun updateGestureExclusion() {
    // Skip this call if we're not running on Android 10+
    if (Build.VERSION.SDK_INT < 29) return

    thumb?.also { t ->
        gestureExclusionRects += t.copyBounds()
    }
}
  1. يمكنك الاتصال بـ "systemGestureExclusionRects()" من خلال قوائم "gestureExclusionRects" التي تنشئها.

راجِع نموذج الرمز التالي من MySeekBar.kt:

private val gestureExclusionRects = mutableListOf<Rect>()

private fun updateGestureExclusion() {
    // Skip this call if we're not running on Android 10+
    if (Build.VERSION.SDK_INT < 29) return

    thumb?.also { t ->
        gestureExclusionRects += t.copyBounds()
    }
    // Finally pass our updated list of rectangles to the system
    systemGestureExclusionRects = gestureExclusionRects
}
  1. يمكنك طلب بيانات طريقة updateGestureExclusion() من onDraw() أو onLayout(). إلغاء onDraw() وإضافة مكالمة إلى updateGestureExclusion.

راجِع نموذج الرمز التالي من MySeekBar.kt:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    updateGestureExclusion()
}
  1. يجب تعديل مراجع SeekBar. للبدء، افتح fragment_nowplaying.xml.
  2. تغيير SeekBar إلى com.example.android.uamp.view.MySeekBar

راجِع نموذج الرمز التالي من fragment_nowplaying.xml:

<com.example.android.uamp.view.MySeekBar
    android:id="@+id/seekBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="parent" />
  1. لتعديل مراجع SeekBar في NowPlayingFragment.kt، افتح NowPlayingFragment.kt وغيِّر نوع positionSeekBar إلى MySeekBar. لمطابقة نوع المتغيّر، غيِّر القيم العامة SeekBar لطلب findViewById إلى MySeekBar.

راجِع نموذج الرمز التالي من NowPlayingFragment.kt:

val positionSeekBar: MySeekBar = view.findViewById<MySeekBar>(
     R.id.seekBar
).apply { progress = 0 }
  1. يمكنك تشغيل التطبيق والتفاعل مع "SeekBar". إذا استمرّت التعارضات في الإيماءات، يمكنك تجربة حدود الإبهام وتعديلها في MySeekBar. يجب الحرص على عدم إنشاء منطقة استبعاد الإيماءات تكون أكبر من اللازم، لأنّ ذلك يحدّ من المكالمات المحتملة الأخرى لاستبعاد الإيماءات ويؤدي إلى إنشاء سلوك غير متّسق للمستخدم.

7. تهانينا

تهانينا! لقد تعلمت كيفية تجنب التعارضات وحلها باستخدام إيماءات النظام!

لقد جعلت تطبيقك يستخدم وضع ملء الشاشة عند تمديد الحافة إلى الحافة واستخدام مجموعات داخلية لإبعاد عناصر التحكم في التطبيقات عن مناطق الإيماءات. وتعلّمت أيضًا كيفية إيقاف إيماءة الرجوع في النظام في عناصر التحكّم في التطبيق.

لقد تعرفت الآن على الخطوات الأساسية المطلوبة لكي تعمل تطبيقاتك مع "إيماءات النظام".

مواد إضافية

المستندات المرجعية