نمایش محتوا لبه به لبه در نماها

روش نوشتن را امتحان کنید
Jetpack Compose ابزار رابط کاربری پیشنهادی برای اندروید است. یاد بگیرید که چگونه با لبه به لبه در Compose کار کنید.

وقتی SDK 35 یا بالاتر را روی دستگاهی با اندروید ۱۵ یا بالاتر هدف قرار دهید، برنامه شما به صورت لبه به لبه نمایش داده می‌شود. پنجره با کشیدن خطوط پشت میله‌های سیستم، کل عرض و ارتفاع صفحه نمایش را در بر می‌گیرد. میله‌های سیستم شامل نوار وضعیت، نوار عنوان و نوار ناوبری هستند.

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

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

شکل ۱. نوارهای سیستم در یک طرح‌بندی لبه به لبه.

هنگام پیاده‌سازی طرح‌بندی لبه به لبه در برنامه خود، موارد زیر را در نظر داشته باشید:

  1. فعال کردن نمایشگر لبه به لبه
  2. پیاده‌سازی طرح‌بندی‌های تطبیقی ​​برای بهینه‌سازی تجربه کاربری در فرم‌فکتورهای مختلف
  3. هرگونه همپوشانی بصری را مدیریت کنید
  4. نمایش اسکریم‌ها را پشت میله‌های سیستم در نظر بگیرید
نمونه‌ای از تصاویر پشت نوار وضعیت
شکل ۲. نمونه‌ای از تصاویر پشت نوار وضعیت.

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

اگر برنامه شما SDK 35 یا بالاتر را هدف قرار داده باشد، edge-to-edge به طور خودکار برای دستگاه‌های اندروید ۱۵ یا بالاتر فعال می‌شود.

برای فعال کردن لبه به لبه در نسخه‌های قبلی اندروید، به صورت دستی enableEdgeToEdge در onCreate از Activity خود فراخوانی کنید.

کاتلین

 override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         WindowCompat.enableEdgeToEdge(window)
        ...
      }

جاوا

 @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WindowCompat.enableEdgeToEdge(getWindow());
        ...
      }

به طور پیش‌فرض، enableEdgeToEdge() نوارهای سیستم را شفاف می‌کند، به جز در حالت ناوبری سه دکمه‌ای که نوار وضعیت یک نوار شفاف می‌گیرد. رنگ آیکون‌های سیستم و نوار شفاف بر اساس تم روشن یا تیره سیستم تنظیم می‌شود.

برای فعال کردن نمایش لبه به لبه در برنامه خود بدون استفاده از تابع enableEdgeToEdge() ، به بخش تنظیم دستی نمایش لبه به لبه مراجعه کنید.

همپوشانی‌ها را با استفاده از درج‌ها مدیریت کنید

همانطور که در شکل 3 نشان داده شده است، ممکن است برخی از نماهای برنامه شما پشت نوارهای سیستم ترسیم شوند.

شما می‌توانید با واکنش به insetها، همپوشانی‌ها را برطرف کنید، insetها مشخص می‌کنند که کدام قسمت‌های صفحه با رابط کاربری سیستم مانند نوار ناوبری یا نوار وضعیت تلاقی دارند. intersecting می‌تواند به معنای نمایش بالای محتوا باشد، اما همچنین می‌تواند برنامه شما را در مورد حرکات سیستم مطلع کند.

انواع درج‌هایی که برای نمایش لبه به لبه برنامه شما اعمال می‌شوند عبارتند از:

  • درج نوارهای سیستم: بهترین حالت برای نماهایی است که قابل لمس هستند و نباید از نظر بصری توسط نوارهای سیستم مبهم شوند.

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

  • درج‌های اشاره سیستم: برای نواحی ناوبری اشاره‌ای که توسط سیستم استفاده می‌شوند و نسبت به برنامه شما اولویت دارند.

میله‌های سیستمی

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

برای مثال، دکمه عملیاتی شناور (FAB) در شکل 3 تا حدی توسط نوار ناوبری پنهان شده است:

نمونه‌ای از پیاده‌سازی لبه به لبه، اما نوار ناوبری، FAB را پوشش می‌دهد.
شکل ۳. نوار ناوبری که در یک طرح‌بندی لبه به لبه، روی یک FAB همپوشانی دارد.

برای جلوگیری از این نوع همپوشانی بصری در حالت ژست یا حالت دکمه، می‌توانید حاشیه‌های نما را با استفاده getInsets(int) به همراه WindowInsetsCompat.Type.systemBars() افزایش دهید.

