درک حاشیه‌های پنجره در WebView

WebView تراز محتوا را بااستفاده از دو نمای دید مدیریت می‌کند: نمای دید چیدمان (اندازه صفحه) و نمای دید دیداری (بخشی از صفحه که کاربر واقعاً می‌بیند). درحالی‌که نمای چیدمان معمولاً ثابت است، نمای دیداری به‌طور پویا تغییر می‌کند وقتی کاربران بزرگ‌نمایی یا پیمایش می‌کنند، یا وقتی عناصر میانای کاربر سیستم (مانند صفحه‌کلید نرم‌افزاری) ظاهر می‌شوند.

سازگاری ویژگی

پشتیبانی «وب‌نما» از درج‌های پنجره درطول زمان تکامل یافته است تا رفتار محتوای وب با انتظارات برنامه Android بومی همسو شود:

Milestone ویژگی اضافه شد حوزه
M136 displayCutout() و systemBars() ازطریق CSS safe-area-insets پشتیبانی می‌شوند. فقط WebView تمام‌صفحه.
M139 پشتیبانی از ime() (ویرایشگر روش ورودی، که یک صفحه‌کلید است) ازطریق تغییر اندازه نمای دیداری. همه WebViews.
M144 پشتیبانی displayCutout() و systemBars(). همه «وب‌نماها» (صرف‌نظر از وضعیت تمام‌صفحه).

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

مکانیک اصلی

WebView ازطریق دو سازوکار اصلی، تورفتگی‌ها را مدیریت می‌کند:

  • مناطق امن (displayCutout، systemBars): WebView این ابعاد را ازطریق متغیرهای CSS safe-area-inset-* به محتوای وب بازارسال می‌کند. این به توسعه‌دهندگان امکان می‌دهد از پنهان شدن عناصر تعاملی خود (مانند نوارهای پیمایش) توسط بریدگی‌ها یا نوارهای وضعیت جلوگیری کنند.

  • تغییر اندازه نمای بصری بااستفاده از ویرایشگر روش ورودی (IME): از نسخه M139، ویرایشگر روش ورودی (IME) مستقیماً اندازه نمای بصری را تغییر می‌دهد. این سازوکار تغییر اندازه نیز براساس تقاطع WebView-Window است. برای مثال، در حالت چندوظیفه‌ای Android، اگر پایین WebView به‌اندازه ۲۰۰ پیکسل مستقل از تراکم زیر پایین پنجره گسترش یابد، نمای دیداری ۲۰۰ پیکسل مستقل از تراکم کوچک‌تر از اندازه WebView است. این تغییر اندازه نمای دیداری (هم برای IME و هم برای تقاطع WebView-Window) فقط برای پایین WebView اعمال می‌شود. این سازوکار از تغییر اندازه برای هم‌پوشانی چپ، راست، یا بالا پشتیبانی نمی‌کند. این یعنی صفحه‌کلیدهای لنگرانداخته که در این لبه‌ها ظاهر می‌شوند، باعث تغییر اندازه نمای دیداری نمی‌شوند.

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

منطق مرزها و هم‌پوشانی

WebView باید مقادیر درج غیرصفر را فقط زمانی دریافت کند که عناصر میانای کاربر سیستم (نوارها، برش‌های نمایشگر، یا صفحه‌کلید) مستقیماً با مرزهای صفحه WebView هم‌پوشانی داشته باشند. اگر «وب‌نما» با این عناصر میانای کاربر هم‌پوشانی نداشته باشد (مثلاً اگر «وب‌نما» در مرکز صفحه باشد و با نوارهای سیستم تماس نداشته باشد)، باید این حاشیه‌ها را به‌صورت صفر دریافت کند.

برای لغو این منطق پیش‌فرض و ارائه محتوای وب با ابعاد کامل سیستم بدون درنظر گرفتن هم‌پوشانی، از روش setOnApplyWindowInsetsListener استفاده کنید و شیء windowInsets اصلی و اصلاح‌نشده را از شنونده برگردانید. ارائه ابعاد کامل سیستم می‌تواند با فعال کردن محتوای وب برای هم‌تراز شدن با سخت‌افزار دستگاه، صرف‌نظر از موقعیت فعلی «وب‌نما»، به اطمینان از سازگاری طراحی کمک کند. این کار باعث می‌شود وقتی WebView حرکت می‌کند یا گسترش می‌یابد تا لبه‌های صفحه‌نمایش را لمس کند، انتقال به‌آرامی انجام شود.

