ناوبری اشاره و تجربه لبه به لبه

۱. مقدمه

برای اندروید نسخه ۱۰ یا بالاتر، ژست‌های ناوبری به عنوان یک حالت جدید پشتیبانی می‌شوند. این به برنامه شما اجازه می‌دهد از کل صفحه استفاده کند و تجربه نمایش فراگیرتری را ارائه دهد. وقتی کاربر از لبه پایین صفحه به بالا می‌کشد، به صفحه اصلی اندروید می‌رود. وقتی از لبه‌های چپ یا راست به داخل می‌کشد، به صفحه قبلی می‌رود.

با این دو حرکت، برنامه شما می‌تواند از فضای صفحه نمایش در پایین صفحه استفاده کند. با این حال، اگر برنامه شما از حرکات استفاده می‌کند یا کنترل‌هایی در قسمت‌های حرکات سیستم دارد، ممکن است با حرکات سراسری سیستم تداخل ایجاد کند.

این آزمایشگاه کد قصد دارد به شما آموزش دهد که چگونه از insets برای جلوگیری از تداخل حرکات استفاده کنید. علاوه بر این، این آزمایشگاه کد قصد دارد به شما آموزش دهد که چگونه از API Gesture Exclusion برای کنترل‌هایی مانند دستگیره‌های کشیدن که باید در مناطق حرکات قرار گیرند، استفاده کنید.

آنچه یاد خواهید گرفت

  • نحوه استفاده از شنونده‌های درج (inset listeners) در نماها (views)
  • نحوه استفاده از API حذف حرکات
  • نحوه عملکرد حالت فراگیر هنگام فعال بودن حرکات

این آزمایشگاه کد قصد دارد برنامه شما را با حرکات سیستمی سازگار کند. مفاهیم و بلوک‌های کد نامربوط حذف شده و برای کپی و پیست در اختیار شما قرار داده شده‌اند.

آنچه خواهید ساخت

پخش‌کننده موسیقی جهانی اندروید (UAMP) یک برنامه پخش موسیقی نمونه برای اندروید است که با کاتلین نوشته شده است. شما UAMP را برای ناوبری حرکتی تنظیم خواهید کرد.

  • از insetها برای دور کردن کنترل‌ها از نواحی حرکتی استفاده کنید
  • از API مربوط به Gesture Exclusion برای غیرفعال کردن ژست بازگشت برای کنترل‌هایی که با هم تداخل دارند استفاده کنید.
  • از ساخته‌های خود برای بررسی تغییرات رفتار حالت فراگیر با ناوبری حرکتی استفاده کنید

آنچه نیاز دارید

۲. مرور کلی برنامه

پخش‌کننده موسیقی جهانی اندروید (UAMP) یک برنامه پخش موسیقی نمونه برای اندروید است که با زبان کاتلین نوشته شده است. این برنامه از ویژگی‌هایی مانند پخش در پس‌زمینه، مدیریت فوکوس صوتی، ادغام با دستیار صوتی و پلتفرم‌های مختلف مانند Wear، TV و Auto پشتیبانی می‌کند.

شکل ۱ : جریانی در UAMP

UAMP یک کاتالوگ موسیقی را از یک سرور راه دور بارگذاری می‌کند و به کاربر اجازه می‌دهد آلبوم‌ها و آهنگ‌ها را مرور کند. کاربر روی یک آهنگ ضربه می‌زند و آن آهنگ از طریق بلندگوها یا هدفون‌های متصل پخش می‌شود. این برنامه برای کار با حرکات سیستم طراحی نشده است. بنابراین، وقتی UAMP را روی دستگاهی که اندروید ۱۰ یا بالاتر دارد اجرا می‌کنید، در ابتدا با برخی مشکلات مواجه می‌شوید.

۳. آماده شوید

برای دریافت برنامه نمونه، مخزن را از GitHub کپی کنید و به شاخه starter بروید:

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

