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

روش نوشتن را امتحان کنید
Jetpack Compose ابزار رابط کاربری پیشنهادی برای اندروید است. یاد بگیرید که چگونه با صفحه کلید در Compose کار کنید.

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

شکل ۱. دو نمونه از حالت باز-بسته شدن کیبورد نرم‌افزاری.

پیش‌نیازها

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

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

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

منابع اضافی