সফ্টওয়্যার কীবোর্ড নিয়ন্ত্রণ এবং অ্যানিমেট করুন

কম্পোজ পদ্ধতিটি চেষ্টা করুন
জেটপ্যাক কম্পোজ হলো অ্যান্ড্রয়েডের জন্য প্রস্তাবিত UI টুলকিট। কম্পোজে কীভাবে কিবোর্ড ব্যবহার করতে হয় তা জেনে নিন।

WindowInsetsCompat ব্যবহার করে, আপনার অ্যাপ সিস্টেম বারের সাথে যেভাবে কাজ করে, ঠিক সেভাবেই অন-স্ক্রিন কীবোর্ড (যাকে IME- ও বলা হয়) কোয়েরি এবং নিয়ন্ত্রণ করতে পারে। এছাড়াও, সফটওয়্যার কীবোর্ড খোলা বা বন্ধ করার সময় নির্বিঘ্ন ট্রানজিশন তৈরি করতে আপনার অ্যাপ WindowInsetsAnimationCompat ব্যবহার করতে পারে।

চিত্র ১. সফটওয়্যার কিবোর্ড খোলা ও বন্ধ হওয়ার দুটি উদাহরণ।

পূর্বশর্ত

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

ডিফল্ট সিঙ্ক্রোনাইজড ইনসেট অ্যানিমেশন

এপিআই লেভেল ৩৭.২ এবং তার উপরের সংস্করণ থেকে, যে অ্যাপ্লিকেশনগুলো কাস্টম WindowInsetsAnimationCompat.Callback ইমপ্লিমেন্ট না করে ডিফল্ট উইন্ডো লেআউট অ্যাডজাস্টমেন্ট (যেমন WindowInsetsController.show(WindowInsets.Type.ime()) ) ব্যবহার করে IME দেখানোর সময় SOFT_INPUT_ADJUST_RESIZE বা SOFT_INPUT_ADJUST_PAN ) ব্যবহার করে, তারা সিস্টেম-লেভেলের সিনক্রোনাইজড ফ্রেম-বাই-ফ্রেম অ্যানিমেশনের সুবিধা পেতে পারে। এটি চালু করা হলে, এই ফিচারটি স্বয়ংক্রিয়ভাবে প্রতিটি ফ্রেমে ইনসেটগুলো প্রয়োগ করে এবং একটি লেআউট পাস ট্রিগার করে, যা কিবোর্ড নাড়াচাড়ার সাথে সাথে একটি মসৃণ ট্রানজিশন তৈরি করে।

এই আচরণটি সক্রিয় করতে, আপনার অ্যাক্টিভিটি বা অ্যাপ্লিকেশনের AndroidManifest.xml ফাইলে নিম্নলিখিত <property> ট্যাগটি যোগ করুন:

<property
    android:name="android.window.PROPERTY_COMPAT_ALLOW_SYNCHRONIZED_INSETS_ANIMATION"
    android:value="true" />

মূল থ্রেডের বাইরে ইনসেট অ্যানিমেশনগুলি সম্পাদন করুন।

ডিফল্টরূপে, WindowInsetsAnimationCompat.Callback রেজিস্টার করলে উইন্ডো ইনসেট অ্যানিমেশনটি অ্যাপের প্রধান থ্রেডে রান করা নিশ্চিত হয়। যদি আপনার অ্যাপ অ্যানিমেশন চলাকালীন ক্রমাগত তার ভিউ লেআউট আপডেট এবং সিনক্রোনাইজ করতে থাকে (যেমন onProgress মেথডে), তবে এটি প্রয়োজনীয়। তবে, যদি আপনার অ্যাপের শুধুমাত্র লাইফসাইকেল ট্রানজিশনগুলো (যেমন যখন IME-এর অ্যানিমেশন শুরু বা শেষ হয়) শোনার প্রয়োজন হয় এবং ফ্রেম-বাই-ফ্রেম ভিউ আপডেট করার দরকার না থাকে, তাহলে প্রধান থ্রেডে রান করলে অপ্রয়োজনীয় ওভারহেড এবং অ্যানিমেশনে স্টাটার হতে পারে—বিশেষ করে যদি প্রধান থ্রেডটি অন্য কোনো কাজে ব্যস্ত থাকে।