روش دیگر این است که مخزن را به صورت یک فایل زیپ دانلود کنید، آن را از حالت فشرده خارج کنید و در اندروید استودیو باز کنید.

مراحل زیر را انجام دهید:

  1. برنامه را در اندروید استودیو باز کنید و بسازید.
  2. یک دستگاه مجازی جدید ایجاد کنید و سطح API 29 را انتخاب کنید. همچنین می‌توانید یک دستگاه واقعی که سطح API 29 یا بالاتر را اجرا می‌کند، متصل کنید.
  3. برنامه را اجرا کنید. لیستی که مشاهده می‌کنید، آهنگ‌ها را در زیر گزینه‌های پیشنهادی و آلبوم‌ها گروه‌بندی می‌کند.
  4. روی «توصیه‌شده» کلیک کنید و آهنگی را از لیست آهنگ‌ها انتخاب کنید.
  5. برنامه شروع به پخش آهنگ می‌کند.

فعال کردن پیمایش با اشاره

اگر یک نمونه شبیه‌ساز جدید با سطح API 29 اجرا کنید، ممکن است ناوبری حرکتی به طور پیش‌فرض فعال نباشد. برای فعال کردن ناوبری حرکتی، تنظیمات سیستم > سیستم > ناوبری سیستم > ناوبری حرکتی را انتخاب کنید.

برنامه را با Gesture Navigation اجرا کنید

اگر برنامه را با فعال بودن Gesture Navigation اجرا کنید و شروع به پخش یک آهنگ کنید، ممکن است متوجه شوید که کنترل‌های پخش‌کننده بسیار نزدیک به قسمت‌های اشاره‌ای خانه و بازگشت هستند.

۴. لبه به لبه بروید

لبه به لبه چیست؟

برنامه‌هایی که روی اندروید ۱۰ یا بالاتر اجرا می‌شوند، می‌توانند صرف نظر از اینکه حرکات یا دکمه‌ها برای ناوبری فعال باشند، یک تجربه کامل از صفحه نمایش لبه به لبه ارائه دهند. برای ارائه یک تجربه لبه به لبه، برنامه‌های شما باید از نوارهای ناوبری و وضعیت شفاف استفاده کنند.

پشت نوار ناوبری نقاشی بکشید

برای اینکه برنامه شما بتواند محتوا را در زیر نوار ناوبری رندر کند، ابتدا باید پس‌زمینه نوار ناوبری را شفاف کنید. سپس باید نوار وضعیت را شفاف کنید. این به برنامه شما اجازه می‌دهد تا برنامه شما را در تمام ارتفاع صفحه نمایش دهد.

برای تغییر رنگ نوار ناوبری و نوار وضعیت، مراحل زیر را انجام دهید:

  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>

پرچم‌های قابلیت مشاهده رابط کاربری سیستم

همچنین باید پرچم‌های قابلیت مشاهده رابط کاربری سیستم را تنظیم کنید تا به سیستم بگویید برنامه را زیر نوارهای سیستم قرار دهد. APIهای 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

اکنون برنامه به صورت لبه به لبه طراحی می‌شود، اما مشکلات مربوط به قابلیت استفاده، کنترل‌های برنامه که با هم تداخل و تداخل دارند، وجود دارد و این موارد باید حل شوند.

۵. طرح‌های تودرتو

WindowInsets به برنامه می‌گوید که رابط کاربری سیستم کجا روی محتوای شما ظاهر شود، و همچنین به برنامه می‌گوید که کدام قسمت‌های صفحه نمایش، حرکات سیستمی نسبت به حرکات درون برنامه‌ای اولویت دارند. Insets توسط کلاس WindowInsets و کلاس WindowInsetsCompat در Jetpack نمایش داده می‌شوند. ما اکیداً توصیه می‌کنیم که از WindowInsetsCompat برای داشتن رفتار سازگار در تمام سطوح API استفاده کنید.

درج‌های سیستم و درج‌های اجباری سیستم

