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

ใช้ 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. } }
ตัวอย่าง
แอป
MediaPlayerActivity
: ดูวิธีใช้ Media3 Exoplayer และ WindowManager สำหรับสร้างวิดีโอแบบรับรู้ภาพพับ โปรแกรมเล่นวิดีโอเพิ่มประสิทธิภาพแอปกล้องบนอุปกรณ์แบบพับได้ด้วย Jetpack WindowManager Codelab: ดูวิธีใช้การจัดตั้งบนโต๊ะสำหรับแอปการถ่ายภาพ แสดงช่องมองภาพครึ่งบนของหน้าจอ (เหนือรอยพับ) และตัวควบคุมครึ่งล่าง (ใต้รอยพับ)
ระดับการจอง
ฟีเจอร์พับได้อีกอย่างที่น่าสนใจคือลักษณะการวางแบบหนังสือ ซึ่งอุปกรณ์จะเปิดอยู่ครึ่งหนึ่งและบานพับอยู่ในแนวตั้ง ท่าทางการอ่านหนังสือเหมาะอย่างยิ่งสำหรับการอ่าน 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
- รองรับอุปกรณ์แบบพับได้และอุปกรณ์แบบ 2 หน้าจอด้วย Jetpack WindowManager
- เพิ่มประสิทธิภาพแอปกล้องบนอุปกรณ์แบบพับได้ด้วย Jetpack WindowManager