WebView-তে উইন্ডো ইনসেট বোঝা

WebView দুটি ভিউপোর্ট ব্যবহার করে কন্টেন্ট অ্যালাইনমেন্ট ম্যানেজ করে: লেআউট ভিউপোর্ট (পৃষ্ঠার সাইজ) এবং ভিজ্যুয়াল ভিউপোর্ট (পৃষ্ঠার যে অংশটি ব্যবহারকারী আসলে দেখেন)। লেআউট ভিউপোর্ট সাধারণত স্ট্যাটিক হলেও, ব্যবহারকারী জুম বা স্ক্রল করলে অথবা সিস্টেম UI এলিমেন্ট (যেমন, সফ্টওয়্যার কীবোর্ড) দেখা গেলে, ভিজ্যুয়াল ভিউপোর্ট ডায়নামিক্যালি পরিবর্তিত হয়।

ফিচার কম্প্যাটিবিলিটি

ওয়েব কন্টেন্টের আচরণকে নেটিভ Android অ্যাপের প্রত্যাশার সাথে সারিবদ্ধ করতে, উইন্ডো ইনসেটের জন্য WebView-এর সহায়তা সময়ের সাথে সাথে পরিবর্তিত হয়েছে:

মাইলস্টোন ফিচার যোগ করা হয়েছে সুযোগ
M136 CSS safe-area-insets-এর মাধ্যমে displayCutout() ও systemBars() কাজ করে। শুধুমাত্র ফুল-স্ক্রিন WebView.
M139 ime() (ইনপুট পদ্ধতি এডিটর, যা একটি কীবোর্ড) ভিজ্যুয়াল ভিউপোর্ট রিসাইজ করার মাধ্যমে সাপোর্ট করে। সব WebView.
M144 displayCutout() এবং systemBars() সহায়তা। সব WebView (ফুল-স্ক্রিন স্ট্যাটাস যাই হোক না কেন)।

আরও তথ্যের জন্য, WindowInsetsCompat দেখুন।

কোর মেকানিক্স

WebView দুটি প্রাথমিক মেকানিজমের মাধ্যমে ইনসেট ম্যানেজ করে:

  • নিরাপদ জায়গা (displayCutout, systemBars): WebView এই ডাইমেনশনগুলি CSS safe-area-inset-* ভেরিয়েবলের মাধ্যমে ওয়েব কন্টেন্টে ফরওয়ার্ড করে। এটি ডেভেলপারদের নিজস্ব ইন্টার‍্যাক্টিভ এলিমেন্ট (যেমন নেভিগেশন বার) নচ বা স্ট্যাটাস বারের মাধ্যমে অস্পষ্ট হওয়া থেকে আটকাতে সাহায্য করে।

  • ইনপুট পদ্ধতি এডিটর (IME) ব্যবহার করে ভিজ্যুয়াল ভিউপোর্ট ছোট-বড় করা: M139 থেকে শুরু করে, ইনপুট পদ্ধতি এডিটর (IME) সরাসরি ভিজ্যুয়াল ভিউপোর্ট ছোট-বড় করে। এই ছোট-বড় করার পদ্ধতিও WebView-Window ইন্টারসেকশনের উপর ভিত্তি করে কাজ করে। যেমন, Android মাল্টিটাস্কিং মোডে, WebView-এর নিচের অংশ উইন্ডোর নিচের অংশের থেকে ২০০ডিপি বেশি হলে, ভিজ্যুয়াল ভিউপোর্ট WebView-এর সাইজের থেকে ২০০ডিপি ছোট হয়। এই ভিজ্যুয়াল ভিউপোর্ট রিসাইজিং (IME এবং WebView-Window ইন্টারসেকশন, দুটির জন্যই) শুধুমাত্র WebView-এর নিচের দিকে প্রয়োগ করা হয়। এই মেকানিজম বাঁদিকে, ডানদিকে বা উপরে ওভারল্যাপের জন্য ছোট-বড় করার সুবিধা দেয় না। এর অর্থ হল, এইসব প্রান্তে দেখানো ডক করা কীবোর্ড, ভিজ্যুয়াল ভিউপোর্ট ছোট বড় করার প্রক্রিয়া ট্রিগার করে না।

