שליטה במקלדת התוכנה והנפשה

באמצעות WindowInsetsCompat, האפליקציה יכולה לשלוח שאילתות למקלדת שמופיעה במסך (שנקראת גם IME) ולשלוט בה, בדומה לאופן שבו היא מקיימת אינטראקציה עם סרגי המערכת. אפשר גם להשתמש ב-WindowInsetsAnimationCompat באפליקציה כדי ליצור מעברים חלקים כשמפעילים או מכבים את מקלדת התוכנה.

איור 1. שתי דוגמאות למעבר של המקלדת הווירטואלית ממצב פתוח למצב סגור.

דרישות מוקדמות

לפני שמגדירים את אמצעי הבקרה והאנימציה של מקלדת התוכנה, צריך להגדיר את האפליקציה כך שתוצג מלאה. כך הוא יכול לטפל בחלונות משנה של מערכת, כמו סרגי המערכת והמקלדת במסך.

בדיקת הרשאות הגישה של תוכנת המקלדת

משתמשים ב-WindowInsets כדי לבדוק את החשיפה של מקלדת התוכנה.

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;

לחלופין, אפשר להשתמש ב-ViewCompat.setOnApplyWindowInsetsListener כדי לראות שינויים בחשיפה של מקלדת התוכנה.

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

סנכרון האנימציה עם מקלדת התוכנה

כשמשתמש מקייש על שדה קלט טקסט, המקלדת מחליקה למקומה מתחתית המסך, כפי שמוצג בדוגמה הבאה:

איור 2. אנימציה מסונכרנת של המקלדת.
  • הדוגמה שסומנה בתווית 'לא מסונכרן' באיור 2 מציגה את התנהגות ברירת המחדל ב-Android 10 (רמת API 29), שבה שדה הטקסט והתוכן של האפליקציה נכנסים למיקום שלהם במקום לסנכרן עם האנימציה של המקלדת. התנהגות כזו עלולה לגרום לתנודות חזותיות.

  • ב-Android 11 (API ברמה 30) ואילך, אפשר להשתמש ב-WindowInsetsAnimationCompat כדי לסנכרן את המעבר של האפליקציה עם החלקה של המקלדת למעלה ולמטה מהחלק התחתון של המסך. התוצאה נראית חלקה יותר, כפי שמוצג בדוגמה שמסומנת בתווית 'סנכרון' באיור 2.

מגדירים את WindowInsetsAnimationCompat.Callback כך שהתצוגה תסתנכרן עם אנימציית המקלדת.

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.
    });

יש כמה שיטות לשינוי ברירת המחדל ב-WindowInsetsAnimationCompat.Callback, למשל onPrepare(), onStart(), onProgress() ו-onEnd(). מתחילים בקריאה ל-onPrepare() לפני שמשנים את הפריסה.

onPrepare נקרא כשאנימציה של תצוגה מוטמעת מתחילה, ולפני שהתצוגות מחדשות את הפריסה שלהן בגלל אנימציה. אפשר להשתמש בו כדי לשמור את מצב ההתחלה, שהוא במקרה הזה הקואורדינטה התחתונה של התצוגה.

תמונה שבה מוצגת הקואורדינטה התחתונה של מצב ההתחלה בתצוגת הבסיס.
איור 3. שימוש ב-onPrepare() כדי לתעד את מצב ההתחלה.

קטע הקוד הבא מציג קריאה לדוגמה ל-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 נקראת כשאנימציית הכנסה מתחילה. אפשר להשתמש בו כדי להגדיר את כל מאפייני התצוגה למצב הסופי של שינויי הפריסה. אם הגדרתם קריאה חוזרת מסוג OnApplyWindowInsetsListener לאחת מהתצוגות, היא כבר מופעלת בשלב הזה. זהו זמן טוב לשמור את המצב הסופי של מאפייני התצוגה.

תמונה שבה מוצגת הקואורדינטה התחתונה של מצב הסיום בתצוגה
איור 4. שימוש ב-onStart() כדי לתעד את מצב הסיום.

קטע הקוד הבא מציג קריאה לדוגמה ל-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 נקראת כשהחלקים הפנימיים משתנים במהלך הפעלת אנימציה, כך שאפשר לשנות את ברירת המחדל שלה ולקבל התראה בכל פריים במהלך האנימציה של המקלדת. מעדכנים את מאפייני התצוגה כך שהאנימציה של התצוגה תהיה מסונכרנת עם המקלדת.

בשלב הזה, כל השינויים בפריסה הושלמו. לדוגמה, אם משתמשים ב-View.translationY כדי להזיז את התצוגה, הערך פוחת בהדרגה בכל קריאה ל-method הזה, ובסופו של דבר מגיע ל-0 כדי לחזור למיקום התצוגה המקורי.

איור 5. שימוש ב-onProgress() כדי לסנכרן את האנימציות.

קטע הקוד הבא מציג קריאה לדוגמה ל-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;
}

אפשר גם לשנות את onEnd. ה-method הזה נקרא אחרי שהאנימציה מסתיימת. זה הזמן המתאים לבטל שינויים זמניים.

מקורות מידע נוספים