รับรู้ถึงการพับของแอป

จอแสดงผลขนาดใหญ่แบบกางออกและสถานะแบบพับที่ไม่ซ้ำกันช่วยให้ผู้ใช้ได้รับประสบการณ์การใช้งานแบบใหม่ในอุปกรณ์แบบพับได้ หากต้องการให้แอปรับรู้การพับ ให้ใช้ไลบรารี Jetpack WindowManager ซึ่งให้บริการแพลตฟอร์ม API สําหรับฟีเจอร์หน้าต่างของอุปกรณ์แบบพับได้ เช่น การพับและการบานพับ เมื่อแอปรับรู้การพับ แอปจะปรับเลย์เอาต์เพื่อหลีกเลี่ยงการวางเนื้อหาที่สําคัญในพื้นที่ของรอยพับหรือรอยบานพับ และใช้รอยพับและรอยบานพับเป็นตัวคั่นตามปกติ

การทราบว่าอุปกรณ์รองรับการกำหนดค่าต่างๆ เช่น การวางบนโต๊ะหรือการวางแบบหนังสือ จะช่วยให้คุณตัดสินใจได้ว่าควรรองรับเลย์เอาต์ที่แตกต่างกันหรือให้บริการฟีเจอร์ที่เฉพาะเจาะจง

ข้อมูลหน้าต่าง

อินเทอร์เฟซ WindowInfoTracker ใน Jetpack WindowManager แสดงหน้าต่าง ข้อมูลเค้าโครง เมธอด windowLayoutInfo() ของอินเทอร์เฟซจะแสดงผลสตรีมข้อมูล WindowLayoutInfo ที่แจ้งให้แอปทราบเกี่ยวกับสถานะการพับของอุปกรณ์แบบพับได้ เมธอด WindowInfoTracker#getOrCreate() จะสร้าง อินสแตนซ์ของ WindowInfoTracker

WindowManager สนับสนุนการรวบรวมข้อมูล WindowLayoutInfo โดยใช้ โฟลว์ Kotlin และ Callback ของ Java

โฟลว์ Kotlin

หากต้องการเริ่มและหยุดการรวบรวมข้อมูลของ WindowLayoutInfo คุณสามารถใช้ไอคอนรีสตาร์ทได้ โครูทีนที่รับรู้ถึงอายุการใช้งาน ซึ่งโค้ดบล็อก repeatOnLifecycle ดำเนินการเมื่อวงจรอยู่ที่ STARTED เป็นอย่างน้อย และหยุดเมื่อเหตุการณ์ วงจรคือ STOPPED ระบบจะเริ่มต้นการเรียกใช้บล็อกโค้ดอีกครั้งโดยอัตโนมัติเมื่อวงจรเป็น STARTED อีกครั้ง ในตัวอย่างนี้ บล็อกโค้ดจะรวบรวมและใช้ข้อมูล WindowLayoutInfo

class DisplayFeaturesActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDisplayFeaturesBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                    .windowLayoutInfo(this@DisplayFeaturesActivity)
                    .collect { newLayoutInfo ->
                        // Use newLayoutInfo to update the layout.
                    }
            }
        }
    }
}

การเรียกกลับของ Java

เลเยอร์ความเข้ากันได้ของคอลแบ็กที่รวมอยู่ในข้อกําหนดของ androidx.window:window-java ช่วยให้คุณรวบรวมการอัปเดต WindowLayoutInfo ได้โดยไม่ต้องใช้โฟลว์ Kotlin อาร์ติแฟกต์ประกอบด้วยคลาส WindowInfoTrackerCallbackAdapter ซึ่งปรับWindowInfoTrackerให้รองรับการลงทะเบียน (และการยกเลิกการลงทะเบียน) ของคอลแบ็กเพื่อรับการอัปเดต WindowLayoutInfo ตัวอย่างเช่น

public class SplitLayoutActivity extends AppCompatActivity {

    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    private ActivitySplitLayoutBinding binding;
    private final LayoutStateChangeCallback layoutStateChangeCallback =
            new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoTracker =
                new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoTracker
           .removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo newLayoutInfo) {
           SplitLayoutActivity.this.runOnUiThread( () -> {
               // Use newLayoutInfo to update the layout.
           });
       }
   }
}

การสนับสนุน RxJava