API های inset زیر رایج ترین انواع inset مورد استفاده هستند:

  • درج پنجره سیستم: آنها به شما می‌گویند که رابط کاربری سیستم در کجای برنامه شما نمایش داده می‌شود. ما در مورد نحوه استفاده از درج‌های سیستم برای دور کردن کنترل‌های خود از میله‌های سیستم بحث خواهیم کرد.
  • درج حرکات سیستم: آنها تمام نواحی حرکتی را برمی‌گردانند. هرگونه کنترل سوایپ درون برنامه‌ای در این نواحی می‌تواند به طور تصادفی حرکات سیستم را فعال کند.
  • درج‌های اجباری حرکات: آن‌ها زیرمجموعه‌ای از درج‌های حرکات سیستمی هستند و نمی‌توان آن‌ها را لغو کرد. آن‌ها به شما می‌گویند که در چه قسمت‌هایی از صفحه، رفتار حرکات سیستمی همیشه نسبت به حرکات درون برنامه‌ای اولویت دارد.

استفاده از inset برای جابجایی کنترل‌های برنامه

حالا که اطلاعات بیشتری در مورد APIهای درج‌شده دارید، می‌توانید کنترل‌های برنامه را همانطور که در مراحل زیر توضیح داده شده است، اصلاح کنید:

  1. یک نمونه از playerLayout از نمونه شیء view دریافت کنید.
  2. یک OnApplyWindowInsetsListener به playerView اضافه کنید.
  3. دور کردن نما از ناحیه ژست: مقدار inset سیستم برای bottom را پیدا کنید و padding نما را به همان مقدار افزایش دهید. برای به‌روزرسانی padding نما بر این اساس، به [مقدار مرتبط با padding پایین برنامه]، [مقدار مرتبط با مقدار inset پایین سیستم] را اضافه کنید.

نمونه کد زیر از 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 اولین باری که مقدار inset bottom را به مقدار padding پایین برنامه که در activity_main.xml تعریف شده است اضافه می‌کنید، کاملاً کار می‌کند. با این حال، فراخوانی‌های بعدی همچنان مقدار inset bottom را به padding پایین نمای از قبل به‌روزرسانی شده اضافه می‌کنند.

برای رفع این مشکل، مراحل زیر را انجام دهید:

  1. مقدار اولیه‌ی فاصله‌گذاری view را ثبت کنید. یک val جدید ایجاد کنید و مقدار اولیه‌ی فاصله‌گذاری view مربوط به playerView را درست قبل از کد شنونده ذخیره کنید.

نمونه کد زیر از NowPlayingFragment.kt را بررسی کنید:

   val initialPadding = playerView.paddingBottom
  1. از این مقدار اولیه برای به‌روزرسانی padding پایین نما استفاده کنید، که به شما امکان می‌دهد از استفاده از مقدار padding پایین فعلی برنامه جلوگیری کنید.

نمونه کد زیر از NowPlayingFragment.kt را بررسی کنید:

   playerView.setOnApplyWindowInsetsListener { view, insets ->
            view.updatePadding(bottom = insets.systemWindowInsetBottom + initialPadding)
            insets
        }
  1. برنامه را دوباره اجرا کنید. بین برنامه‌ها حرکت کنید و به صفحه اصلی بروید. وقتی برنامه را برگردانید، کنترل‌های پخش‌کننده درست بالای ناحیه ژست‌ها قرار دارند.

طراحی مجدد کنترل‌های برنامه

نوار جستجو (seebar) پخش‌کننده خیلی به ناحیه‌ی اشاره‌ی پایینی نزدیک است، به این معنی که کاربر می‌تواند هنگام کشیدن انگشت افقی، به‌طور تصادفی اشاره‌ی خانه را فعال کند. اگر فاصله‌ی بین دو نقطه را بیشتر کنید، می‌تواند مشکل را حل کند، اما ممکن است پخش‌کننده را بالاتر از حد مطلوب نیز حرکت دهد.

