सॉफ़्टवेयर कीबोर्ड को कंट्रोल और ऐनिमेट करें

Compose को आज़माएं
Jetpack Compose, Android के लिए सुझाई गई यूज़र इंटरफ़ेस (यूआई) टूलकिट है. Compose में कीबोर्ड इस्तेमाल करने का तरीका जानें.

WindowInsetsCompat का इस्तेमाल करके, आपका ऐप्लिकेशन ऑन-स्क्रीन कीबोर्ड (इसे आईएमई भी कहा जाता है) के बारे में क्वेरी कर सकता है और उसे कंट्रोल कर सकता है. यह ठीक उसी तरह काम करता है जिस तरह यह सिस्टम बार के साथ इंटरैक्ट करता है. आपका ऐप्लिकेशन WindowInsetsAnimationCompat का इस्तेमाल करके, सॉफ़्टवेयर कीबोर्ड के खुलने या बंद होने पर, ट्रांज़िशन को बेहतर बना सकता है.

पहली इमेज. सॉफ़्टवेयर कीबोर्ड के खुलने और बंद होने के ट्रांज़िशन के दो उदाहरण.

ज़रूरी शर्तें

सॉफ़्टवेयर कीबोर्ड के लिए कंट्रोल और ऐनिमेशन सेट अप करने से पहले, अपने ऐप्लिकेशन को एज-टू-एज डिसप्ले के लिए कॉन्फ़िगर करें. इससे यह सिस्टम विंडो इनसेट को मैनेज कर पाता है. जैसे, सिस्टम बार और ऑन-स्क्रीन कीबोर्ड.

डिफ़ॉल्ट रूप से सिंक किया गया इनसेट ऐनिमेशन

एपीआई लेवल 37.2 और इसके बाद के वर्शन से, डिफ़ॉल्ट विंडो लेआउट अडजस्टमेंट का इस्तेमाल करने वाले ऐप्लिकेशन (जैसे, WindowInsetsController.show(WindowInsets.Type.ime()) का इस्तेमाल करके आईएमई दिखाते समय SOFT_INPUT_ADJUST_RESIZE या SOFT_INPUT_ADJUST_PAN), कस्टम WindowInsetsAnimationCompat.Callback को लागू किए बिना, सिस्टम-लेवल पर सिंक किए गए फ़्रेम-दर-फ़्रेम ऐनिमेशन का फ़ायदा ले सकते हैं. इस सुविधा के चालू होने पर, यह हर फ़्रेम पर इनसेट को अपने-आप लागू करती है और लेआउट पास को ट्रिगर करती है. इससे कीबोर्ड के मूव होने पर, ट्रांज़िशन आसानी से होता है.

इस सुविधा को चालू करने के लिए, AndroidManifest.xml फ़ाइल में अपनी गतिविधि या ऐप्लिकेशन में यह <property> टैग जोड़ें:

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

मुख्य थ्रेड से बाहर, इनसेट ऐनिमेशन दिखाना

डिफ़ॉल्ट रूप से, WindowInsetsAnimationCompat.Callback को रजिस्टर करने से यह पक्का होता है कि विंडो इनसेट ऐनिमेशन, ऐप्लिकेशन के मुख्य थ्रेड पर चलता है. यह तब ज़रूरी होता है, जब आपका ऐप्लिकेशन ऐनिमेशन के दौरान, अपने व्यू लेआउट को लगातार अपडेट और सिंक कर रहा हो. जैसे, onProgress तरीके में. हालांकि, अगर आपके ऐप्लिकेशन को सिर्फ़ लाइफ़साइकल ट्रांज़िशन (जैसे, जब आईएमई ऐनिमेशन शुरू या खत्म होता है) को सुनना है और व्यू को फ़्रेम-दर-फ़्रेम अपडेट करने की ज़रूरत नहीं है, तो मुख्य थ्रेड पर चलाने से ग़ैर-ज़रूरी ओवरहेड और ऐनिमेशन में रुकावट आ सकती है. खास तौर पर, अगर मुख्य थ्रेड किसी और काम में व्यस्त है.

एपीआई लेवल 37.2 और इसके बाद के वर्शन से, इन ट्रांज़िशन को देखने के लिए, ViewTreeObserver.WindowInsetsAnimationListener का इस्तेमाल किया जा सकता है. इस लिसनर को फ़्रेम-दर-फ़्रेम प्रोग्रेस अपडेट नहीं मिलते (कोई onProgress कॉलबैक नहीं), इसलिए सिस्टम, इनसेट ऐनिमेशन को मुख्य थ्रेड के बजाय, खास ऐनिमेशन थ्रेड पर चला सकता है. इससे ट्रांज़िशन ज़्यादा आसान हो जाता है.

