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

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

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

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

পূর্বশর্ত

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

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

সফটওয়্যার কিবোর্ডের দৃশ্যমানতা পরীক্ষা করতে 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 ওভাররাইড করতে পারেন। অ্যানিমেশন শেষ হওয়ার পর এই মেথডটি কল করা হয়। যেকোনো অস্থায়ী পরিবর্তন গুছিয়ে নেওয়ার জন্য এটি একটি ভালো সময়।

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