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 adlandırılır) 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 değerini de kullanabilir.

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

Ön koşullar

Yazılım klavyesi için kontrol ve animasyonu ayarlamadan önce uygulamanızı kenardan kenara görüntüleyecek şekilde yapılandırın. Bu sayede sistem çubukları ve dokunmatik klavye gibi sistem penceresi iç içe yerleştirilmelerini işleyebilir.

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 klavyesi görünürlüğünde yapılan değişiklikleri gözlemlemek için ViewCompat.setOnApplyWindowInsetsListener simgesini de 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

Kullanıcı bir metin giriş alanına dokunduğunda, aşağıdaki örnekte gösterildiği gibi klavye, ekranın alt kısmından yerine kayarak yerine yerleşir:

Şekil 2. Senkronize klavye animasyonu.
  • Şekil 2'de "Senkronize değil" olarak etiketlenen örnekte, Android 10'daki (API düzeyi 29) varsayılan davranış gösterilmektedir. Bu davranışta, metin alanı ve uygulamanın içeriği klavyenin animasyonunu senkronize etmek yerine yerine sabitlenir. 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. Bu, Şekil 2'de "Senkronize edildi" olarak etiketlenen örnekte gösterildiği gibi daha düzgün görünür.

WindowInsetsAnimationCompat.Callback görünümünü, klavye animasyonunu senkronize edecek şekilde 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'te geçersiz kılma için birkaç yöntem 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 içe yerleştirilmiş animasyon başladığında ve görünümler bir animasyon nedeniyle yeniden düzenlenmeden önce çağrılır. Başlangıç durumunu (bu durumda görünümün alt koordinatı) kaydetmek için 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() kullanma

Aşağıdaki snippet'te onPrepare işlevine yapılan ö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();
}

onStart, bir içe yerleştirilmiş animasyon başladığında çağrılır. Tüm görünüm özelliklerini, düzen değişikliklerinin son durumuna ayarlamak için bu özelliği kullanabilirsiniz. Görünümlerden herhangi birine ayarlanmış bir OnApplyWindowInsetsListener geri çağırma işleviniz varsa bu noktada çağrılmıştır. Bu, görünüm özelliklerinin bitiş durumunu kaydetmek için iyi bir zamandır.

Görünümün son durum alt koordinatını gösteren resim
Şekil 4. Bitiş durumunu kaydetmek için onStart()'ü kullanma

Aşağıdaki snippet'te onStart işlevine yapılan ö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, bir animasyon çalıştırmanın bir parçası olarak içe yerleştirilenler değiştiğinde çağrılır. Böylece, bu işlevi geçersiz kılabilir ve klavye animasyonu sırasında her karede bildirim alabilirsiniz. Görünüm özelliklerini, görünümün klavyeyle senkronize şekilde animasyonlu olarak hareket etmesi için 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() kullanma

Aşağıdaki snippet'te onProgress işlevine yapılan ö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 değerini geçersiz kılabilirsiniz. Bu yöntem, animasyon sona erdikten sonra çağrılır. Bu, geçici tüm değişiklikleri temizlemek için iyi bir zamandır.

Ek kaynaklar