با استفاده از WindowInsetsCompat ، برنامه شما میتواند صفحه کلید روی صفحه (که IME نیز نامیده میشود) را مشابه نحوه تعامل با نوارهای سیستم، جستجو و کنترل کند. برنامه شما همچنین میتواند از WindowInsetsAnimationCompat برای ایجاد انتقالهای یکپارچه هنگام باز یا بسته شدن صفحه کلید نرمافزاری استفاده کند.
پیشنیازها
قبل از تنظیم کنترل و انیمیشن برای صفحهکلید نرمافزاری، برنامه خود را طوری پیکربندی کنید که لبه به لبه نمایش داده شود . این به برنامه اجازه میدهد تا عناصر پنجره سیستم مانند نوارهای سیستم و صفحهکلید روی صفحه را مدیریت کند.
انیمیشن پیشفرض همگامسازیشدهی درونصفحهای
از سطح API 37.2 و بالاتر، برنامههایی که از تنظیمات پیشفرض چیدمان پنجره (مانند SOFT_INPUT_ADJUST_RESIZE یا SOFT_INPUT_ADJUST_PAN هنگام نمایش IME با استفاده از WindowInsetsController.show(WindowInsets.Type.ime()) ) بدون پیادهسازی WindowInsetsAnimationCompat.Callback سفارشی استفاده میکنند، میتوانند از انیمیشنهای فریم به فریم هماهنگشده در سطح سیستم بهرهمند شوند. وقتی این ویژگی فعال باشد، به طور خودکار insetها را روی هر فریم اعمال میکند و یک layout pass را فعال میکند و با حرکت صفحه کلید، یک انتقال روان ایجاد میکند.
برای فعال کردن این رفتار، تگ <property> زیر را به activity یا application خود در فایل AndroidManifest.xml اضافه کنید:
<property
android:name="android.window.PROPERTY_COMPAT_ALLOW_SYNCHRONIZED_INSETS_ANIMATION"
android:value="true" />
اجرای انیمیشنهای insets خارج از thread اصلی
به طور پیشفرض، ثبت یک WindowInsetsAnimationCompat.Callback باعث میشود که انیمیشن درج پنجره در نخ اصلی برنامه اجرا شود. این امر در صورتی ضروری است که برنامه شما به طور مداوم در طول انیمیشن، طرحبندی نمای خود را بهروزرسانی و همگامسازی کند (مانند متد onProgress ). با این حال، اگر برنامه شما فقط نیاز به گوش دادن به انتقالهای چرخه عمر (مانند زمانی که IME شروع یا پایان انیمیشن را انجام میدهد) داشته باشد و نیازی به بهروزرسانی نماها به صورت فریم به فریم نداشته باشد، اجرا در نخ اصلی میتواند باعث سربار غیرضروری و وقفه در انیمیشن شود - به خصوص اگر نخ اصلی مشغول باشد.
از سطح API 37.2 و بالاتر، میتوانید از ViewTreeObserver.WindowInsetsAnimationListener برای مشاهده این انتقالها استفاده کنید. از آنجا که این شنونده بهروزرسانیهای پیشرفت فریم به فریم را دریافت نمیکند (بدون فراخوانی onProgress )، سیستم میتواند انیمیشن inset را به جای thread اصلی، روی یک thread انیمیشن اختصاصی اجرا کند که منجر به انتقال روانتر میشود.
کاتلین
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; });
همگامسازی انیمیشن با صفحهکلید نرمافزار
همانطور که در مثال زیر نشان داده شده است، کاربری که روی یک فیلد ورودی متن ضربه میزند، باعث میشود صفحهکلید از پایین صفحه به جای خود بلغزد:
مثالی که در شکل ۲ با عنوان «غیرهمگامسازیشده» مشخص شده است، رفتار پیشفرض در اندروید ۱۰ (سطح API ۲۹) را نشان میدهد که در آن فیلد متنی و محتوای برنامه به جای همگامسازی با انیمیشن صفحهکلید، در جای خود قرار میگیرند - رفتاری که میتواند از نظر بصری نامطلوب باشد.
در اندروید ۱۱ (سطح API 30) و بالاتر، میتوانید از
WindowInsetsAnimationCompatبرای همگامسازی انتقال برنامه با صفحهکلید که از پایین صفحه به بالا و پایین میلغزد، استفاده کنید. این روش، همانطور که در مثال با برچسب "Synchronized" در شکل ۲ نشان داده شده است، روانتر به نظر میرسد.
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. });
چندین متد برای override کردن در WindowInsetsAnimationCompat.Callback وجود دارد، که عبارتند از onPrepare() ، onStart() ، onProgress() و onEnd() . قبل از هرگونه تغییر در طرحبندی، با فراخوانی onPrepare() شروع کنید.
متد onPrepare زمانی فراخوانی میشود که یک انیمیشن insets شروع میشود و قبل از اینکه نماها به دلیل یک انیمیشن دوباره چیدمان شوند. میتوانید از آن برای ذخیره حالت شروع استفاده کنید، که در این مورد مختصات پایین نما است.

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 زمانی فراخوانی میشود که یک انیمیشن insets شروع میشود. میتوانید از آن برای تنظیم تمام ویژگیهای نما در حالت پایانی تغییرات طرحبندی استفاده کنید. اگر یک فراخوانی برگشتی 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 زمانی فراخوانی میشود که مقادیر insets به عنوان بخشی از اجرای یک انیمیشن تغییر کنند، بنابراین میتوانید آن را لغو کنید و در طول انیمیشن صفحه کلید، در هر فریم از آن مطلع شوید. ویژگیهای نما را بهروزرسانی کنید تا نما با صفحه کلید هماهنگ شود.
تمام تغییرات طرحبندی در این مرحله کامل شدهاند. برای مثال، اگر از 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 override کنید. این متد پس از پایان انیمیشن فراخوانی میشود. این زمان مناسبی برای پاک کردن هرگونه تغییر موقت است.
منابع اضافی
- انیمیشن پنجرهها (WinsetsAnimation) در گیتهاب.
