Dengan menggunakan WindowInsetsCompat,
aplikasi Anda dapat mengkueri dan mengontrol keyboard virtual (juga disebut
IME) seperti
cara aplikasi berinteraksi dengan kolom sistem. Aplikasi Anda juga dapat menggunakan
WindowInsetsAnimationCompat
untuk membuat transisi yang lancar saat keyboard virtual dibuka atau ditutup.
Prasyarat
Sebelum menyiapkan kontrol dan animasi untuk keyboard virtual, konfigurasikan aplikasi Anda untuk menampilkan layar penuh. Hal ini memungkinkannya menangani inset jendela sistem seperti kolom sistem dan keyboard virtual.
Animasi inset tersinkronisasi default
Mulai API level 37.2 dan yang lebih tinggi, aplikasi yang menggunakan penyesuaian tata letak jendela default (seperti SOFT_INPUT_ADJUST_RESIZE atau SOFT_INPUT_ADJUST_PAN saat
menampilkan IME menggunakan WindowInsetsController.show(WindowInsets.Type.ime()))
tanpa mengimplementasikan WindowInsetsAnimationCompat.Callback kustom dapat memanfaatkan
animasi frame demi frame yang disinkronkan di tingkat sistem. Jika diaktifkan, fitur ini
akan otomatis menerapkan inset pada setiap frame dan memicu penerusan
tata letak, sehingga menciptakan transisi yang mulus saat keyboard bergerak.
Untuk mengaktifkan perilaku ini, tambahkan tag <property> berikut ke aktivitas atau
aplikasi Anda di file AndroidManifest.xml:
<property
android:name="android.window.PROPERTY_COMPAT_ALLOW_SYNCHRONIZED_INSETS_ANIMATION"
android:value="true" />
Melakukan animasi inset di luar thread utama
Secara default, mendaftarkan
WindowInsetsAnimationCompat.Callback akan menerapkan bahwa
animasi inset jendela berjalan di thread utama aplikasi. Hal ini diperlukan jika
aplikasi Anda terus memperbarui dan menyinkronkan tata letak tampilannya
selama animasi (seperti dalam metode onProgress). Namun, jika aplikasi Anda
hanya perlu memproses transisi siklus proses (seperti saat IME memulai atau
mengakhiri animasi) dan tidak perlu mengupdate tampilan frame demi frame, menjalankan di
thread utama dapat menyebabkan overhead yang tidak perlu dan animasi tersendat—terutama jika
thread utama sedang sibuk.
Dari level API 37.2 dan yang lebih tinggi, Anda dapat menggunakan
ViewTreeObserver.WindowInsetsAnimationListener
untuk mengamati transisi ini. Karena pemroses ini tidak menerima
update progres frame demi frame (tidak ada callback onProgress), sistem dapat menjalankan
animasi inset pada thread animasi khusus, bukan thread utama,
sehingga menghasilkan transisi yang lebih lancar.
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);
Memeriksa visibilitas software keyboard
Gunakan WindowInsets untuk memeriksa visibilitas
keyboard virtual.
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;
Atau, Anda dapat menggunakan
ViewCompat.setOnApplyWindowInsetsListener
untuk mengamati perubahan visibilitas keyboard virtual.
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; });
Menyinkronkan animasi dengan keyboard virtual
Saat pengguna mengetuk kolom input teks, keyboard akan muncul dari bagian bawah layar, seperti yang ditunjukkan dalam contoh berikut:
Contoh berlabel "Tidak disinkronkan" pada gambar 2 menunjukkan perilaku default di Android 10 (level API 29), yang menampilkan kolom teks dan konten aplikasi langsung muncul, bukan disinkronkan dengan animasi keyboard—perilaku yang dapat mengganggu secara visual.
Di Android 11 (level API 30) dan yang lebih tinggi, Anda dapat menggunakan
WindowInsetsAnimationCompatuntuk menyinkronkan transisi aplikasi dengan keyboard yang digeser ke atas dan ke bawah dari bagian bawah layar. Hal ini terlihat lebih lancar, seperti yang ditunjukkan dalam contoh berlabel "Disinkronkan" pada gambar 2.
Konfigurasikan
WindowInsetsAnimationCompat.Callback
dengan tampilan yang akan disinkronkan dengan animasi keyboard.
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. });
Ada beberapa metode yang harus diganti di WindowInsetsAnimationCompat.Callback,
yaitu
onPrepare(),
onStart(),
onProgress(),
dan
onEnd().
Mulailah dengan memanggil onPrepare() sebelum perubahan tata letak apa pun.
onPrepare dipanggil saat animasi inset dimulai dan sebelum tampilan
ditata ulang karena animasi. Anda dapat menggunakannya untuk menyimpan status awal,
yang dalam hal ini adalah koordinat bawah tampilan.
onPrepare() untuk
merekam status awal.
Cuplikan berikut menunjukkan contoh panggilan ke onPrepare:
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 dipanggil saat animasi inset dimulai. Anda dapat menggunakannya untuk menetapkan semua
properti tampilan ke status akhir perubahan tata letak. Jika Anda memiliki callback
OnApplyWindowInsetsListener yang ditetapkan ke salah satu tampilan, callback tersebut sudah
dipanggil pada tahap ini. Ini adalah saat yang tepat untuk menyimpan status akhir properti
tampilan.
onStart() untuk mencatat
status akhir.
Cuplikan berikut menunjukkan contoh panggilan ke onStart:
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 dipanggil saat inset berubah sebagai bagian dari menjalankan animasi,
sehingga Anda dapat menggantinya dan diberi tahu di setiap frame selama animasi
keyboard. Perbarui properti tampilan sehingga tampilan dianimasikan
secara sinkron dengan keyboard.
Semua perubahan tata letak telah selesai pada tahap ini. Misalnya, jika Anda menggunakan
View.translationY untuk menggeser tampilan, nilainya akan berkurang secara bertahap untuk setiap
panggilan metode ini dan pada akhirnya mencapai 0 ke posisi tata letak asli.
onProgress() untuk
menyinkronkan animasi.
Cuplikan berikut menunjukkan contoh panggilan ke onProgress:
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; }
Secara opsional, Anda dapat mengganti onEnd. Metode ini dipanggil setelah animasi
selesai. Ini adalah saat yang tepat untuk membersihkan perubahan sementara.
Referensi lainnya
- WindowInsetsAnimation di GitHub.