مثال کد زیر نحوه پیاده‌سازی درج نوار سیستمی را نشان می‌دهد:

کاتلین

ViewCompat.setOnApplyWindowInsetsListener(fab) { v, windowInsets ->
  val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
  // Apply the insets as a margin to the view. This solution sets
  // only the bottom, left, and right dimensions, but you can apply whichever
  // insets are appropriate to your layout. You can also update the view padding
  // if that's more appropriate.
  v.updateLayoutParams<MarginLayoutParams> {
      leftMargin = insets.left
      bottomMargin = insets.bottom
      rightMargin = insets.right
  }

  // Return CONSUMED if you don't want the window insets to keep passing
  // down to descendant views.
  WindowInsetsCompat.CONSUMED
}

جاوا

ViewCompat.setOnApplyWindowInsetsListener(fab, (v, windowInsets) -> {
  Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
  // Apply the insets as a margin to the view. This solution sets only the
  // bottom, left, and right dimensions, but you can apply whichever insets are
  // appropriate to your layout. You can also update the view padding if that's
  // more appropriate.
  MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
  mlp.leftMargin = insets.left;
  mlp.bottomMargin = insets.bottom;
  mlp.rightMargin = insets.right;
  v.setLayoutParams(mlp);

  // Return CONSUMED if you don't want the window insets to keep passing
  // down to descendant views.
    return WindowInsetsCompat.CONSUMED;
});

اگر این راه‌حل را روی مثال نشان داده شده در شکل ۳ اعمال کنید، همانطور که در شکل ۴ نشان داده شده است، هیچ همپوشانی بصری در حالت دکمه مشاهده نمی‌شود:

یک نوار ناوبری شفاف که FAB را پوشش نمی‌دهد
شکل ۴. حل همپوشانی بصری در حالت دکمه‌ای

همین امر در مورد حالت ناوبری اشاره‌ای نیز صدق می‌کند، همانطور که در شکل 5 نشان داده شده است:

لبه به لبه با ناوبری اشاره‌ای
شکل ۵. حل مشکل همپوشانی بصری در حالت ناوبری اشاره‌ای.

برش‌های نمایشی

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

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

کاتلین

ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets ->
  val bars = insets.getInsets(
    WindowInsetsCompat.Type.systemBars()
      or WindowInsetsCompat.Type.displayCutout()
  )
  v.updatePadding(
    left = bars.left,
    top = bars.top,
    right = bars.right,
    bottom = bars.bottom,
  )
  WindowInsetsCompat.CONSUMED
}

جاوا

ViewCompat.setOnApplyWindowInsetsListener(mBinding.recyclerView, (v, insets) -> {
  Insets bars = insets.getInsets(
    WindowInsetsCompat.Type.systemBars()
    | WindowInsetsCompat.Type.displayCutout()
  );
  v.setPadding(bars.left, bars.top, bars.right, bars.bottom);
  return WindowInsetsCompat.CONSUMED;
});

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

clipToPadding روی RecyclerView تنظیم کنید تا padding همراه با آیتم‌های لیست اسکرول شود. این کار به آیتم‌ها اجازه می‌دهد تا هنگام اسکرول کاربر، همانطور که در مثال زیر نشان داده شده است، به پشت میله‌های سیستم بروند.

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

درج حرکات سیستم

درج‌های حرکات سیستم، نواحی از پنجره را نشان می‌دهند که در آن‌ها حرکات سیستم نسبت به برنامه شما اولویت دارند. این نواحی در شکل 6 با رنگ نارنجی نشان داده شده‌اند:

نمونه‌ای از درج ژست‌های سیستمی
شکل ۶. درج حرکات سیستم.

مانند درج‌های نوار سیستم، می‌توانید با استفاده از getInsets(int) به همراه WindowInsetsCompat.Type.systemGestures() از همپوشانی درج‌های ژست سیستم جلوگیری کنید.

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

در اندروید ۱۰ یا بالاتر، درج حرکات سیستمی شامل یک درج پایین برای حرکت خانه و یک درج چپ و راست برای حرکات برگشت است:

نمونه‌ای از اندازه‌گیری‌های درج ژست سیستم
شکل ۷. اندازه‌گیری‌های درج ژست سیستم.

مثال کد زیر نحوه پیاده‌سازی درج‌های ژست سیستمی را نشان می‌دهد:

کاتلین

ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets ->
    val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
    // Apply the insets as padding to the view. Here, set all the dimensions
    // as appropriate to your layout. You can also update the view's margin if
    // more appropriate.
    view.updatePadding(insets.left, insets.top, insets.right, insets.bottom)

    // Return CONSUMED if you don't want the window insets to keep passing down
    // to descendant views.
    WindowInsetsCompat.CONSUMED
}