আগে, ভিজ্যুয়াল ভিউপোর্ট ফিক্সড থাকত, প্রায়ই কীবোর্ডের পিছনে ইনপুট ফিল্ড লুকিয়ে থাকত। ভিউ পোর্ট ছোট বড় করার মাধ্যমে, পৃষ্ঠার দৃশ্যমান অংশটি ডিফল্টভাবে স্ক্রলযোগ্য হয়ে যায়, যার ফলে ব্যবহারকারীরা অস্পষ্ট কন্টেন্ট দেখতে পান।

বাউন্ড ও ওভারল্যাপ লজিক

WebView-এর স্ক্রিন বাউন্ডের সাথে সিস্টেম UI এলিমেন্ট (বার, ডিসপ্লে কাটআউট বা কীবোর্ড) সরাসরি ওভারল্যাপ করলে তবেই WebView-এর নন-জিরো ইনসেট ভ্যালু পাওয়া উচিত। কোনও WebView যদি এইসব UI এলিমেন্টের সাথে ওভারল্যাপ না করে (যেমন, কোনও WebView স্ক্রিনের মাঝখানে থাকলে এবং সিস্টেম বার স্পর্শ না করলে), তাহলে এটি ইনসেট হিসেবে শূন্য পাবে।

এই ডিফল্ট লজিক ওভাররাইড করতে এবং ওভারল্যাপ নির্বিশেষে সম্পূর্ণ সিস্টেম ডাইমেনশন সহ ওয়েব কন্টেন্ট প্রদান করতে, setOnApplyWindowInsetsListener পদ্ধতি ব্যবহার করুন এবং লিসেনার থেকে আসল, অপরিবর্তিত windowInsets অবজেক্ট ফিরিয়ে দিন। সম্পূর্ণ সিস্টেম ডাইমেনশন প্রদান করলে, WebView-এর বর্তমান পজিশন যাই হোক না কেন, ডিভাইসের হার্ডওয়্যারের সাথে ওয়েব কন্টেন্ট অ্যালাইন করার মাধ্যমে ডিজাইনের ধারাবাহিকতা বজায় রাখতে সাহায্য করতে পারে। WebView স্ক্রিনের প্রান্তে পৌঁছানোর জন্য সরানোর বা বড় করার সময় এটি মসৃণ ট্রানজিশন নিশ্চিত করে।

Kotlin

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-এর ডিফল্ট সেটিংস বেশিরভাগ অ্যাপের জন্য অটোমেটিক কাজ করে। তবে, আপনার অ্যাপ যদি কাস্টম লেআউট ব্যবহার করে (যেমন, স্ট্যাটাস বার বা কীবোর্ডের জন্য আপনি যদি নিজের প্যাডিং যোগ করেন), তাহলে ওয়েব কন্টেন্ট ও নেটিভ UI কীভাবে একসাথে কাজ করে তা উন্নত করতে আপনি নিম্নলিখিত পদ্ধতি ব্যবহার করতে পারেন। আপনার নেটিভ UI যদি WindowInsets-এর উপর ভিত্তি করে কোনও কন্টেনারে প্যাডিং প্রয়োগ করে, তাহলে ডবল-প্যাডিং এড়াতে WebView-তে পৌঁছানোর আগে আপনাকে অবশ্যই এই ইনসেটগুলি সঠিকভাবে ম্যানেজ করতে হবে।

ডবল-প্যাডিং হল এমন একটি পরিস্থিতি যেখানে নেটিভ লেআউট এবং ওয়েব কন্টেন্ট একই ইনসেট ডাইমেনশন প্রয়োগ করে, যার ফলে অতিরিক্ত স্পেসিং হয়। যেমন, ধরুন একটি ফোনে ৪০ পিক্সেলের স্ট্যাটাস বার আছে। নেটিভ ভিউ এবং WebView, দুটিই ৪০ পিক্সেল ইনসেট দেখতে পায়। উভয়ই ৪০ পিক্সেল প্যাডিং যোগ করে, যার ফলে ব্যবহারকারী উপরে ৮০ পিক্সেল ফাঁকা জায়গা দেখতে পান।

জিরোয়িং পদ্ধতি

ডবল-প্যাডিং আটকাতে, আপনাকে নিশ্চিত করতে হবে যে নেটিভ ভিউ প্যাডিংয়ের জন্য ইনসেট ডাইমেনশন ব্যবহার করার পরে, আপনি নতুন WindowInsets অবজেক্টে Insets.NONE ব্যবহার করে সেই ডাইমেনশন রিসেট করে শূন্য করে দিয়েছেন। এটি করতে হবে পরিবর্তিত অবজেক্টটি ভিউ হায়ারার্কি থেকে WebView-তে পাস করার আগে।

