จอแสดงผลขนาดใหญ่ที่กางออกและสถานะการพับที่ไม่เหมือนใครช่วยให้ผู้ใช้ได้รับประสบการณ์ใหม่ๆ ในอุปกรณ์แบบพับได้ หากต้องการให้แอปของคุณรับรู้การพับ ให้ใช้ไลบรารี Jetpack WindowManager ซึ่งมีพื้นผิว API สำหรับฟีเจอร์หน้าต่างของอุปกรณ์แบบพับได้ เช่น การพับและบานพับ เมื่อแอปของคุณรับรู้การพับแล้ว แอปจะปรับเลย์เอาต์เพื่อหลีกเลี่ยงการวางเนื้อหาสําคัญในบริเวณที่พับหรือบานพับ และใช้การพับและบานพับเป็นตัวคั่นตามธรรมชาติ
การทำความเข้าใจว่าอุปกรณ์รองรับการกำหนดค่าต่างๆ เช่น ท่าทางวางบนโต๊ะหรือท่าทางเหมือนหนังสือ จะช่วยในการตัดสินใจเกี่ยวกับการรองรับเลย์เอาต์ต่างๆ หรือการมอบฟีเจอร์ที่เฉพาะเจาะจง
ข้อมูลหน้าต่าง
อินเทอร์เฟซ WindowInfoTracker ใน Jetpack WindowManager จะแสดงข้อมูลเลย์เอาต์หน้าต่าง
เมธอด windowLayoutInfo() ของอินเทอร์เฟซจะแสดง
สตรีมข้อมูล WindowLayoutInfo ที่แจ้งให้แอปทราบเกี่ยวกับสถานะการพับของอุปกรณ์แบบพับได้
เมธอด WindowInfoTracker#getOrCreate() จะสร้างอินสแตนซ์ของ WindowInfoTracker
WindowManager รองรับการเก็บรวบรวมข้อมูล WindowLayoutInfo โดยใช้โฟลว์ Kotlin และการเรียกกลับ 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 Dependency ช่วยให้คุณเก็บรวบรวมข้อมูลอัปเดต
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 และ
androidx.window:window-rxjava3 Dependency ประกอบด้วยเมธอด
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_OPENEDorientation: การวางแนวของการพับหรือบานพับHORIZONTALหรือVERTICALocclusionType: การพับหรือบานพับบดบังจอแสดงผลบางส่วนหรือไม่NONEหรือFULLisSeparating: การพับหรือบานพับสร้างพื้นที่แสดงผลเชิงตรรกะ 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);
}
การเปลี่ยนแปลงขนาดหน้าต่าง
พื้นที่แสดงผลของแอปอาจเปลี่ยนแปลงได้อันเป็นผลมาจากการเปลี่ยนแปลงการกำหนดค่าอุปกรณ์ เช่น เมื่ออุปกรณ์พับหรือกางออก หมุน หรือมีการปรับขนาดหน้าต่างในโหมดหลายหน้าต่าง
คลาส WindowMetricsCalculator ของ Jetpack WindowManager ช่วยให้คุณดึงข้อมูลเมตริกหน้าต่างปัจจุบันและสูงสุดได้ เช่นเดียวกับแพลตฟอร์ม
WindowMetrics ที่เปิดตัวในระดับ API 30, WindowManager
WindowMetrics จะแสดงขอบเขตหน้าต่าง แต่ API เข้ากันได้แบบย้อนหลัง
จนถึงระดับ API 14
แหล่งข้อมูลเพิ่มเติม
ตัวอย่าง
- Jetpack WindowManager: ตัวอย่างวิธีใช้ไลบรารี Jetpack WindowManager
- Jetcaster : การใช้ท่าทางวางบนโต๊ะกับ Compose
Codelabs
- รองรับอุปกรณ์แบบพับได้และอุปกรณ์แบบ 2 หน้าจอด้วย Jetpack WindowManager
- เพิ่มประสิทธิภาพแอปกล้องในอุปกรณ์แบบพับได้ด้วย Jetpack WindowManager