Kotlin

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)

Java

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 का इस्तेमाल करें.

Kotlin

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

Java

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

इसके अलावा, सॉफ़्टवेयर कीबोर्ड दिखने की सेटिंग में हुए बदलावों को देखने के लिए, ViewCompat.setOnApplyWindowInsetsListener का इस्तेमाल किया जा सकता है.

Kotlin

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

Java

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

सॉफ़्टवेयर कीबोर्ड के साथ ऐनिमेशन सिंक करना

जब कोई उपयोगकर्ता टेक्स्ट इनपुट फ़ील्ड पर टैप करता है, तो कीबोर्ड स्क्रीन के सबसे निचले हिस्से से स्लाइड होकर ऊपर की ओर आता है. इसे यहां दिए गए उदाहरण में देखा जा सकता है:

दूसरी इमेज. सिंक किए गए कीबोर्ड का ऐनिमेशन.
  • फ़िगर 2 में "सिंक नहीं किया गया" लेबल वाला उदाहरण, Android 10 (एपीआई लेवल 29) में डिफ़ॉल्ट ऐक्शन और सेटिंग दिखाता है. इसमें ऐप्लिकेशन का टेक्स्ट फ़ील्ड और कॉन्टेंट, कीबोर्ड के ऐनिमेशन के साथ सिंक होने के बजाय, अपनी जगह पर स्नैप हो जाता है. यह ऐक्शन और सेटिंग, विज़ुअल के तौर पर खराब हो सकती है.

  • Android 11 (एपीआई लेवल 30) और इसके बाद के वर्शन में, WindowInsetsAnimationCompat का इस्तेमाल करके, ऐप्लिकेशन के ट्रांज़िशन को स्क्रीन के सबसे नीचे से ऊपर और नीचे की ओर स्लाइड होने वाले कीबोर्ड के साथ सिंक किया जा सकता है. यह ज़्यादा स्मूद दिखता है, जैसा कि इमेज 2 में "सिंक किया गया" लेबल वाले उदाहरण में दिखाया गया है.

WindowInsetsAnimationCompat.Callback को कॉन्फ़िगर करें, ताकि व्यू को कीबोर्ड ऐनिमेशन के साथ सिंक किया जा सके.

Kotlin

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

Java

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

WindowInsetsAnimationCompat.Callback में ओवरराइड करने के कई तरीके हैं. जैसे, onPrepare(), onStart(), onProgress(), और onEnd(). लेआउट में कोई भी बदलाव करने से पहले, onPrepare() को कॉल करें.

onPrepare को तब कॉल किया जाता है, जब इनसेट ऐनिमेशन शुरू हो रहा हो और ऐनिमेशन की वजह से व्यू को फिर से लेआउट करने से पहले. इसका इस्तेमाल, शुरुआती स्थिति को सेव करने के लिए किया जा सकता है. इस मामले में, यह व्यू का सबसे निचला कोऑर्डिनेट है.

रूट व्यू के शुरुआती स्टेटस के सबसे नीचे वाले कोऑर्डिनेट को दिखाने वाली इमेज.
तीसरी इमेज. शुरुआती स्थिति को रिकॉर्ड करने के लिए, onPrepare() का इस्तेमाल करना.

यहां दिए गए स्निपेट में, onPrepare को कॉल करने का एक सैंपल दिखाया गया है:

Kotlin

var startBottom = 0f

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

Java

float startBottom;

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

इनसेट ऐनिमेशन शुरू होने पर, onStart को कॉल किया जाता है. इसका इस्तेमाल करके, सभी व्यू प्रॉपर्टी को लेआउट में हुए बदलावों की आखिरी स्थिति पर सेट किया जा सकता है. अगर आपके पास किसी भी व्यू पर सेट किया गया OnApplyWindowInsetsListener कॉलबैक है, तो उसे इस समय पहले ही कॉल किया जा चुका है. यह व्यू प्रॉपर्टी की आखिरी स्थिति को सेव करने का सही समय है.

व्यू के आखिरी स्टेटस के निचले हिस्से के कोऑर्डिनेट दिखाने वाली इमेज
इमेज 4. onStart() का इस्तेमाल करके, आखिरी स्थिति को रिकॉर्ड करना.

यहां दिए गए स्निपेट में, onStart को कॉल करने का एक सैंपल दिखाया गया है:

Kotlin

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
}

Java

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 को कॉल करने का एक सैंपल दिखाया गया है:

Kotlin

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
}

Java

@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 को बदलने का विकल्प होता है. ऐनिमेशन खत्म होने के बाद, इस तरीके को कॉल किया जाता है. यह कुछ समय के लिए किए गए बदलावों को हटाने का सही समय है.

अन्य संसाधन