جاوا

ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
    Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures());
    // Apply the insets as padding to the view. Here, set all the dimensions
    // as appropriate to your layout. You can also update the view's margin if
    // more appropriate.
    view.setPadding(insets.left, insets.top, insets.right, insets.bottom);

    // Return CONSUMED if you don't want the window insets to keep passing down
    // to descendant views.
    return WindowInsetsCompat.CONSUMED;
});

اجزای مواد

بسیاری از کامپوننت‌های متریال اندروید مبتنی بر نماها (com.google.android.material) به طور خودکار insetها را مدیریت می‌کنند، از جمله BottomAppBar ، BottomNavigationView ، NavigationRailView و NavigationView

با این حال، AppBarLayout به طور خودکار insetها را مدیریت نمی‌کند. برای مدیریت insetهای بالا android:fitsSystemWindows="true" را اضافه کنید.

نحوه مدیریت درج‌ها با کامپوننت‌های متریال در Compose را بخوانید.

دیسپاچینگ داخلی سازگار با نسخه‌های قبلی

برای جلوگیری از ارسال insetها به viewهای فرزند و جلوگیری از over-padding، می‌توانید insetها را با استفاده از ثابت WindowInsetsCompat.CONSUMED مصرف کنید. با این حال، در دستگاه‌هایی که اندروید ۱۰ (سطح API ۲۹ و قبل از آن) را اجرا می‌کنند، insetها پس از فراخوانی WindowInsetsCompat.CONSUMED به siblingها ارسال نمی‌شوند، که می‌تواند باعث همپوشانی بصری ناخواسته شود.

مثال ارسال با درج شکسته
شکل ۸. مثال ارسال inset ناقص. insetها پس از اینکه ViewGroup 1 در اندروید ۱۰ (سطح API ۲۹) و قبل از آن insetها را مصرف می‌کند، به viewهای خواهر و برادر ارسال نمی‌شوند و باعث می‌شوند TextView 2 با نوار ناوبری سیستم همپوشانی داشته باشد. با این حال، همانطور که انتظار می‌رود، insetها در اندروید ۱۱ (سطح API ۳۰) و بالاتر به viewهای خواهر و برادر ارسال می‌شوند.

برای تأیید اینکه insetها برای همه نسخه‌های اندروید پشتیبانی‌شده به siblingها ارسال می‌شوند، قبل از مصرف insetها از ViewGroupCompat#installCompatInsetsDispatch استفاده کنید، که در AndroidX Core و Core-ktx 1.16.0-alpha01 و بالاتر موجود است.

کاتلین

// Use the i.d. assigned to your layout's root view, e.g. R.id.main
val rootView = findViewById(R.id.main)
// Call before consuming insets
ViewGroupCompat.installCompatInsetsDispatch(rootView)

جاوا

// Use the i.d. assigned to your layout's root view, e.g. R.id.main
LinearLayout rootView = findViewById(R.id.main);
// Call before consuming insets
ViewGroupCompat.installCompatInsetsDispatch(rootView);
مثال اعزام با درج ثابت
شکل ۹. رفع مشکل ارسال inset پس از فراخوانی ViewGroupCompat#installCompatInsetsDispatch.

حالت فراگیر

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

کاتلین

val windowInsetsController =
      WindowCompat.getInsetsController(window, window.decorView)

// Hide the system bars.
windowInsetsController.hide(Type.systemBars())

// Show the system bars.
windowInsetsController.show(Type.systemBars())

جاوا

Window window = getWindow();
WindowInsetsControllerCompat windowInsetsController =
      WindowCompat.getInsetsController(window, window.getDecorView());
if (windowInsetsController == null) {
    return;
  }
// Hide the system bars.
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());

// Show the system bars.
windowInsetsController.show(WindowInsetsCompat.Type.systemBars());

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

آیکون‌های نوار سیستم

فراخوانی enableEdgeToEdge تضمین می‌کند که رنگ آیکون‌های نوار سیستم با تغییر تم دستگاه به‌روزرسانی شوند.

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

کاتلین

WindowCompat.getInsetsController(window, window.decorView)
    .isAppearanceLightStatusBars = false

جاوا

WindowCompat.getInsetsController(window, window.getDecorView())
    .setAppearanceLightStatusBars(false);

محافظت از میله سیستم

