ควบคุมและทำให้แป้นพิมพ์ของซอฟต์แวร์เคลื่อนไหว

การใช้ WindowInsetsCompat แอปสามารถค้นหาและควบคุมแป้นพิมพ์บนหน้าจอ (หรือที่เรียกว่า IME) คล้ายกับ วิธีที่ผู้ใช้โต้ตอบกับแถบของระบบ นอกจากนี้ แอปยังสามารถใช้ WindowInsetsAnimationCompat เพื่อสร้างการเปลี่ยนที่ราบรื่นเมื่อเปิดหรือปิดแป้นพิมพ์ซอฟต์แวร์

รูปที่ 1 ตัวอย่างซอฟต์แวร์แป้นพิมพ์ 2 รายการ การเปลี่ยนแปลงแบบปิดแบบเปิด

สิ่งที่ต้องมีก่อน

ก่อนตั้งค่าการควบคุมและภาพเคลื่อนไหวสำหรับซอฟต์แวร์แป้นพิมพ์ ให้กำหนดค่า แอปของคุณให้แสดงแบบขอบต่อขอบ ซึ่งช่วยให้ จะจัดการกับส่วนต่างๆ ของหน้าต่างระบบ เช่น แถบระบบและแป้นพิมพ์บนหน้าจอ

ตรวจสอบการเปิดเผยซอฟต์แวร์แป้นพิมพ์

ใช้ 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 เมื่อภาพเคลื่อนไหวประกอบเริ่มต้นขึ้น คุณสามารถใช้การตั้งค่านี้เพื่อตั้งค่าทั้งหมด คุณสมบัติมุมมองเป็นสถานะสิ้นสุดของเค้าโครงจะเปลี่ยนไป หากคุณมี ตั้งค่า Callback 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 เพื่อเปลี่ยนมุมมอง ค่าจะค่อยๆ ลดลงสำหรับทุกๆ ของวิธีนี้ และในที่สุดจะถึง 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 ก็ได้ ระบบจะเรียกเมธอดนี้หลังจากภาพเคลื่อนไหว จบแล้ว นี่คือช่วงเวลาที่ดีในการแก้ไขการเปลี่ยนแปลงที่เกิดขึ้นชั่วคราว

แหล่งข้อมูลเพิ่มเติม