Dengan layar besar yang dibentangkan dan bentuk lipatan unik memungkinkan pengalaman pengguna baru di perangkat foldable. Agar aplikasi Anda menjadi fold aware, gunakan library Jetpack WindowManager yang menyediakan platform API untuk fitur jendela perangkat foldable, seperti lipatan dan engsel. Jika aplikasi Anda fold-aware, aplikasi dapat menyesuaikan tata letaknya untuk menghindari penempatan konten penting di area lipatan atau engsel serta menggunakan lipatan dan engsel sebagai pemisah alami.
Informasi jendela
Antarmuka WindowInfoTracker
di Jetpack WindowManager mengekspos informasi tata letak jendela. Metode windowLayoutInfo()
antarmuka menampilkan aliran data WindowLayoutInfo
yang memberi tahu aplikasi Anda tentang status lipat perangkat foldable. Metode getOrCreate()
WindowInfoTracker
membuat instance WindowInfoTracker
.
WindowManager menyediakan dukungan untuk mengumpulkan data WindowLayoutInfo
menggunakan Alur Kotlin dan callback Java.
Alur Kotlin
Untuk memulai dan menghentikan pengumpulan data WindowLayoutInfo
, Anda dapat menggunakan coroutine berbasis siklus proses yang dapat dimulai ulang tempat blok kode repeatOnLifecycle
dijalankan saat siklus proses setidaknya dalam status STARTED
dan dihentikan saat siklus proses dalam status STOPPED
. Eksekusi blok kode otomatis dimulai ulang saat siklus proses dalam status STARTED
lagi. Pada contoh berikut, blok kode mengumpulkan dan menggunakan data 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.
}
}
}
}
}
Callback Java
Lapisan kompatibilitas callback yang disertakan dalam dependensi androidx.window:window-java
memungkinkan Anda mengumpulkan update WindowLayoutInfo
tanpa menggunakan Flow Kotlin. Artefak ini menyertakan class WindowInfoTrackerCallbackAdapter
yang menyesuaikan WindowInfoTracker
untuk mendukung pendaftaran callback (dan pembatalan pendaftaran) untuk menerima update WindowLayoutInfo
, misalnya:
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.
});
}
}
}
Dukungan RxJava
Jika Anda sudah menggunakan RxJava
(versi 2
atau 3
), Anda dapat memanfaatkan artefak yang memungkinkan penggunaan Observable
atau Flowable
untuk mengumpulkan update WindowLayoutInfo
tanpa menggunakan Flow Kotlin.
Lapisan kompatibilitas yang disediakan oleh dependensi androidx.window:window-rxjava2
dan androidx.window:window-rxjava3
menyertakan metode WindowInfoTracker#windowLayoutInfoFlowable()
dan WindowInfoTracker#windowLayoutInfoObservable()
, yang memungkinkan aplikasi Anda menerima update WindowLayoutInfo
, misalnya:
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 the WindowLayoutInfo observable
disposable?.dispose()
}
}
Fitur layar perangkat foldable
Class WindowLayoutInfo
Jetpack WindowManager membuat fitur jendela layar tersedia sebagai daftar elemen DisplayFeature
.
FoldingFeature
adalah jenis DisplayFeature
yang memberikan informasi tentang layar perangkat foldable, termasuk informasi berikut:
state
: Status lipatan perangkat,FLAT
atauHALF_OPENED
orientation
: Orientasi lipatan atau engsel,HORIZONTAL
atauVERTICAL
occlusionType
: Apakah lipatan atau engsel menyembunyikan bagian layar,NONE
atauFULL
isSeparating
: Apakah lipatan atau engsel membuat dua area tampilan logis, benar (true) atau salah (salah)
Perangkat foldable HALF_OPENED
selalu melaporkan isSeparating
sebagai benar (true) karena layar dipisahkan menjadi dua area tampilan. Selain itu, isSeparating
selalu bernilai benar (true) pada perangkat dua layar saat aplikasi membentang pada kedua layar.
Properti bounds
FoldingFeature
(diwarisi dari DisplayFeature
) mewakili persegi panjang pembatas fitur lipat seperti lipatan atau engsel. Batas dapat digunakan untuk memosisikan elemen di layar yang terkait dengan fitur.
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { // Safely collects from windowInfoRepo 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() .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 } } } }
Mode di atas meja
Dengan menggunakan informasi yang disertakan dalam objek FoldingFeature
, aplikasi Anda dapat mendukung postur seperti mode di atas meja,
dengan ponsel berada di permukaan, engsel berada dalam posisi horizontal, dan layar perangkat foldable dibuka setengah.
Mode di atas meja menawarkan kenyamanan kepada pengguna dalam mengoperasikan ponsel tanpa memegang ponsel. Mode di atas meja sangat cocok untuk menonton media, mengambil foto, dan melakukan panggilan video.
![Aplikasi pemutar video dalam mode di atas meja](https://developer.android.com/static/images/large-screens/gallery/media/tabletop.png?authuser=8&hl=id)
Gunakan FoldingFeature.State
dan FoldingFeature.Orientation
untuk menentukan apakah perangkat dalam mode di atas meja:
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); }
Setelah mengetahui bahwa perangkat dalam mode di atas meja, perbarui tata letak aplikasi Anda. Untuk aplikasi media, pembaruan ini biasanya berarti menempatkan pemutaran di paruh atas dan kontrol posisi serta konten tambahan tepat di bawahnya untuk pengalaman menonton atau mendengarkan secara handsfree.
Contoh
Aplikasi
MediaPlayerActivity
: Lihat cara menggunakan Media3 Exoplayer dan WindowManager untuk membuat pemutar video yang fold-aware.Codelab Menciptakan pengalaman kamera pada perangkat foldable: Pelajari cara menerapkan mode di atas meja untuk aplikasi fotografi. Tampilkan jendela bidik di bagian atas layar, paruh atas, dan kontrol di bagian bawah layar, paruh bawah.
Mode buku
Postur unik lain dari perangkat foldable adalah mode buku, dengan perangkat terbuka setengah dan engsel vertikal. Mode buku sangat cocok untuk membaca e-book. Dengan tata letak dua halaman pada perangkat foldable layar besar yang dapat dilipat dan terbuka seperti buku, mode buku menciptakan pengalaman membaca buku sungguhan.
Mode ini juga dapat digunakan untuk fotografi jika Anda ingin mengambil rasio aspek yang berbeda sambil mengambil gambar secara handsfree.
Terapkan mode buku dengan teknik yang sama dengan yang digunakan untuk mode di atas meja. Satu-satunya perbedaan adalah kode harus memeriksa apakah orientasi fitur lipat adalah vertikal, bukan horizontal:
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); }
Perubahan ukuran jendela
Area tampilan aplikasi dapat berubah sebagai akibat dari perubahan konfigurasi perangkat—misalnya, saat perangkat dilipat atau dibentangkan, diputar, atau jendela diubah ukurannya dalam mode multi-aplikasi.
Class WindowMetricsCalculator
Jetpack WindowManager memungkinkan Anda mengambil metrik jendela saat ini dan maksimum. Seperti WindowMetrics
platform yang diperkenalkan di level API 30, WindowMetrics
WindowManager menyediakan batas jendela, tetapi API-nya kompatibel dengan versi lama sampai dengan level API 14.
Lihat Class ukuran jendela.
Referensi tambahan
Contoh
- Jetpack WindowManager: Contoh cara menggunakan library Jetpack WindowManager
- Jetcaster: Implementasi postur mode di atas meja dengan Compose
Codelab
- Mendukung perangkat foldable dan perangkat dua layar dengan Jetpack WindowManager
- Menciptakan pengalaman kamera pada perangkat foldable
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Mendukung perangkat foldable dan perangkat dua layar dengan Jetpack WindowManager
- Mengoptimalkan aplikasi kamera di perangkat foldable dengan Jetpack WindowManager
- Mode kompatibilitas perangkat