หากใช้ RxJava (เวอร์ชัน 2 หรือ 3) อยู่แล้ว คุณสามารถใช้ข้อดีของอาร์ติแฟกต์ที่ช่วยให้ใช้ Observable หรือ Flowable เพื่อรวบรวมการอัปเดต WindowLayoutInfo โดยไม่ต้องใช้เวิร์กโฟลว์ Kotlin

เลเยอร์ความเข้ากันได้ที่ androidx.window:window-rxjava2 และ ทรัพยากร Dependency androidx.window:window-rxjava3 ประกอบด้วย WindowInfoTracker#windowLayoutInfoFlowable() และ WindowInfoTracker#windowLayoutInfoObservable() เมธอด ซึ่งช่วยให้ แอปที่จะรับการอัปเดต WindowLayoutInfo เช่น

class RxActivity: AppCompatActivity {

    private lateinit var binding: ActivityRxBinding

    private var disposable: Disposable? = null
    private lateinit var observable: Observable<WindowLayoutInfo>

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

        // Create a new observable.
        observable = WindowInfoTracker.getOrCreate(this@RxActivity)
            .windowLayoutInfoObservable(this@RxActivity)
   }

   @Override
   protected void onStart() {
       super.onStart();

        // Subscribe to receive WindowLayoutInfo updates.
        disposable?.dispose()
        disposable = observable
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { newLayoutInfo ->
            // Use newLayoutInfo to update the layout.
        }
   }

   @Override
   protected void onStop() {
       super.onStop();

        // Dispose of the WindowLayoutInfo observable.
        disposable?.dispose()
   }
}

ฟีเจอร์ของจอแสดงผลแบบพับได้

คลาส WindowLayoutInfo ของ Jetpack WindowManager สร้างฟีเจอร์ของ แสดงหน้าต่างที่พร้อมใช้งานเป็นรายการขององค์ประกอบ DisplayFeature

FoldingFeature เป็น DisplayFeature ประเภทหนึ่งที่ให้ข้อมูล เกี่ยวกับจอแสดงผลแบบพับได้ ได้แก่

  • state: สถานะพับของอุปกรณ์ FLAT หรือ HALF_OPENED

  • orientation: การวางแนวของส่วนพับหรือบานพับ HORIZONTAL หรือ VERTICAL

  • occlusionType: รอยพับหรือบานพับบดบังการแสดงผลบางส่วนหรือไม่ NONE หรือ FULL

  • isSeparating: ระบุว่าการพับหรือบานพับสร้างพื้นที่แสดงผลแบบตรรกะ 2 พื้นที่หรือไม่ จริงหรือเท็จ

อุปกรณ์แบบพับได้ที่มีHALF_OPENEDจะรายงานisSeparatingเป็น "จริง" เสมอเนื่องจากหน้าจอแยกออกเป็น 2 พื้นที่แสดงผล นอกจากนี้ isSeparating จะถือเป็น "จริง" เสมอในอุปกรณ์แบบ 2 หน้าจอเมื่อแอปพลิเคชันแสดงในทั้ง 2 หน้าจอ

พร็อพเพอร์ตี้ FoldingFeature bounds (รับค่ามาจาก DisplayFeature) แสดงสี่เหลี่ยมผืนผ้าที่กําหนดขอบเขตของฟีเจอร์การพับ เช่น การพับหรือบานพับ คุณสามารถใช้ขอบเขตเพื่อจัดตําแหน่งองค์ประกอบบนหน้าจอโดยสัมพันธ์กับองค์ประกอบต่อไปนี้

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }

        }
    }
}

Java

private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}

ท่าทางบนโต๊ะ

เมื่อใช้ข้อมูลที่รวมอยู่ในออบเจ็กต์ FoldingFeature แอปของคุณจะรองรับลักษณะการวางต่างๆ เช่น การวางบนโต๊ะ ซึ่งโทรศัพท์วางอยู่บนพื้นผิว บานพับอยู่ในตำแหน่งแนวนอน และหน้าจอแบบพับได้เปิดอยู่ครึ่งหนึ่ง

การจัดวางแบบตั้งโต๊ะช่วยให้ผู้ใช้ใช้งานโทรศัพท์ได้อย่างสะดวก ถือโทรศัพท์ไว้ในมือ ท่าทางการวางบนโต๊ะเหมาะอย่างยิ่งสำหรับการดูสื่อ ถ่ายภาพ และวิดีโอคอล

