ওয়েবভিউতে উইন্ডো ইনসেটগুলি বুঝুন

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

বৈশিষ্ট্য সামঞ্জস্য

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

মাইলফলক বৈশিষ্ট্য যোগ করা হয়েছে পরিধি
এম১৩৬ CSS safe-area-insets-এর মাধ্যমে displayCutout() এবং systemBars() সমর্থন। শুধুমাত্র পূর্ণস্ক্রিন ওয়েবভিউ।
এম১৩৯ ভিজ্যুয়াল ভিউপোর্ট রিসাইজিংয়ের মাধ্যমে ime() (ইনপুট মেথড এডিটর, যা একটি কীবোর্ড) সমর্থন। সকল ওয়েবভিউ।
এম১৪৪ displayCutout() এবং systemBars() সমর্থন। সকল ওয়েবভিউ (পূর্ণস্ক্রিন অবস্থা নির্বিশেষে)।

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

মূল মেকানিক্স

WebView দুটি প্রধান পদ্ধতির মাধ্যমে ইনসেট পরিচালনা করে:

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

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

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

সীমানা এবং ওভারল্যাপ যুক্তি

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

এই আচরণটি প্রশমিত করতে, ওয়েব-সাইড লিসেনারগুলো পর্যালোচনা করে নিশ্চিত করুন যে ভিউপোর্ট পরিবর্তনের ফলে অনিচ্ছাকৃতভাবে blur() জাভাস্ক্রিপ্ট ফাংশন বা ফোকাস-ক্লিয়ারিং আচরণগুলো চালু না হয়ে যায়।

ইনসেট হ্যান্ডলিং বাস্তবায়ন করুন

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

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

শূন্য করার পদ্ধতি

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

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

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

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

নিম্নলিখিত উদাহরণটি অ্যাপ এবং ওয়েবভিউ-এর মধ্যে একটি ত্রুটিপূর্ণ মিথস্ক্রিয়া প্রদর্শন করে:

  1. প্রাথমিক অবস্থা: অ্যাপটি শুরুতে অব্যবহৃত ইনসেট (যেমন, displayCutout() বা systemBars() ) WebView-তে পাঠায়, যা অভ্যন্তরীণভাবে ওয়েব কন্টেন্টে প্যাডিং প্রয়োগ করে।
  2. অবস্থার পরিবর্তন এবং ত্রুটি: যদি অ্যাপটির অবস্থা পরিবর্তিত হয় (উদাহরণস্বরূপ, কীবোর্ড লুকিয়ে যায়) এবং অ্যাপটি এর ফলে সৃষ্ট ইনসেটগুলো পরিচালনা করার জন্য WindowInsetsCompat.CONSUMED রিটার্ন করার সিদ্ধান্ত নেয়।
  3. নোটিফিকেশন অবরুদ্ধ: ইনসেটগুলি ব্যবহার করলে অ্যান্ড্রয়েড সিস্টেম ভিউ হায়ারার্কি বরাবর ওয়েবভিউতে প্রয়োজনীয় আপডেট নোটিফিকেশন পাঠাতে পারে না।
  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 ব্যবহার করুন অথবা একটি WebView সাবক্লাসে onApplyWindowInsets ওভাররাইড করুন।

  2. ইনসেট মুছে ফেলুন: শুরুতেই ব্যবহৃত ইনসেটগুলোর একটি সেট (যেমন, WindowInsetsCompat.CONSUMED ) ফেরত দিন। এই পদক্ষেপটি ইনসেট নোটিফিকেশনকে ওয়েবভিউতে পৌঁছানো থেকে সম্পূর্ণরূপে বিরত রাখে, যার ফলে আধুনিক ভিউপোর্ট রিসাইজিং কার্যকরভাবে নিষ্ক্রিয় হয়ে যায় এবং ওয়েবভিউ তার প্রাথমিক ভিজ্যুয়াল ভিউপোর্ট সাইজ ধরে রাখতে বাধ্য হয়।