کاتلین

ViewCompat.setOnApplyWindowInsetsListener(myWebView) { _, windowInsets ->
    // By returning the original windowInsets object, we override the default
    // behavior that zeroes out system insets (like system bars or display
    // cutouts) when they don't directly overlap the WebView's screen bounds.
    windowInsets
}

جاوا

ViewCompat.setOnApplyWindowInsetsListener(myWebView, (v, windowInsets) -> {
    // By returning the original windowInsets object, we override the default
    // behavior that zeroes out system insets (like system bars or display
    // cutouts) when they don't directly overlap the WebView's screen bounds.
    return windowInsets;
});

مدیریت رویدادهای تغییر اندازه

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

  1. کاربر روی عنصر ورودی تمرکز می‌کند.
  2. صفحه‌کلید ظاهر می‌شود و رویداد تغییر اندازه را راه‌اندازی می‌کند.
  3. کد وب‌سایت در پاسخ به تغییر اندازه، تمرکز را پاک می‌کند.
  4. صفحه‌کلید پنهان می‌شود چون تمرکز ازدست رفته است.

برای کاهش این رفتار، شنوندگان سمت وب را بررسی کنید تا مطمئن شوید تغییرات نمای هدف به‌طور ناخواسته تابع جاوا اسکریپت blur() یا رفتارهای پاک‌سازی تمرکز را راه‌اندازی نمی‌کند.

پیاده‌سازی مدیریت درج

تنظیمات پیش‌فرض WebView به‌طور خودکار برای اکثر برنامه‌ها کار می‌کند. بااین‌حال، اگر برنامه‌تان از چیدمان‌های سفارشی استفاده می‌کند (برای مثال، اگر برای درنظر گرفتن نوار وضعیت یا صفحه‌کلید، حاشیه داخلی خودتان را اضافه می‌کنید)، می‌توانید از رویکردهای زیر برای بهبود نحوه عملکرد محتوای وب و واسط کاربر بومی با یکدیگر استفاده کنید. اگر واسط کاربر بومی شما براساس WindowInsets به ظرفی لایه اضافه می‌کند، باید این تورفتگی‌ها را قبل‌از رسیدن به «وب‌نما» به‌درستی مدیریت کنید تا از لایه اضافه کردن مضاعف جلوگیری شود.

حاشیه داخلی مضاعف وضعیتی است که در آن چیدمان بومی و محتوای وب ابعاد حاشیه داخلی یکسانی را اعمال می‌کنند، که منجر به ایجاد فاصله اضافی می‌شود. برای مثال، تلفنی را با نوار وضعیت ۴۰ پیکسلی تصور کنید. هم نمای بومی و هم WebView درج ۴۰ پیکسلی را می‌بینند. هر دو ۴۰ پیکسل حاشیه داخلی اضافه می‌کنند، درنتیجه کاربر فاصله‌ای ۸۰ پیکسلی در بالا می‌بیند.

رویکرد صفر کردن

برای جلوگیری از لایه‌گذاری مضاعف، باید مطمئن شوید که پس‌از اینکه نمای بومی از بعد درج برای لایه‌گذاری استفاده کرد، آن بعد را بااستفاده از Insets.NONE در شیء WindowInsets جدید به صفر بازنشانی کنید و سپس شیء اصلاح‌شده را ازطریق سلسله‌مراتب نما به WebView ارسال کنید.

هنگام اعمال حاشیه داخلی به نمای والد، معمولاً باید از رویکرد صفر کردن با تنظیم Insets.NONE به‌جای WindowInsetsCompat.CONSUMED استفاده کنید. برگرداندن WindowInsetsCompat.CONSUMED ممکن است در برخی شرایط کارساز باشد. اما اگر گرداننده برنامه‌تان تورفتگی‌ها را تغییر دهد یا لایه داخلی خودش را اضافه کند، ممکن است با مشکل مواجه شود. رویکرد صفر کردن این محدودیت‌ها را ندارد.

اجتناب از حاشیه داخلی کاذب با صفر کردن حاشیه‌های داخلی