এপিআই লেভেল ৩৭.২ এবং তার উপরের সংস্করণ থেকে, আপনি এই ট্রানজিশনগুলো পর্যবেক্ষণ করার জন্য ViewTreeObserver.WindowInsetsAnimationListener ব্যবহার করতে পারেন। যেহেতু এই লিসেনারটি ফ্রেম-বাই-ফ্রেম অগ্রগতির আপডেট পায় না ( onProgress কলব্যাক নেই), তাই সিস্টেম মূল থ্রেডের পরিবর্তে একটি ডেডিকেটেড অ্যানিমেশন থ্রেডে ইনসেট অ্যানিমেশনটি চালাতে পারে, যার ফলে একটি মসৃণ ট্রানজিশন হয়।

কোটলিন

val listener = object : ViewTreeObserver.WindowInsetsAnimationListener {
  override fun onPrepare(animation: WindowInsetsAnimation) {
    // Handle preparation before animation starts
  }

  override fun onEnd(animation: WindowInsetsAnimation) {
    // Clean up temporary changes
  }
}

// Add the listener
view.viewTreeObserver.addWindowInsetsAnimationListener(listener)

// Remove the listener when no longer needed
view.viewTreeObserver.removeWindowInsetsAnimationListener(listener)

জাভা

ViewTreeObserver.WindowInsetsAnimationListener listener =
    new ViewTreeObserver.WindowInsetsAnimationListener() {
      @Override
      public void onPrepare(@NonNull WindowInsetsAnimation animation) {
        // Handle preparation before animation starts
      }

      @Override
      public void onEnd(@NonNull WindowInsetsAnimation animation) {
        // Clean up temporary changes
      }
    };

// Add the listener
view.getViewTreeObserver().addWindowInsetsAnimationListener(listener);

// Remove the listener when no longer needed
view.getViewTreeObserver().removeWindowInsetsAnimationListener(listener);

কীবোর্ড সফটওয়্যারের দৃশ্যমানতা পরীক্ষা করুন

সফটওয়্যার কিবোর্ডের দৃশ্যমানতা পরীক্ষা করতে WindowInsets ব্যবহার করুন।

কোটলিন

val insets = ViewCompat.getRootWindowInsets(view) ?: return
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom

জাভা

WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(view);
boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;

বিকল্পভাবে, সফটওয়্যার কীবোর্ডের দৃশ্যমানতার পরিবর্তন পর্যবেক্ষণ করতে আপনি ViewCompat.setOnApplyWindowInsetsListener ব্যবহার করতে পারেন।

কোটলিন

ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
  val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
  val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
  insets
}

জাভা

ViewCompat.setOnApplyWindowInsetsListener(view, (v, insets) -> {
  boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
  int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
  return insets;
});

সফটওয়্যার কিবোর্ডের সাথে অ্যানিমেশন সিঙ্ক্রোনাইজ করুন

ব্যবহারকারী কোনো টেক্সট ইনপুট ফিল্ডে ট্যাপ করলে কিবোর্ডটি স্ক্রিনের নিচ থেকে স্লাইড করে যথাস্থানে চলে আসে, যেমনটি নিচের উদাহরণে দেখানো হয়েছে:

