بااستفاده از WindowInsetsCompat،
برنامه شما میتواند صفحهکلید مجازی (که به آن
IME نیز گفته میشود) را مشابه
نحوه تعامل آن با نوارهای سیستم، پرسمان و کنترل کند. برنامه شما همچنین میتواند از
WindowInsetsAnimationCompat
استفاده کند تا هنگام باز یا بسته شدن صفحهکلید نرمافزاری، انتقالهای یکپارچه ایجاد کند.
پیشنیازها
قبلاز راهاندازی کنترل و پویانمایی برای صفحهکلید نرمافزاری، برنامهتان را برای نمایش لبهبهلبه پیکربندی کنید. این کار به آن اجازه میدهد دروننشستهای پنجره سیستم مانند نوار سیستم و صفحهکلید مجازی را مدیریت کند.
پویانمایی پیشفرض درجهای همگامسازیشده
از سطح میانای برنامهسازی کاربردی ۳۷.۲ و بالاتر، برنامههایی که از تنظیمات پیشفرض چیدمان پنجره (مانند SOFT_INPUT_ADJUST_RESIZE یا SOFT_INPUT_ADJUST_PAN هنگام نمایش IME بااستفاده از WindowInsetsController.show(WindowInsets.Type.ime())) بدون پیادهسازی WindowInsetsAnimationCompat.Callback سفارشی استفاده میکنند، میتوانند از پویانماییهای همگامسازیشده فریمبهفریم در سطح سیستم بهرهمند شوند. وقتی این ویژگی فعال باشد، بهطور خودکار تورفتگیها را روی هر قاب اعمال میکند و یک گذر چیدمان را راهاندازی میکند، که باعث ایجاد یک انتقال روان هنگام حرکت صفحهکلید میشود.
برای فعال کردن این رفتار، برچسب <property> زیر را به فعالیت یا
برنامه خود در فایل AndroidManifest.xml اضافه کنید:
<property
android:name="android.window.PROPERTY_COMPAT_ALLOW_SYNCHRONIZED_INSETS_ANIMATION"
android:value="true" />
انجام پویانماییهای درجشده خارج از رشته اصلی
بهطور پیشفرض، ثبت
WindowInsetsAnimationCompat.Callback باعث میشود که
پویانمایی درج پنجره در رشته اصلی برنامه اجرا شود. اگر برنامهتان بهطور مداوم چیدمان نمای خود را درطول پویانمایی بهروزرسانی و همگامسازی میکند (مثلاً در روش onProgress)، این کار ضروری است. بااینحال، اگر برنامه شما فقط نیاز به گوش دادن به انتقالهای چرخه حیات دارد (مانند زمانی که IME شروع یا پایان پویانمایی میکند) و نیازی به بهروزرسانی نماها فریمبهفریم ندارد، اجرا در رشته اصلی میتواند باعث سربار غیرضروری و لکنت پویانمایی شود—بهویژه اگر رشته اصلی در غیر این صورت مشغول باشد.
از سطح میانای برنامهسازی کاربردی ۳۷.۲ و بالاتر، میتوانید از
ViewTreeObserver.WindowInsetsAnimationListener
برای مشاهده این انتقالها استفاده کنید. چون این شنونده بهروزرسانیهای پیشرفت فریمبهفریم را دریافت نمیکند (بدون callback onProgress)، سیستم میتواند پویانمایی درج را بهجای رشته اصلی در یک رشته پویانمایی اختصاصی اجرا کند که منجر به انتقال روانتر میشود.
کاتلین
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)
جاوا
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);
بررسی رؤیتپذیری نرمافزار صفحهکلید
از WindowInsets برای بررسی نمایان بودن صفحهکلید نرمافزاری استفاده کنید.
کاتلین
val insets = ViewCompat.getRootWindowInsets(view) ?: return val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
جاوا
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(view); boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
یا میتوانید از
ViewCompat.setOnApplyWindowInsetsListener
برای مشاهده تغییرات در رؤیتپذیری صفحهکلید نرمافزاری استفاده کنید.
کاتلین
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets -> val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom insets }
جاوا
ViewCompat.setOnApplyWindowInsetsListener(view, (v, insets) -> { boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom; return insets; });
همگامسازی پویانمایی با صفحهکلید نرمافزاری
وقتی کاربر روی فیلد ورودی نوشتار تکضرب میزند، صفحهکلید از پایین صفحه به جای خود میلغزد، همانطور که در مثال زیر نشان داده شده است:
نمونهای که در شکل ۲ با برچسب «Unsynchronized» (همگامسازینشده) مشخص شده است، رفتار پیشفرض در Android 10 (سطح API ۲۹) را نشان میدهد که در آن فیلد نوشتاری و محتوای برنامه بهجای همگامسازی با پویانمایی صفحهکلید، بهسرعت در جای خود قرار میگیرند—رفتاری که میتواند ازنظر بصری ناخوشایند باشد.
در Android نسخه ۱۱ (سطح میانای برنامهسازی کاربردی ۳۰) و بالاتر، میتوانید از
WindowInsetsAnimationCompatبرای همگامسازی انتقال برنامه با لغزاندن صفحهکلید به بالا و پایین از پایین صفحه استفاده کنید. این حالت همانطور که در نمونه با برچسب «همگامسازیشده» در شکل ۲ نشان داده شده است، روانتر بهنظر میرسد.
WindowInsetsAnimationCompat.Callback
را با نمایی که باید با پویانمایی صفحهکلید همگامسازی شود پیکربندی کنید.
کاتلین
ViewCompat.setWindowInsetsAnimationCallback( view, object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) { // Override methods. } )
جاوا
ViewCompat.setWindowInsetsAnimationCallback( view, new WindowInsetsAnimationCompat.Callback( WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP ) { // Override methods. });
چندین روش برای لغو کردن در WindowInsetsAnimationCompat.Callback وجود دارد،
ازجمله
onPrepare()،
onStart()،
onProgress()،
و
onEnd().
قبلاز هرگونه تغییر در طرحبندی، با فراخوانی onPrepare() شروع کنید.
onPrepare زمانی فراخوانی میشود که پویانمایی حاشیهها درحال شروع شدن است و قبلاز اینکه نماها
بهدلیل پویانمایی دوباره چیده شوند. میتوانید از آن برای ذخیره کردن حالت شروع استفاده کنید،
که در این مورد، مختصات پایین نما است.
onPrepare() برای
ضبط وضعیت شروع.
تکه کد زیر نمونهای از فراخوانی onPrepare را نشان میدهد:
کاتلین
var startBottom = 0f override fun onPrepare( animation: WindowInsetsAnimationCompat ) { startBottom = view.bottom.toFloat() }
جاوا
float startBottom; @Override public void onPrepare( @NonNull WindowInsetsAnimationCompat animation ) { startBottom = view.getBottom(); }
وقتی پویانمایی درج شروع میشود، onStart فراخوانده میشود. میتوانید از آن برای تنظیم همه
ویژگیهای نما در وضعیت نهایی تغییرات چیدمان استفاده کنید. اگر
OnApplyWindowInsetsListener فراخوانی برگشتی برای هرکدام از نماها تنظیم شده باشد، درحالحاضر
در این نقطه فراخوانی شده است. این زمان مناسبی برای ذخیره کردن وضعیت پایانی ویژگیهای
نما است.
onStart() برای ضبط
وضعیت نهایی.
تکهکد زیر نمونهای از فراخوانی onStart را نشان میدهد:
کاتلین
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 }
جاوا
float endBottom; @NonNull @Override public WindowInsetsAnimationCompat.BoundsCompat onStart( @NonNull WindowInsetsAnimationCompat animation, @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds ) { endBottom = view.getBottom(); return bounds; }
onProgress زمانی فراخوانی میشود که تورفتگیها بهعنوان بخشی از اجرای پویانمایی تغییر کنند،
بنابراین میتوانید آن را لغو کنید و در هر فریم درطول پویانمایی صفحهکلید
مطلع شوید. ویژگیهای نما را بهروز کنید تا نما همزمان با صفحهکلید پویانمایی شود.
همه تغییرات چیدمان در این مرحله کامل شده است. برای مثال، اگر از
View.translationY برای تغییر نما استفاده کنید، مقدار بهتدریج برای هر
فراخوانی این روش کاهش مییابد و درنهایت به 0 موقعیت چیدمان اصلی میرسد.
onProgress() برای
همگامسازی پویانماییها.
تکهکد زیر نمونهای از فراخوانی onProgress را نشان میدهد:
کاتلین
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 }
جاوا
@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; }
درصورت تمایل میتوانید onEnd را لغو کنید. این روش پساز پایان پویانمایی فراخوانی میشود. اکنون زمان خوبی برای پاک کردن تغییرات موقت است.
منابع بیشتر
- WindowInsetsAnimation در GitHub.