اگر وقتی برنامه قبلاً درج‌های مصرف‌نشده را ارسال کرده است، درج‌ها را مصرف کنید، یا اگر درج‌ها تغییر کنند (مانند پنهان شدن صفحه‌کلید)، مصرف آن‌ها مانع از دریافت اعلان به‌روزرسانی لازم توسط WebView می‌شود. این امر می‌تواند باعث شود WebView لایه داخلی شبح را از وضعیت قبلی حفظ کند (برای مثال، حفظ لایه داخلی صفحه‌کلید پس‌از پنهان شدن صفحه‌کلید).

مثال زیر تعامل خراب بین برنامه و «وب‌نما» را نشان می‌دهد:

  1. وضعیت اولیه: برنامه در ابتدا حاشیه‌های مصرف‌نشده (برای مثال، displayCutout() یا systemBars()) را به WebView ارسال می‌کند، که به‌طور داخلی فاصله‌گذاری را روی محتوای وب اعمال می‌کند.
  2. تغییر وضعیت و خطا: اگر وضعیت برنامه تغییر کند (برای مثال، صفحه‌کلید پنهان شود) و برنامه تصمیم بگیرد با برگرداندن WindowInsetsCompat.CONSUMED، حاشیه‌های حاصل را مدیریت کند.
  3. اعلان مسدود شد: مصرف کردن حاشیه‌ها مانع از آن می‌شود که سیستم Android اعلان به‌روزرسانی لازم را ازطریق سلسله‌مراتب نما به WebView ارسال کند.
  4. حاشیه داخلی کاذب: چون WebView به‌روزرسانی را دریافت نمی‌کند، حاشیه داخلی را از وضعیت قبلی حفظ می‌کند و باعث ایجاد حاشیه داخلی کاذب می‌شود (برای مثال، حفظ حاشیه داخلی صفحه‌کلید پس‌از پنهان شدن صفحه‌کلید).

به‌جای آن، از WindowInsetsCompat.Builder استفاده کنید تا انواع رسیدگی‌شده را قبل‌از ارسال شیء به نماهای فرزند روی صفر تنظیم کنید. این به WebView اطلاع می‌دهد که آن درج‌های خاص قبلاً درنظر گرفته شده‌اند و درعین‌حال به اعلان اجازه می‌دهد تا در سلسله‌مراتب نما ادامه یابد.

کاتلین

ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, windowInsets ->
    // 1. Identify the inset types you want to handle natively
    val types = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()

    // 2. Extract the dimensions and apply them as padding to the native container
    val insets = windowInsets.getInsets(types)
    view.setPadding(insets.left, insets.top, insets.right, insets.bottom)

    // 3. Return a new WindowInsets object with the handled types set to NONE (zeroed).
    // This informs the WebView that these areas are already padded, preventing
    // double-padding while still allowing the WebView to update its internal state.
    WindowInsetsCompat.Builder(windowInsets)
        .setInsets(types, Insets.NONE)
        .build()
}

جاوا

ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> {
    // 1. Identify the inset types you want to handle natively
    int types = WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout();

    // 2. Extract the dimensions and apply them as padding to the native container
    Insets insets = windowInsets.getInsets(types);
    rootView.setPadding(insets.left, insets.top, insets.right, insets.bottom);

    // 3. Return a new Insets object with the handled types set to NONE (zeroed).
    // This informs the WebView that these areas are already padded, preventing
    // double-padding while still allowing the WebView to update its internal
    // state.
    return new WindowInsetsCompat.Builder(windowInsets)
            .setInsets(types, Insets.NONE)
            .build();
});

نحوه انصراف دادن

برای غیرفعال کردن این رفتارهای مدرن و بازگشت به مدیریت نمای قدیمی، کارهای زیر را انجام دهید:

  1. رهگیری درج‌ها: از setOnApplyWindowInsetsListener استفاده کنید یا onApplyWindowInsets را در زیرکلاس WebView لغو کنید.

  2. پاک کردن حاشیه‌ها: مجموعه حاشیه‌های مصرف‌شده (برای مثال، WindowInsetsCompat.CONSUMED) را از ابتدا برگردانید. این اقدام از انتشار اعلان درج به «وب‌نما» به‌طور کامل جلوگیری می‌کند، به‌طور مؤثری تغییر اندازه ناحیه نمایش مدرن را غیرفعال می‌کند و «وب‌نما» را مجبور می‌کند اندازه ناحیه نمایش دیداری اولیه خود را حفظ کند.