รูปที่ 1 แอปวิดีโอเพลเยอร์ในโหมดตั้งโต๊ะ

ใช้ FoldingFeature.State และ FoldingFeature.Orientation เพื่อระบุ อุปกรณ์อยู่ในท่าตั้งบนโต๊ะหรือไม่:

Kotlin

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

Java

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

เมื่อทราบว่าอุปกรณ์อยู่ในลักษณะบนโต๊ะแล้ว ให้อัปเดตเลย์เอาต์ของแอป ตามนั้น สำหรับแอปสื่อ ซึ่งมักหมายถึงการวางการเล่นไว้เหนือ การควบคุมพับและวางตำแหน่งและเนื้อหาเสริมที่อยู่ด้านล่างสำหรับ ประสบการณ์การดูหรือฟังแบบแฮนด์ฟรี

ใน Android 15 (API ระดับ 35) ขึ้นไป คุณสามารถเรียกใช้ API แบบซิงโครนัสเพื่อ ตรวจจับว่าอุปกรณ์รองรับการยืนบนโต๊ะหรือไม่ ไม่ว่าสถานการณ์ปัจจุบันจะเป็นอย่างไรก็ตาม สถานะของอุปกรณ์

API จะแสดงรายการท่าทางที่อุปกรณ์รองรับ หากรายการมีท่าทางการวางอุปกรณ์บนโต๊ะ คุณสามารถแยกเลย์เอาต์แอปเพื่อรองรับท่าทางดังกล่าว และทำการทดสอบ A/B ใน UI ของแอปสำหรับเลย์เอาต์แบบตั้งโต๊ะและแบบเต็มหน้าจอ

Kotlin

if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}

Java

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}

ตัวอย่าง

ระดับการจอง

ฟีเจอร์พับได้อีกอย่างที่น่าสนใจคือลักษณะการวางแบบหนังสือ ซึ่งอุปกรณ์จะเปิดอยู่ครึ่งหนึ่งและบานพับอยู่ในแนวตั้ง ท่าทางการอ่านหนังสือเหมาะอย่างยิ่งสำหรับการอ่าน eBook ด้วย การจัดวางแบบ 2 หน้าบนหน้าจอขนาดใหญ่แบบพับได้เปิดออกเหมือนหนังสือที่ผูกไว้ หนังสือ เพื่อบันทึกประสบการณ์การอ่านของหนังสือเล่มจริง

นอกจากนี้ยังใช้ถ่ายภาพได้หากต้องการจับภาพในสัดส่วนภาพอื่นขณะถ่ายภาพแบบแฮนด์ฟรี

ใช้การจัดท่าหนังสือด้วยเทคนิคเดียวกับที่ใช้ในการจัดท่าทางบนโต๊ะ ความแตกต่างเพียงอย่างเดียวคือโค้ดควรตรวจสอบว่าการวางแนวของฟีเจอร์การพับเป็นแนวตั้งแทนแนวนอน

Kotlin

fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}

Java

boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

การเปลี่ยนแปลงขนาดหน้าต่าง

พื้นที่แสดงผลของแอปอาจเปลี่ยนแปลงได้จากการเปลี่ยนแปลงการกำหนดค่าอุปกรณ์ ตัวอย่างเช่น เมื่ออุปกรณ์พับหรือกางออก หมุน หรือหน้าต่าง ปรับขนาดในโหมดหลายหน้าต่าง

คลาส WindowManager WindowMetricsCalculator ของ Jetpack ช่วยให้คุณดึงข้อมูลเมตริกกรอบเวลาปัจจุบันและสูงสุดได้ WindowManager WindowMetrics จะระบุขอบเขตของหน้าต่างเช่นเดียวกับแพลตฟอร์มWindowMetrics ที่เปิดตัวใน API ระดับ 30 แต่ API นี้ใช้งานย้อนหลังได้จนถึง API ระดับ 14

โปรดดูหัวข้อใช้คลาสขนาดหน้าต่าง

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

ตัวอย่าง

  • Jetpack WindowManager: ตัวอย่างวิธีใช้ Jetpack ไลบรารี WindowManager
  • Jetcaster : การใช้ท่าทางบนโต๊ะด้วย Compose

Codelabs