Yazılım klavyesini kontrol etme ve animasyon çalıştırma

Uygulamanız, WindowInsetsCompat sayesinde sistem çubuklarıyla etkileşimine benzer şekilde dokunmatik klavyeyi (IME olarak da bilinir) sorgulayabilir ve kontrol edebilir. Uygulamanız, yazılım klavyesi açıldığında veya kapatıldığında sorunsuz geçişler oluşturmak için WindowInsetsAnimationCompat aracını da kullanabilir.

Şekil 1. Yazılım klavyesi açık-kapalı geçişine ilişkin iki örnek.

Ön koşullar

Yazılım klavyesi için kontrol ve animasyon ayarlamadan önce uygulamanızı uçtan uca görüntüleyecek şekilde yapılandırın. Bu, sistem çubukları ve dokunmatik klavye gibi sistem penceresi alt öğelerini işleyebilmesini sağlar.

Klavye yazılımının görünürlüğünü kontrol etme

Yazılım klavye görünürlüğünü kontrol etmek için WindowInsets öğesini kullanın.

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;

Alternatif olarak, yazılım klavyesinin görünürlüğündeki değişiklikleri gözlemlemek için ViewCompat.setOnApplyWindowInsetsListener aracını da kullanabilirsiniz.

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;
});

Animasyonu yazılım klavyesiyle senkronize etme

Bir kullanıcının metin giriş alanına dokunulduğunda, aşağıdaki örnekte gösterildiği gibi klavye, ekranın alt kısmından yerine kayarak yerine oturur:

Şekil 2. Senkronize klavye animasyonu.
  • Şekil 2'deki "Senkronize edilmemiş" etiketli örnek, Android 10'daki (API düzeyi 29) varsayılan davranışı göstermektedir. Burada metin alanı ve uygulamanın içeriği, klavyenin animasyoniyle senkronize etmek yerine yerine yerleşir. Bu davranış, görsel olarak rahatsız edici olabilir.

  • Android 11 (API düzeyi 30) ve sonraki sürümlerde uygulama geçişini, ekranın altından yukarı ve aşağı kayan klavyeyle senkronize etmek için WindowInsetsAnimationCompat kullanabilirsiniz. Şekil 2'deki "Senkronize edilmiş" etiketli örnekte gösterildiği gibi bu daha yumuşak görünür.

WindowInsetsAnimationCompat.Callback simgesini, klavye animasyonuyla senkronize edilecek görünümle yapılandırın.

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 ürününde geçersiz kılmanın birkaç yöntemi vardır: onPrepare(), onStart(), onProgress() ve onEnd(). Düzen herhangi bir değişiklik yapmadan önce onPrepare() komutunu çağırarak başlayın.

onPrepare, bir insets animasyonu başladığında ve görünümler animasyon nedeniyle yeniden yerleştirilmeden önce çağrılır. Başlangıç durumunu (bu örnekte, görünümün alt koordinatı) kaydetmek için bunu kullanabilirsiniz.

Kök görünümünün başlangıç durumu alt koordinatını gösteren resim.
Şekil 3. Başlangıç durumunu kaydetmek için onPrepare() kullanılıyor.

Aşağıdaki snippet'te onPrepare için örnek bir çağrı gösterilmektedir:

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();
}

Bir iç içe ekleme animasyonu başladığında onStart çağrılır. Tüm görünüm özelliklerini, düzen değişikliklerinin son durumuna ayarlamak için bunu kullanabilirsiniz. Görünümlerden herhangi birine ayarlanmış bir OnApplyWindowInsetsListener geri çağırmanız varsa zaten bu noktada çağrılır. Bu, görünüm özelliklerinin bitiş durumunu kaydetmek için iyi bir zamandır.

Görünümün bitiş durumu alt koordinatını gösteren resim
Şekil 4. Bitiş durumunu kaydetmek için onStart() kullanılıyor.

Aşağıdaki snippet'te onStart için örnek bir çağrı gösterilmektedir:

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, animasyonun çalıştırılması kapsamında ayarlar değiştiğinde çağrılır. Böylece, klavye animasyonu sırasında bunu geçersiz kılabilir ve her karede bildirim alabilirsiniz. Görünüm özelliklerini, görünümün klavyeyle senkronize olarak canlandırılacağı şekilde güncelleyin.

Bu noktada tüm düzen değişiklikleri tamamlanmıştır. Örneğin, görünümü değiştirmek için View.translationY kullanırsanız değer, bu yöntemin her çağrısında kademeli olarak azalır ve sonunda orijinal düzen konumuna 0 ulaşır.

Şekil 5. Animasyonları senkronize etmek için onProgress() kullanılıyor.

Aşağıdaki snippet'te onProgress için örnek bir çağrı gösterilmektedir:

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;
}

İsteğe bağlı olarak, onEnd politikasını geçersiz kılabilirsiniz. Bu yöntem, animasyon bittikten sonra çağrılır. Bu, geçici tüm değişiklikleri temizlemek için iyi bir zamandır.

Ek kaynaklar