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

1. مقدمه

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

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

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

چیزی که یاد خواهید گرفت

  • نحوه استفاده از شنوندگان داخلی در نماها
  • نحوه استفاده از Gesture Exclusion API
  • هنگام فعال بودن حرکات، حالت همهجانبه چگونه رفتار می کند

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

چیزی که خواهی ساخت

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

  • برای دور کردن کنترل‌ها از قسمت‌های اشاره‌ای، از ورودی‌ها استفاده کنید
  • از Gesture Exclusion API برای انصراف از اشاره عقب برای کنترل‌هایی که تداخل دارند استفاده کنید
  • از ساخت‌های خود برای کاوش تغییرات رفتاری حالت همهجانبه با ناوبری اشاره استفاده کنید

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

2. نمای کلی برنامه

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

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

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

3. راه اندازی شوید

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

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

همچنین می‌توانید مخزن را به‌صورت فایل فشرده دانلود کنید، آن را از حالت فشرده خارج کنید و در Android Studio باز کنید.

مراحل زیر را کامل کنید:

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

ژست پیمایش را فعال کنید

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

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

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

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>

پرچم‌های نمایان بودن رابط کاربری سیستم

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

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

5. درونی

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

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

API های داخلی زیر متداول ترین انواع درونی مورد استفاده هستند:

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

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

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

  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. دوباره برنامه را اجرا کنید و به صفحه پخش کننده بروید. کنترل‌های پخش‌کننده پایین از قسمت اشاره‌ای پایینی دور می‌شوند.

اکنون کنترل‌های برنامه با Gesture Navigation کار می‌کنند، اما کنترل‌ها بیش از حد انتظار حرکت می‌کنند. شما باید این را حل کنید.

بالشتک ها و حاشیه های فعلی را حفظ کنید

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

این به این دلیل است که برنامه هر بار که فعالیت شروع می‌شود requestApplyInsets() را راه‌اندازی می‌کند. حتی بدون این تماس، WindowInsets می‌تواند چندین بار در هر زمان در طول چرخه حیات یک View ارسال شود.

InsetListener فعلی در playerView اولین باری که مقدار پایینی ورودی را به مقدار padding پایین برنامه اعلام شده در activity_main.xml اضافه می‌کنید، کاملاً کار می‌کند. با این حال، تماس‌های بعدی به افزودن مقدار پایینی درج شده به صفحه پایین نمای از قبل به‌روزرسانی شده ادامه می‌دهند.

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

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

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

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

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

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

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

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

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

  1. fragment_nowplaying.xml را باز کنید. به نمای طراحی بروید و 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. برنامه را اجرا کنید و با پخش کننده و نوار جستجو تعامل داشته باشید.

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

6. Gesture Exclusion API

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

e6d98e94dcf83dde.png

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

از Gesture Exclusion API استفاده کنید

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

  1. یک بسته جدید با نام view ایجاد کنید.
  2. برای فراخوانی این API، یک کلاس جدید به نام 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. برای رد شدن از این تماس در سطح 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. از آنجایی که Gesture Exclusion API دارای محدودیت 200 dp است، فقط انگشت شست نوار جستجو را حذف کنید. یک کپی از کران های 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. با لیست‌های gestureExclusionRects که ایجاد می‌کنید systemGestureExclusionRects() را فراخوانی کنید.

نمونه کد زیر 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. تبریک می گویم

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

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

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

مواد اضافی

اسناد مرجع