চিত্র ২. সমন্বিত কিবোর্ড অ্যানিমেশন।
  • চিত্র ২-এ "Unsynchronized" লেবেলযুক্ত উদাহরণটি অ্যান্ড্রয়েড ১০ (এপিআই লেভেল ২৯)-এর ডিফল্ট আচরণ প্রদর্শন করে, যেখানে টেক্সট ফিল্ড এবং অ্যাপের কন্টেন্ট কিবোর্ডের অ্যানিমেশনের সাথে সিঙ্ক্রোনাইজ না হয়ে হঠাৎ করে নিজ নিজ স্থানে বসে যায়—এই আচরণটি দেখতে দৃষ্টিকটু হতে পারে।

  • অ্যান্ড্রয়েড ১১ (এপিআই লেভেল ৩০) এবং এর পরবর্তী সংস্করণগুলোতে, স্ক্রিনের নিচ থেকে কিবোর্ডের উপরে-নিচে স্লাইড করার সাথে অ্যাপের ট্রানজিশন সিঙ্ক্রোনাইজ করতে আপনি WindowInsetsAnimationCompat ব্যবহার করতে পারেন। এটি দেখতে আরও মসৃণ লাগে, যেমনটি চিত্র ২-এর "সিঙ্ক্রোনাইজড" লেবেলযুক্ত উদাহরণটিতে দেখানো হয়েছে।

কিবোর্ড অ্যানিমেশনের সাথে সিঙ্ক্রোনাইজ করার জন্য ভিউটি দিয়ে WindowInsetsAnimationCompat.Callback কনফিগার করুন।

কোটলিন

ViewCompat.setWindowInsetsAnimationCallback(
  view,
  object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
    // Override methods.
  }
)

জাভা

ViewCompat.setWindowInsetsAnimationCallback(
    view,
    new WindowInsetsAnimationCompat.Callback(
        WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP
    ) {
      // Override methods.
    });

WindowInsetsAnimationCompat.Callback এ ওভাররাইড করার মতো বেশ কয়েকটি মেথড রয়েছে, যেমন onPrepare() , onStart() , onProgress() , এবং onEnd() । লেআউটের যেকোনো পরিবর্তনের আগে onPrepare() কল করার মাধ্যমে শুরু করুন।

যখন একটি ইনসেট অ্যানিমেশন শুরু হয় এবং অ্যানিমেশনের কারণে ভিউগুলো পুনরায় বিন্যস্ত হওয়ার আগে onPrepare কল করা হয়। আপনি এটি ব্যবহার করে প্রারম্ভিক অবস্থা সংরক্ষণ করতে পারেন, যা এই ক্ষেত্রে ভিউটির বটম কোঅর্ডিনেট।

একটি চিত্র যা রুট ভিউ-এর প্রারম্ভিক অবস্থার বটম কোঅর্ডিনেট দেখাচ্ছে।
চিত্র ৩। প্রারম্ভিক অবস্থা লিপিবদ্ধ করতে onPrepare() এর ব্যবহার।

নিম্নলিখিত কোড স্নিপেটটি onPrepare এর একটি নমুনা কল দেখাচ্ছে:

কোটলিন

var startBottom = 0f

override fun onPrepare(
  animation: WindowInsetsAnimationCompat
) {
  startBottom = view.bottom.toFloat()
}

জাভা

float startBottom;

@Override
public void onPrepare(
    @NonNull WindowInsetsAnimationCompat animation
) {
  startBottom = view.getBottom();
}

যখন একটি ইনসেট অ্যানিমেশন শুরু হয়, তখন onStart কল করা হয়। লেআউট পরিবর্তনের শেষ অবস্থায় সমস্ত ভিউ প্রপার্টি সেট করতে আপনি এটি ব্যবহার করতে পারেন। যদি কোনো ভিউতে OnApplyWindowInsetsListener কলব্যাক সেট করা থাকে, তবে সেটি এই পর্যায়ে ইতিমধ্যেই কল করা হয়ে যায়। ভিউ প্রপার্টিগুলোর শেষ অবস্থা সংরক্ষণ করার জন্য এটি একটি উপযুক্ত সময়।