استفاده از insetها به شما امکان می‌دهد تداخل حرکات را برطرف کنید، اما گاهی اوقات با تغییرات کوچک در طراحی، می‌توانید از تداخل حرکات به طور کامل جلوگیری کنید. برای طراحی مجدد کنترل‌های پخش‌کننده برای جلوگیری از تداخل حرکات، مراحل زیر را انجام دهید:

  1. fragment_nowplaying.xml را باز کنید. به نمای طراحی (Design view) بروید و SeekBar را در پایین صفحه انتخاب کنید:

74918dec3926293f.png

  1. به نمای کد بروید.
  2. برای انتقال SeekBar به بالای playerLayout ، layout_constraintTop_toBottomOf مربوط به SeekBar را به 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. برنامه را اجرا کنید و با پخش‌کننده و نوار جستجو تعامل داشته باشید.

این تغییرات طراحی حداقلی، برنامه را به طور قابل توجهی بهبود می‌بخشند.

۶. API حذف ژست‌های حرکتی

تداخل کنترل‌های بازیکن برای ژست‌های حرکتی در ناحیه ژست‌های حرکتی خانه برطرف شده است. ناحیه ژست‌های حرکتی پشت نیز می‌تواند با کنترل‌های برنامه تداخل ایجاد کند. تصویر زیر نشان می‌دهد که نوار جستجوی بازیکن در حال حاضر در هر دو ناحیه ژست پشتی راست و چپ قرار دارد:

e6d98e94dcf83dde.png

SeekBar به طور خودکار تداخل ژست‌ها را مدیریت می‌کند. با این حال، ممکن است لازم باشد از اجزای رابط کاربری دیگری استفاده کنید که باعث تداخل ژست‌ها می‌شوند. در این موارد، می‌توانید Gesture Exclusion API برای غیرفعال کردن جزئی ژست برگشت استفاده کنید.

استفاده از API حذف ژست‌های حرکتی

برای ایجاد یک منطقه‌ی استثنای ژست (gesture)، تابع setSystemGestureExclusionRects() را در نمای خود به همراه لیستی از اشیاء rect فراخوانی کنید. این اشیاء rect به مختصات نواحی مستطیلیِ مستثنی شده نگاشت می‌شوند. این فراخوانی باید در متدهای onLayout() یا onDraw() نمای مورد نظر انجام شود. برای انجام این کار، مراحل زیر را انجام دهید:

  1. یک بسته جدید با نام view ایجاد کنید.
  2. برای فراخوانی این API، یک کلاس جدید به نام MySeekBar ایجاد کنید و AppCompatSeekBar ارث‌بری (extend) کنید.

نمونه کد زیر از 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. برای رد کردن این فراخوانی در سطح API 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. از آنجا که API مربوط به Gesture Exclusion محدودیت ۲۰۰ dp دارد، فقط انگشت شست Seekbar را مستثنی کنید. یک کپی از مرزهای Seekbar بگیرید و هر شیء را به یک لیست قابل تغییر اضافه کنید.

نمونه کد زیر از 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 آزمایش و تغییر دهید. مراقب باشید که یک منطقه‌ی محرومیت از حرکات بزرگتر از حد لازم ایجاد نکنید، زیرا این کار سایر فراخوانی‌های محرومیت از حرکات بالقوه را محدود می‌کند و باعث ایجاد رفتار متناقض برای کاربر می‌شود.

۷. تبریک

تبریک! شما یاد گرفتید که چگونه از تداخل‌ها با حرکات سیستمی جلوگیری کرده و آنها را حل کنید!

شما با گسترش لبه به لبه و استفاده از inset برای دور کردن کنترل‌های برنامه از محدوده‌های اشاره، باعث شدید برنامه‌تان به صورت تمام صفحه نمایش داده شود. همچنین یاد گرفتید که چگونه حرکت سیستم به عقب را در کنترل‌های برنامه غیرفعال کنید.

اکنون مراحل کلیدی مورد نیاز برای کار کردن برنامه‌هایتان با حرکات سیستمی را می‌دانید!

مواد اضافی

اسناد مرجع