পেরেন্ট ভিউতে প্যাডিং প্রয়োগ করার সময়, সাধারণত WindowInsetsCompat.CONSUMED-এর পরিবর্তে Insets.NONE সেট করে জিরো করার পদ্ধতি ব্যবহার করা উচিত। WindowInsetsCompat.CONSUMED-এ ফিরে যাওয়া নির্দিষ্ট কিছু পরিস্থিতিতে কাজ করতে পারে। তবে, আপনার অ্যাপের হ্যান্ডলার ইনসেট পরিবর্তন করলে বা নিজস্ব প্যাডিং যোগ করলে, এটি সমস্যার সম্মুখীন হতে পারে। জিরোইং অ্যাপ্রোচে এইসব সীমাবদ্ধতা নেই।

ইনসেট শূন্য করে ঘোস্ট প্যাডিং এড়ানো

অ্যাপটি আগে ব্যবহার না করা ইনসেট পাস করার পরে আপনি যদি ইনসেট ব্যবহার করেন বা ইনসেট পরিবর্তন হলে (যেমন কীবোর্ড লুকানো), সেগুলি ব্যবহার করলে WebView-কে প্রয়োজনীয় আপডেট বিজ্ঞপ্তি পেতে বাধা দেয়। এর ফলে WebView-তে আগের অবস্থার ঘোস্ট প্যাডিং থেকে যেতে পারে (যেমন, কীবোর্ড লুকানোর পরেও কীবোর্ড প্যাডিং থেকে যাওয়া)।

অ্যাপ ও WebView-এর মধ্যে কাজ না করা ইন্টার‍্যাকশনের উদাহরণ নিচে দেওয়া হল:

  1. প্রাথমিক অবস্থা: অ্যাপটি প্রাথমিকভাবে WebView-তে ব্যবহার না করা ইনসেট (যেমন, displayCutout() বা systemBars()) পাস করে, যা ইন্টার্নালি ওয়েব কন্টেন্টে প্যাডিং প্রয়োগ করে।
  2. স্টেট পরিবর্তন ও সমস্যা: অ্যাপ যদি স্টেট পরিবর্তন করে (যেমন, কীবোর্ড লুকানো) এবং অ্যাপ যদি WindowInsetsCompat.CONSUMED রিটার্ন করার মাধ্যমে ফলাফল ইনসেট ম্যানেজ করার সিদ্ধান্ত নেয়।
  3. বিজ্ঞপ্তি ব্লক করা হয়েছে: ইনসেট ব্যবহার করলে Android সিস্টেম WebView-তে ভিউ হায়ারার্কি অনুযায়ী প্রয়োজনীয় আপডেট বিজ্ঞপ্তি পাঠাতে পারে না।
  4. ঘোস্ট প্যাডিং: WebView আপডেট না পাওয়ার কারণে, এটি আগের অবস্থা থেকে প্যাডিং ধরে রাখে, যার ফলে ঘোস্ট প্যাডিং হয় (যেমন, কীবোর্ড লুকানোর পরেও কীবোর্ড প্যাডিং ধরে রাখা)।

পরিবর্তে, চাইল্ড ভিউতে অবজেক্ট পাস করার আগে হ্যান্ডেল করা টাইপ শূন্যে সেট করতে WindowInsetsCompat.Builder ব্যবহার করুন। এটি WebView-কে জানায় যে ভিউ হায়ারার্কি নিচে চালিয়ে যাওয়ার জন্য বিজ্ঞপ্তি চালু করার সময় সেই নির্দিষ্ট ইনসেটগুলি আগে থেকেই বিবেচনা করা হয়েছে।

Kotlin

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. ইনসেপ্ট ইনসেট: WebView সাবক্লাসে setOnApplyWindowInsetsListener ব্যবহার করুন অথবা onApplyWindowInsets ওভাররাইড করুন।

  2. ইনসেট খালি করা: শুরু থেকে ইনসেটের (যেমন, WindowInsetsCompat.CONSUMED) একটি ব্যবহৃত সেট ফিরিয়ে দিন। এই অ্যাকশন ইনসেট বিজ্ঞপ্তি WebView-তে প্রচার করা থেকে আটকায়, কার্যকরভাবে আধুনিক ভিউপোর্ট রিসাইজিং বন্ধ করে এবং WebView-কে তার প্রাথমিক ভিজ্যুয়াল ভিউপোর্ট সাইজ বজায় রাখতে বাধ্য করে।