صفحه کلید نرم افزار را کنترل و متحرک کنید

با استفاده از WindowInsetsCompat ، برنامه شما می‌تواند صفحه‌کلید روی صفحه (که IME نیز نامیده می‌شود) پرس و جو و کنترل کند، شبیه به نحوه تعامل آن با نوارهای سیستم. برنامه شما همچنین می‌تواند از WindowInsetsAnimationCompat برای ایجاد انتقال بدون درز هنگام باز یا بسته شدن صفحه‌کلید نرم‌افزار استفاده کند.

شکل 1. دو نمونه از انتقال باز-بسته صفحه کلید نرم افزار.

پیش نیازها

قبل از تنظیم کنترل و انیمیشن برای صفحه کلید نرم افزار، برنامه خود را برای نمایش لبه به لبه پیکربندی کنید. این به آن اجازه می‌دهد تا با ورودی‌های پنجره‌ی سیستم مانند نوارهای سیستم و صفحه‌کلید روی صفحه کار کند.

قابلیت مشاهده نرم افزار صفحه کلید را بررسی کنید

از 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;
});

همگام سازی انیمیشن با صفحه کلید نرم افزار

ضربه زدن کاربر به فیلد ورودی متن باعث می شود صفحه کلید از پایین صفحه به جای خود بلغزد، همانطور که در مثال زیر نشان داده شده است:

شکل 2. انیمیشن کیبورد همگام شده.
  • مثالی که در شکل 2 با عنوان "Unsynchronized" نامگذاری شده است، رفتار پیش‌فرض اندروید 10 (سطح API 29) را نشان می‌دهد که در آن قسمت متن و محتوای برنامه به جای همگام‌سازی با انیمیشن صفحه‌کلید در جای خود می‌چسبد - رفتاری که می‌تواند از نظر بصری ناخوشایند باشد.

  • در اندروید 11 (سطح API 30) و بالاتر، می‌توانید از WindowInsetsAnimationCompat برای همگام‌سازی انتقال برنامه با صفحه‌کلید که از پایین صفحه به بالا و پایین می‌لغزد، استفاده کنید. همانطور که در مثال با عنوان "Synchronized" در شکل 2 نشان داده شده است، این نرم افزار به نظر می رسد.

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 زمانی فراخوانی می شود که یک انیمیشن insets شروع می شود و قبل از اینکه نماها به دلیل یک انیمیشن دوباره نمایش داده شوند. می توانید از آن برای ذخیره حالت شروع استفاده کنید که در این حالت مختصات پایینی نما است.

تصویری که مختصات پایین حالت شروع نمای ریشه را نشان می دهد.
شکل 3. استفاده از 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();
}

هنگام شروع انیمیشن insets onStart فراخوانی می شود. می توانید از آن برای تنظیم تمام ویژگی های view در حالت پایانی تغییرات طرح بندی استفاده کنید. اگر پاسخ تماس OnApplyWindowInsetsListener روی هر یک از نماها تنظیم شده باشد، قبلاً در این مرحله فراخوانی شده است. این زمان خوبی برای ذخیره وضعیت پایانی خصوصیات view است.

تصویری که مختصات پایین حالت پایانی نما را نشان می دهد
شکل 4. استفاده از 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 را به روز کنید تا نما در هماهنگی با صفحه کلید متحرک شود.

تمام تغییرات طرح در این مرحله کامل شده است. به عنوان مثال، اگر از View.translationY برای جابجایی نما استفاده می کنید، مقدار به تدریج برای هر فراخوانی این روش کاهش می یابد و در نهایت به موقعیت طرح اولیه 0 می رسد.

شکل 5. استفاده از 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 را لغو کنید. این روش پس از اتمام انیمیشن فراخوانی می شود. این زمان خوبی برای پاکسازی تغییرات موقتی است.

منابع اضافی