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

Uygulamanız, WindowInsetsCompat kullanarak dokunmatik klavyeyi (IME olarak da adlandırılır) sistem çubuklarıyla etkileşim şekline benzer şekilde sorgulayıp 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şinin iki örneği.

Ön koşullar

Yazılım klavyesi için kontrolü ve animasyonu ayarlamadan önce, uygulamanızı uçtan uca görüntüleyecek şekilde yapılandırın. Bu işlem, sistem çubukları ve dokunmatik klavye gibi sistem penceresi eklerini işlemesine olanak tanır.

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

Yazılım klavyesinin görünürlüğünü kontrol etmek için WindowInsets düğmesini 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üğündeki değişiklikleri gözlemlemek için ViewCompat.setOnApplyWindowInsetsListener 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 eder

Aşağıdaki örnekte gösterildiği gibi, metin giriş alanına dokunan bir kullanıcı, klavyenin ekranın alt kısmından yerine kaymasına neden olur:

Şekil 2. Senkronize klavye animasyonu.
  • Şekil 2'deki "Senkronize edilmemiş" etiketli örnekte, Android 10'daki (API düzeyi 29) varsayılan davranış gösterilmiştir. Bu davranışta, uygulama metin alanı ve içeriği klavyenin animasyonuyla senkronize edilmeden yerine tutturulur. Bu davranış görsel olarak sarsıcı olabilir.

  • Android 11 (API düzeyi 30) ve sonraki sürümlerde, uygulamanın geçişini klavyenin ekranın altından yukarı ve aşağı kaydırmasıyla senkronize etmek için WindowInsetsAnimationCompat uygulamasını kullanabilirsiniz. Bu işlem daha düzgün görünür. Şekil 2'deki "Senkronize edilmiş" etiketli örnekte gösterildiği gibi.

WindowInsetsAnimationCompat.Callback'i, 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(). Herhangi bir düzen değişikliğinden önce onPrepare() çağrısıyla başlayın.

onPrepare, bir inset animasyonu başladığında ve görünümler animasyon nedeniyle yeniden düzenlenmeden önce çağrılır. Bunu, görünümün alt koordinatı olan başlangıç durumunu 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() 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 insets animasyonu başladığında onStart çağrılır. Bunu, tüm görünüm özelliklerini düzen değişikliklerinin son durumuna ayarlamak için kullanabilirsiniz. Görüntülemelerden herhangi birine ayarlanmış bir OnApplyWindowInsetsListener geri çağırmanız varsa zaten bu noktada çağrılır. Bu, görünüm özelliklerinin son durumunu kaydetmek için iyi bir zamandır.

Görünümün bitiş durumunun 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;
}

Bir animasyonun çalıştırılması sırasında ekler değiştiğinde onProgress çağrılır. Böylece bunu geçersiz kılabilir ve klavye animasyonu sırasında her karede bilgilendirilebilirsiniz. Görünüm özelliklerini, görünümün klavyeyle senkronizasyon sırasında canlandırılacağı şekilde güncelleyin.

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

Şekil 5. Animasyonları senkronize etmek için onProgress() kullanma.

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 sona erdikten sonra çağrılır. Bu, geçici değişiklikleri temizlemek için iyi bir zamandır.

Ek kaynaklar