একটি চিত্র যা ভিউটির শেষ অবস্থার বটম স্থানাঙ্ক দেখাচ্ছে।
চিত্র ৪। শেষ অবস্থা লিপিবদ্ধ করতে onStart() এর ব্যবহার।

নিম্নলিখিত কোড স্নিপেটটি onStart এর একটি নমুনা কল দেখাচ্ছে:

কোটলিন

var endBottom = 0f

override fun onStart(
  animation: WindowInsetsAnimationCompat,
  bounds: WindowInsetsAnimationCompat.BoundsCompat
): WindowInsetsAnimationCompat.BoundsCompat {
  // Record the position of the view after the IME transition.
  endBottom = view.bottom.toFloat()

  return bounds
}

জাভা

float endBottom;

@NonNull
@Override
public WindowInsetsAnimationCompat.BoundsCompat onStart(
    @NonNull WindowInsetsAnimationCompat animation,
    @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds
) {
  endBottom = view.getBottom();
  return bounds;
}

অ্যানিমেশন চলার অংশ হিসেবে যখন ইনসেটগুলো পরিবর্তিত হয়, তখন onProgress কল করা হয়। তাই আপনি এটিকে ওভাররাইড করে কিবোর্ড অ্যানিমেশনের প্রতিটি ফ্রেমে নোটিফিকেশন পেতে পারেন। ভিউ প্রোপার্টিগুলো আপডেট করুন, যাতে ভিউটি কিবোর্ডের সাথে সিঙ্ক্রোনাইজেশনে অ্যানিমেট হয়।

এই পর্যায়ে লেআউটের সমস্ত পরিবর্তন সম্পন্ন হয়েছে। উদাহরণস্বরূপ, যদি আপনি ভিউটি সরাতে View.translationY ব্যবহার করেন, তাহলে এই মেথডটির প্রতিটি কলের সাথে মানটি ক্রমান্বয়ে কমতে থাকে এবং অবশেষে মূল লেআউট অবস্থানে অর্থাৎ 0 তে পৌঁছায়।

চিত্র ৫। অ্যানিমেশনগুলো সিঙ্ক্রোনাইজ করতে onProgress() এর ব্যবহার।

নিম্নলিখিত কোড স্নিপেটটি onProgress এর একটি নমুনা কল দেখাচ্ছে:

কোটলিন

override fun onProgress(
  insets: WindowInsetsCompat,
  runningAnimations: MutableList<WindowInsetsAnimationCompat>
): WindowInsetsCompat {
  // Find an IME animation.
  val imeAnimation = runningAnimations.find {
    it.typeMask and WindowInsetsCompat.Type.ime() != 0
  } ?: return insets

  // Offset the view based on the interpolated fraction of the IME animation.
  view.translationY =
    (startBottom - endBottom) * (1 - imeAnimation.interpolatedFraction)

  return insets
}

জাভা

@NonNull
@Override
public WindowInsetsCompat onProgress(
    @NonNull WindowInsetsCompat insets,
    @NonNull List<WindowInsetsAnimationCompat> runningAnimations
) {
  // Find an IME animation.
  WindowInsetsAnimationCompat imeAnimation = null;
  for (WindowInsetsAnimationCompat animation : runningAnimations) {
    if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
      imeAnimation = animation;
      break;
    }
  }
  if (imeAnimation != null) {
    // Offset the view based on the interpolated fraction of the IME animation.
    view.setTranslationY((startBottom - endBottom)

        *   (1 - imeAnimation.getInterpolatedFraction()));
  }
  return insets;
}

ঐচ্ছিকভাবে, আপনি onEnd ওভাররাইড করতে পারেন। অ্যানিমেশন শেষ হওয়ার পর এই মেথডটি কল করা হয়। যেকোনো অস্থায়ী পরিবর্তন গুছিয়ে নেওয়ার জন্য এটি একটি ভালো সময়।

অতিরিক্ত সম্পদ