زمانی که برنامه شما SDK 35 یا بالاتر را هدف قرار دهد، لبه به لبه اعمال می‌شود . نوار وضعیت سیستم و نوارهای ناوبری حرکتی شفاف هستند، اما نوار ناوبری سه دکمه‌ای نیمه‌شفاف است. برای سازگاری با نسخه‌های قبلی enableEdgeToEdge فراخوانی کنید.

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

ایجاد نوارهای سیستم شفاف

با هدف قرار دادن اندروید ۱۵ (SDK 35) یا بالاتر یا با فراخوانی تابع enableEdgeToEdge() با آرگومان‌های پیش‌فرض برای نسخه‌های قبلی، یک نوار وضعیت شفاف ایجاد کنید.

با هدف قرار دادن اندروید ۱۵ یا بالاتر یا با فراخوانی enableEdgeToEdge() با آرگومان‌های پیش‌فرض برای نسخه‌های قبلی، یک نوار ناوبری ژست شفاف ایجاد کنید. برای نوار ناوبری سه دکمه‌ای، Window.setNavigationBarContrastEnforced را روی false تنظیم کنید، در غیر این صورت یک پارچه شفاف اعمال خواهد شد.

ایجاد میله‌های سیستم شفاف

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

  1. وابستگی androidx-core خود را به نسخه ۱.۱۶.۰-beta01 یا بالاتر به‌روزرسانی کنید.
  2. طرح‌بندی XML خود را در androidx.core.view.insets.ProtectionLayout قرار دهید و یک شناسه (ID) به آن اختصاص دهید.
  3. به صورت برنامه‌نویسی شده به ProtectionLayout دسترسی پیدا کنید تا محافظت‌ها را تنظیم کنید، سمت و یک GradientProtection برای نوار وضعیت مشخص کنید.

<androidx.core.view.insets.ProtectionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list_protection"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@+id/item_list"
        android:clipToPadding="false"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--items-->

    </ScrollView>

</androidx.core.view.insets.ProtectionLayout>

findViewById<ProtectionLayout>(R.id.list_protection)
    .setProtections(
        listOf(
            GradientProtection(
                WindowInsetsCompat.Side.TOP,
                // Ideally, this is the pane's background color
                paneBackgroundColor
            )
        )
    )

مطمئن شوید که ColorInt ارسالی به GradientProtection با پس‌زمینه محتوا مطابقت دارد. برای مثال، یک طرح‌بندی list-detail که روی یک صفحه تاشو نمایش داده می‌شود، ممکن است GradientProtections مختلف با رنگ‌های مختلف برای پنل لیست و پنل جزئیات داشته باشد.

شکل ۱. محافظت گرادیانی از رنگ‌های مختلف.

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

  • اگر از قبل طرح‌بندی خود را در یک ProtectionView قرار داده‌اید، می‌توانید یک ColorProtection یا GradientProtection اضافی را به متد setProtections ارسال کنید. قبل از انجام این کار، مطمئن شوید که window.isNavigationBarContrastEnforced = false .
  • در غیر این صورت، window.isNavigationBarContrastEnforced = true قرار دهید. اگر برنامه شما enableEdgeToEdge, window.isNavigationBarContrastEnforced = true است.

نکات دیگر

نکات اضافی هنگام کار با قطعات توکار.

محتوای پیمایشی را لبه به لبه کنید

با مدیریت درج‌ها و تنظیم clipToPadding روی false ، بررسی کنید که آخرین آیتم لیست توسط نوارهای سیستم در RecyclerView یا NestedScrollView شما پنهان نشده باشد.

ویدیوی زیر یک RecyclerView با نمایش لبه به لبه غیرفعال (چپ) و فعال (راست) نشان می‌دهد:

برای نمونه کد، به قطعه کدهای موجود در بخش «ایجاد لیست‌های پویا با RecyclerView» مراجعه کنید.

پنجره‌های محاوره‌ای تمام‌صفحه را لبه‌به‌لبه کنید

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

کاتلین

class MyAlertDialogFragment : DialogFragment() {
    override fun onStart(){
        super.onStart()
        dialog?.window?.let { WindowCompat.enableEdgeToEdge(it) }
    }
    ...
}

جاوا

public class MyAlertDialogFragment extends DialogFragment {
    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        if (dialog != null) {
            Window window = dialog.getWindow();
            if (window != null) {
                WindowCompat.enableEdgeToEdge(window);
            }
        }
    }
    ...
}

منابع اضافی

برای اطلاعات بیشتر در مورد لبه به لبه به منابع زیر مراجعه کنید.

وبلاگ‌ها

طراحی

سایر مستندات

ویدیوها