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

Compose yöntemini deneyin
Jetpack Compose, Android için önerilen kullanıcı arayüzü araç setidir. Compose'da klavyeyle nasıl çalışacağınızı öğrenin.

WindowInsetsCompat kullanarak uygulamanız, sistem çubuklarıyla etkileşim kurma şekline 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 de kullanabilir.

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

Ön koşullar

Yazılım klavyesi için kontrol ve animasyon ayarlamadan önce uygulamanızı uçtan uca gösterecek şekilde yapılandırın. Bu, sistem çubukları ve dokunmatik klavye gibi sistem penceresi yerleştirmelerinin işlenmesini sağlar.

Varsayılan senkronize edilmiş yerleştirme animasyonu

API düzeyi 37.2 ve sonraki sürümlerde, özel bir WindowInsetsAnimationCompat.Callback uygulamadan varsayılan pencere düzeni ayarlamalarını (ör. SOFT_INPUT_ADJUST_RESIZE veya SOFT_INPUT_ADJUST_PAN, WindowInsetsController.show(WindowInsets.Type.ime()) kullanılarak IME gösterilirken) kullanan uygulamalar, sistem düzeyinde senkronize edilmiş kare kare animasyonlardan yararlanabilir. Etkinleştirildiğinde bu özellik, her kareye otomatik olarak iç kısımlar uygular ve klavye hareket ederken sorunsuz bir geçiş sağlamak için düzen geçişini tetikler.

Bu davranışı etkinleştirmek için AndroidManifest.xml dosyasındaki etkinliğinize veya uygulamanıza aşağıdaki <property> etiketini ekleyin:

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

Ana iş parçacığı dışında ekleme animasyonları gerçekleştirme

Varsayılan olarak, WindowInsetsAnimationCompat.Callback kaydetmek, pencere yerleştirme animasyonunun uygulamanın ana iş parçacığında çalışmasını zorunlu kılar. Uygulamanız animasyon sırasında görünüm düzenini sürekli olarak güncelleyip senkronize ediyorsa (ör. onProgress yönteminde olduğu gibi) bu gereklidir. Ancak uygulamanızın yalnızca yaşam döngüsü geçişlerini (ör. IME animasyonunun başlatılması veya sonlandırılması) dinlemesi gerekiyorsa ve görünümleri kare kare güncellemesi gerekmiyorsa ana iş parçacığında çalışmak gereksiz ek yüke ve animasyon takılmalarına neden olabilir. Bu durum, özellikle ana iş parçacığı başka bir işlemle meşgulse geçerlidir.

API düzeyi 37.2 ve sonraki sürümlerde, bu geçişleri gözlemlemek için ViewTreeObserver.WindowInsetsAnimationListener kullanabilirsiniz. Bu işleyici, kare kare ilerleme durumu güncellemeleri almadığından (onProgress geri çağırma yok) sistem, ana iş parçacığı yerine özel bir animasyon iş parçacığında yerleştirme animasyonunu çalıştırabilir. Bu da daha sorunsuz bir geçiş sağlar.

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

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

Yazılım klavyesinin görünürlüğünü kontrol etmek için WindowInsets simgesini 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 etme

Bir kullanıcının metin giriş alanına dokunması, klavyenin ekranın alt kısmından yerine kaymasına neden olur. Bu durum aşağıdaki örnekte gösterilmiştir:

Şekil 2. Senkronize klavye animasyonu.
  • Şekil 2'deki "Senkronize edilmemiş" etiketli ö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 animasyonuyla senkronize olmak yerine yerine oturur. Bu davranış görsel olarak rahatsız edici olabilir.

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

Klavye animasyonuyla senkronize edilecek görünümü kullanarak WindowInsetsAnimationCompat.Callback 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 içinde geçersiz kılmak için çeşitli yöntemler vardır: onPrepare(), onStart(), onProgress(), ve onEnd(). Düzen değişikliklerinden önce onPrepare() ile aramaya başlayın.

onPrepare, bir içe yerleştirme animasyonu başlatıldığında ve görünümler animasyon nedeniyle yeniden düzenlenmeden önce çağrılır. Bu işlevi, başlangıç durumunu (bu örnekte görünümün alt koordinatı) kaydetmek için kullanabilirsiniz.

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

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çe yerleştirme 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 bu özelliği kullanabilirsiniz. Görünümlerden herhangi birine ayarlanmış bir OnApplyWindowInsetsListener geri çağırma varsa bu noktada zaten çağrılmıştır. Bu, görünüm özelliklerinin son durumunu kaydetmek için uygun bir zamandır.

Görünümün son durumdaki alt koordinatını gösteren resim
Şekil 4. onStart() kullanarak bitiş durumunu kaydetme.

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, animasyon çalıştırılırken dolgular değiştiğinde çağrılır. Bu nedenle, klavye animasyonu sırasında her karede geçersiz kılabilir ve bildirim alabilirsiniz. Görünüm özelliklerini, görünümün klavye ile senkronize olacak şekilde animasyon oluşturacak ş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 bu yöntemin her çağrısında değer kademeli olarak azalır ve sonunda orijinal düzen konumunda 0 değerine 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 değerini geçersiz kılabilirsiniz. Bu yöntem, animasyon sona erdikten sonra çağrılır. Bu, geçici değişiklikleri temizlemek için uygun bir zamandır.

Ek kaynaklar