درک پنجره‌های الحاقی در وب‌ویو

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

سازگاری با ویژگی‌ها

پشتیبانی WebView از پنجره‌های الحاقی (window insets) در طول زمان تکامل یافته است تا رفتار محتوای وب را با انتظارات برنامه‌های بومی اندروید هماهنگ کند:

نقطه عطف ویژگی اضافه شد دامنه
ام۱۳۶ از طریق CSS، displayCutout() و systemBars() از safe-area-insets پشتیبانی می‌کنند. فقط نمایش‌های وب تمام‌صفحه.
ام۱۳۹ پشتیبانی از ime() (ویرایشگر متد ورودی، که یک صفحه کلید است) از طریق تغییر اندازه نمای بصری. همه وب ویوها.
ام۱۴۴ پشتیبانی از displayCutout() و systemBars() همه وب‌ویوها (صرف نظر از حالت تمام صفحه).

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

مکانیک هسته

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

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

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

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

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

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

برای لغو این منطق پیش‌فرض و ارائه ابعاد کامل سیستم به محتوای وب، صرف نظر از همپوشانی، از متد setOnApplyWindowInsetsListener استفاده کنید و شیء windowInsets اصلی و اصلاح نشده را از شنونده برگردانید. ارائه ابعاد کامل سیستم می‌تواند با فعال کردن هماهنگی محتوای وب با سخت‌افزار دستگاه، صرف نظر از موقعیت فعلی WebView، به تضمین ثبات طراحی کمک کند. این امر انتقال روان را با حرکت یا گسترش 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. صفحه‌کلید پنهان می‌شود زیرا تمرکز از بین رفته است.

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

پیاده‌سازی مدیریت درج متن (inset)

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

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

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

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

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

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

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

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

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

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

کاتلین

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. قطع کردن insetها: از setOnApplyWindowInsetsListener استفاده کنید یا onApplyWindowInsets در یک زیرکلاس WebView نادیده بگیرید.

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