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 klavyeyi nasıl kullanacağınızı öğrenin.

Uygulamanız, WindowInsetsCompat kullanarak dokunmatik klavyeyi (IME olarak da bilinir) sistem çubuklarıyla etkileşim kurma şekline benzer şekilde 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 API'sini de kullanabilir.

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

Ön koşullar

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

Varsayılan senkronize içe 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. WindowInsetsController.show(WindowInsets.Type.ime()) kullanarak IME'yi gösterirken SOFT_INPUT_ADJUST_RESIZE veya SOFT_INPUT_ADJUST_PAN) kullanan uygulamalar, sistem düzeyinde senkronize edilmiş kare kare animasyonlardan yararlanabilir. Bu özellik etkinleştirildiğinde, her kareye otomatik olarak ekler uygulanır ve düzen geçişi tetiklenir. Böylece klavye hareket ederken sorunsuz bir geçiş sağlanır.

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" />

İç boşluk animasyonlarını ana iş parçacığının dışında gerçekleştirme

Varsayılan olarak, bir WindowInsetsAnimationCompat.Callback kaydetmek, pencere iç metin 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üncelliyor ve senkronize ediyorsa (ör. onProgress yönteminde) bu gereklidir. Ancak uygulamanızın yalnızca yaşam döngüsü geçişlerini dinlemesi gerekiyorsa (ör. IME animasyonu başlattığında veya sonlandırdığında) ve görünümleri kare kare güncellemesi gerekmiyorsa ana iş parçacığında çalıştırmak, özellikle ana iş parçacığı başka bir şekilde meşgulse gereksiz ek yüke ve animasyon takılmasına neden olabilir.

API düzeyi 37.2 ve sonraki sürümlerde bu geçişleri gözlemlemek için ViewTreeObserver.WindowInsetsAnimationListener kullanabilirsiniz. Bu dinleyici, kare kare ilerleme güncellemeleri almadığından (onProgress geri çağırma yok) sistem, içe yerleştirme animasyonunu ana iş parçacığı yerine özel bir animasyon iş parçacığında çalıştırarak daha akıcı 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 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

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

Şekil 2. Senkronize klavye animasyonu.
  • Şekil 2'de "Senkronize edilmemiş" olarak etiketlenen örnek, Android 10'daki (API düzeyi 29) varsayılan davranışı göstermektedir. Bu davranışta, uygulamanın metin alanı ve 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 ekranın alt kısmından yukarı ve aşağı kayan klavyeyle senkronize etmek için WindowInsetsAnimationCompat kullanabilirsiniz. Bu, Şekil 2'de "Senkronize" olarak etiketlenen örnekte gösterildiği gibi daha akıcı görünür.

WindowInsetsAnimationCompat.Callback öğesini, 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 içinde geçersiz kılınacak birkaç yöntem vardır: onPrepare(), onStart(), onProgress(), ve onEnd(). Düzen değişikliklerinden önce onPrepare() yöntemini çağırarak başlayın.

onPrepare, bir içe yerleştirme animasyonu başladığında ve görünümler animasyon nedeniyle yeniden düzenlenmeden önce çağrılır. Bu değişkeni, başlangıç durumunu kaydetmek için kullanabilirsiniz. Bu durumda başlangıç durumu, görünümün alt koordinatıdır.

Kök görünümün başlangıç durumu alt koordinatını gösteren resim.
3.şekil 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();
}

onStart, içe yerleştirme animasyonu başladığında çağrılır. Bu yöntemi kullanarak tüm görünüm özelliklerini düzen değişikliklerinin son durumuna ayarlayabilirsiniz. Görünümlerden herhangi biri için ayarlanmış bir OnApplyWindowInsetsListener geri çağırma işleviniz varsa bu noktada zaten ç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ş durumundaki alt koordinatını gösteren resim
Şekil 4. Son durumu 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, bir animasyonun çalıştırılması kapsamında ekler değiştiğinde çağrılır. Bu sayede, klavye animasyonu sırasında her karede bildirim almak için bu yöntemi geçersiz kılabilirsiniz. Görünüm özelliklerini, görünümün klavyeyle senkronize şekilde animasyonlu olarak gösterileceği ş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ı için değer 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 ayarını geçersiz kılabilirsiniz. Bu yöntem, animasyon bittikten sonra çağrılır. Bu aşamada, geçici değişiklikleri temizleyebilirsiniz.

Ek kaynaklar