1. Pengantar
Penyematan aktivitas, yang diperkenalkan di Android 12L (level API 32), memungkinkan aplikasi berbasis aktivitas menampilkan beberapa aktivitas secara bersamaan di perangkat layar besar untuk membuat tata letak dua panel seperti daftar-detail.
Codelab Membangun tata letak daftar-detail dengan penyematan aktivitas dan Desain Material membahas cara menggunakan panggilan API WindowManager XML atau Jetpack untuk membuat tata letak daftar-detail.
Codelab ini memandu Anda mempelajari beberapa fitur yang baru dirilis untuk penyematan aktivitas, yang akan lebih meningkatkan pengalaman aplikasi Anda di perangkat layar besar. Fitur ini mencakup perluasan panel, penyematan aktivitas, dan peredupan dialog layar penuh.
Prasyarat
- Menyelesaikan codelab Membuat tata letak daftar-detail dengan Penyematan Aktivitas dan Desain Material
- Pengalaman membangun aplikasi di Android Studio, termasuk penyiapan perangkat virtual menggunakan Android 15
Yang akan Anda pelajari
Cara:
- Mengaktifkan perluasan panel
- Mengimplementasikan penyematan aktivitas dengan salah satu jendela terpisah
- Menggunakan peredupan dialog layar penuh
Yang akan Anda butuhkan
- Android Studio versi terbaru
- Ponsel atau emulator Android dengan Android 15
- Tablet besar atau emulator Android dengan lebar terkecil lebih besar dari 600 dp
2. Penyiapan
Mendapatkan aplikasi contoh
Langkah 1: Membuat clone repo
Buat clone repositori Git codelab layar besar:
git clone https://github.com/android/large-screen-codelabs
atau download dan batalkan pengarsipan file zip codelab layar besar:
Langkah 2: Memeriksa file sumber codelab
Buka folder activity-embedding-advanced
.
Langkah 3: Membuka project codelab
Di Android Studio, buka project Kotlin atau Java
Folder activity-embedding-advanced
di file zip dan repo berisi dua project Android Studio: satu di Kotlin, satu di Java. Buka project pilihan Anda. Cuplikan codelab tersedia dalam kedua bahasa.
Membuat perangkat virtual
Jika Anda tidak memiliki ponsel Android, tablet kecil, atau tablet besar di level API 35 atau yang lebih tinggi, buka Pengelola Perangkat di Android Studio dan buat perangkat virtual berikut yang Anda perlukan:
- Ponsel — Pixel 8, level API 35 atau yang lebih tinggi
- Tablet — Pixel Tablet, level API 35 atau yang lebih tinggi
3. Menjalankan aplikasi
Aplikasi contoh menampilkan daftar item. Saat pengguna memilih item, aplikasi akan menampilkan informasi tentang item tersebut.
Aplikasi ini terdiri dari tiga aktivitas:
ListActivity
— Berisi daftar item diRecyclerView
DetailActivity
— Menampilkan informasi tentang item daftar saat item dipilih dari daftarSummaryActivity
— Menampilkan ringkasan informasi saat item daftar Ringkasan dipilih
Melanjutkan dari codelab sebelumnya
Dalam codelab Membuat tata letak daftar-detail dengan penyematan aktivitas dan Desain Material, kita mengembangkan aplikasi yang menampilkan tampilan daftar-detail menggunakan penyematan aktivitas dengan navigasi yang difasilitasi oleh kolom samping navigasi dan menu navigasi bawah.
- Jalankan aplikasi di tablet besar atau emulator Pixel dalam mode potret. Anda akan melihat layar daftar utama dan menu navigasi di bagian bawah.
- Putar tablet ke samping (lanskap). Layar akan terpisah, menampilkan daftar di satu sisi dan detail di sisi lainnya. Menu navigasi di bagian bawah seharusnya tergantikan oleh kolom samping navigasi vertikal.
Fitur baru dengan penyematan aktivitas
Siap meningkatkan kualitas tata letak panel ganda Anda? Dalam codelab ini, kita akan menambahkan beberapa fitur baru yang keren untuk meningkatkan pengalaman pengguna. Berikut yang akan kita buat:
- Mari buat panel tersebut menjadi dinamis. Kita akan menerapkan perluasan panel, yang memberi pengguna kemampuan untuk mengubah ukuran (atau memperluas) panel untuk melihat tampilan yang disesuaikan.
- Mari beri pengguna kemampuan untuk memilih prioritas. Dengan Penyematan Aktivitas, pengguna dapat membuat tugas terpenting mereka selalu berada di layar.
- Perlu fokus pada tugas tertentu? Kita akan menambahkan fitur peredupan layar penuh untuk mengurangi gangguan secara samar dan memungkinkan pengguna berkonsentrasi pada hal yang paling penting.
4. Perluasan panel
Sering kali, pengguna perlu berfokus pada salah satu panel terpisah sambil mempertahankan panel lainnya di layar saat menggunakan tata letak panel ganda di layar besar. Misalnya, membaca artikel di satu sisi sambil mempertahankan daftar percakapan chat di sisi lain. Biasanya, pengguna ingin mengubah ukuran panel untuk memudahkan fokus pada satu aktivitas.
Untuk mencapai tujuan ini, penyematan aktivitas menambahkan API baru agar Anda dapat memberi pengguna kesempatan untuk mengubah rasio pemisahan dan menyesuaikan transisi pengubahan ukuran.
Menambahkan dependensi
Pertama, tambahkan WindowManager 1.4 ke file build.gradle
Anda.
Catatan: Beberapa fitur dalam library ini hanya berfungsi di Android 15 (level API 35) dan yang lebih tinggi.
build.gradle
implementation 'androidx.window:window:1.4.0-alpha02'
Menyesuaikan pemisah jendela
Buat instance DividerAttributes
dan tambahkan ke SplitAttributes
. Objek ini mengonfigurasi perilaku keseluruhan tata letak terpisah Anda. Anda dapat menggunakan properti warna, lebar, dan rentang penarikan DividerAttributes
untuk meningkatkan pengalaman pengguna.
Menyesuaikan pemisah:
- Periksa level API Ekstensi WindowManager. Karena fitur perluasan panel hanya tersedia di level API 6 dan yang lebih tinggi, hal ini juga berlaku untuk fitur baru lainnya.
- Buat
DividerAttributes
: Untuk menata gaya pemisah antar-panel, buat objekDividerAttributes
. Objek ini memungkinkan Anda menetapkan:
- color: Mengubah warna pemisah agar sesuai dengan tema aplikasi atau membuat pemisahan visual.
- widthDp: Menyesuaikan lebar pemisah untuk visibilitas yang lebih baik atau tampilan yang lebih halus.
- Tambahkan ke
SplitAttributes
: Setelah menyesuaikan pemisah, tambahkan ke objekDividerAttributes
. - Tetapkan rentang penarikan (opsional): Anda juga dapat mengontrol seberapa jauh pengguna dapat menarik pemisah untuk mengubah ukuran panel.
DRAG_RANGE_SYSTEM_DEFAULT
: Gunakan nilai khusus ini untuk memungkinkan sistem menentukan rentang penarikan yang sesuai berdasarkan ukuran layar dan faktor bentuk perangkat.- Nilai kustom (antara 0,33 dan 0,66): Tetapkan rentang penarikan Anda sendiri untuk membatasi seberapa banyak pengguna dapat mengubah ukuran panel. Ingat, jika pengguna menariknya hingga melewati batas ini, tata letak terpisah akan dinonaktifkan.
Ganti splitAttributes
dengan kode berikut.
SplitManager.kt
val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.ratio(0.33f))
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
splitAttributesBuilder.setDividerAttributes(
DividerAttributes.DraggableDividerAttributes.Builder()
.setColor(getColor(context, R.color.divider_color))
.setWidthDp(4)
.setDragRange(
DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
.build()
)
}
val splitAttributes: SplitAttributes = splitAttributesBuilder.build()
SplitManager.java
SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.ratio(0.33f))
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT);
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
splitAttributesBuilder.setDividerAttributes(
new DividerAttributes.DraggableDividerAttributes.Builder()
.setColor(ContextCompat.getColor(context, R.color.divider_color))
.setWidthDp(4)
.setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
.build()
);
}
SplitAttributes splitAttributes = splitAttributesBuilder.build();
Buat divider_color.xml
di folder res/color
dengan konten berikut.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#669df6" />
</selector>
Menjalankan aplikasi
Selesai. Bangun dan jalankan aplikasi contoh.
Anda akan melihat perluasan panel dan dapat menariknya.
Mengubah rasio pemisahan di versi lama
Catatan kompatibilitas penting: Fitur perluasan panel hanya tersedia di Ekstensi WindowManager 6 atau yang lebih baru, yang berarti Anda memerlukan Android 15 (level API 35) atau yang lebih tinggi.
Namun, Anda tetap harus memberikan pengalaman yang baik bagi pengguna di versi Android yang lebih lama.
Di Android 14 (level API 34) dan yang lebih rendah, Anda masih dapat memberikan penyesuaian rasio pemisahan dinamis menggunakan class SplitAttributesCalculator
. Hal ini menawarkan cara untuk mempertahankan beberapa level kontrol pengguna terhadap tata letak, bahkan tanpa perlu memperluas panel.
Ingin tahu cara yang optimal untuk menggunakan fitur ini? Kami akan membahas semua praktik terbaik dan tips eksklusif di bagian "Praktik Terbaik".
5. Penyematan aktivitas
Pernahkah Anda ingin mempertahankan satu bagian tampilan layar terpisah agar tetap terbuka sambil membuka-buka layar dengan bebas di bagian lainnya? Bayangkan Anda membaca artikel panjang di satu sisi sambil tetap dapat berinteraksi dengan konten aplikasi lain di sisi lainnya.
Di sinilah peran penyematan aktivitas. Fitur ini memungkinkan Anda menyematkan salah satu jendela terpisah sehingga tetap berada di layar meskipun Anda membuka-buka jendela lain. Hal ini memberikan pengalaman multitasking yang lebih fokus dan produktif bagi pengguna.
Menambahkan tombol penyematan
Pertama, mari tambahkan tombol di DetailActivity.
. Aplikasi menyematkan DetailActivity
ini saat pengguna mengklik tombol.
Buat perubahan berikut ke activity_detail.xml
:
- Tambahkan ID ke
ConstraintLayout
android:id="@+id/detailActivity"
- Tambahkan tombol di bagian bawah tata letak
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/pinButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pin_this_activity"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/>
- Batasi bagian bawah
TextView
ke bagian atas tombol
app:layout_constraintBottom_toTopOf="@id/pinButton"
Hapus baris ini di TextView
.
app:layout_constraintBottom_toBottomOf="parent"
Berikut adalah kode XML lengkap untuk file tata letak activity_detail.xml
Anda, termasuk tombol PIN THIS ACTIVITY (SEMATKAN AKTIVITAS INI) yang baru saja kita tambahkan:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/detailActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
tools:context=".DetailActivity">
<TextView
android:id="@+id/textViewItemDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="36sp"
android:textColor="@color/obsidian"
app:layout_constraintBottom_toTopOf="@id/pinButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/pinButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pin_this_activity"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Tambahkan string pin_this_activity
ke res/values/strings.xml
.
<string name="pin_this_activity">PIN THIS ACTIVITY</string>
Menghubungkan tombol penyematan
- Deklarasikan variabel: Dalam file
DetailActivity.kt
, deklarasikan variabel untuk menyimpan referensi ke tombol PIN THIS ACTIVITY:
DetailActivity.kt
private lateinit var pinButton: Button
DetailActivity.java
private Button pinButton;
- Temukan tombol dalam tata letak dan tambahkan callback
setOnClickListener()
.
DetailActivity.kt / onCreate
pinButton = findViewById(R.id.pinButton)
pinButton.setOnClickListener {
pinActivityStackExample(taskId)
}
DetailActivity.java / onCreate()
Button pinButton = findViewById(R.id.pinButton);
pinButton.setOnClickListener( (view) => {
pinActivityStack(getTaskId());
});
- Buat metode baru bernama
pinActivityStackExample
di classDetailActivity
Anda. Kita akan mengimplementasikan logika penyematan yang sebenarnya di sini.
DetailActivity.kt
private fun pinActivityStackExample(taskId: Int) {
val splitAttributes: SplitAttributes = SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.ratio(0.66f))
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
.build()
val pinSplitRule = SplitPinRule.Builder()
.setSticky(true)
.setDefaultSplitAttributes(splitAttributes)
.build()
SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule)
}
DetailActivity.java
private void pinActivityStackExample(int taskId) {
SplitAttributes splitAttributes = new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.ratio(0.66f))
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
.build();
SplitPinRule pinSplitRule = new SplitPinRule.Builder()
.setSticky(true)
.setDefaultSplitAttributes(splitAttributes)
.build();
SplitController.getInstance(getApplicationContext()).pinTopActivityStack(taskId, pinSplitRule);
}
Catatan:
- Hanya satu aktivitas yang dapat disematkan dalam satu waktu. Lepaskan sematan aktivitas yang saat ini disematkan dengan
unpinTopActivityStack()
sebelum Anda menyematkan aktivitas lain.
- Untuk mengaktifkan perluasan panel saat menyematkan aktivitas, panggil
setDividerAttributes()
untuk
SplitAttributes
yang juga baru dibuat.
Perubahan navigasi kembali
Dengan WindowManager 1.4, perilaku navigasi kembali berubah. Peristiwa kembali dikirim ke aktivitas yang terakhir difokuskan saat menggunakan navigasi tombol.
Navigasi tombol:
- Dengan navigasi tombol, peristiwa kembali kini secara konsisten dikirim ke aktivitas yang terakhir difokuskan. Hal ini menyederhanakan perilaku navigasi kembali, sehingga lebih mudah diprediksi oleh pengguna.
Navigasi gestur:
- Android 14 (level API 34) dan yang lebih rendah: Gestur kembali mengirim peristiwa ke aktivitas tempat gestur terjadi, yang dapat menyebabkan perilaku yang tidak terduga dalam skenario layar terpisah.
- Android 15 (level API 35) dan yang lebih tinggi:
- Aktivitas dalam aplikasi yang sama: Gestur kembali secara konsisten menyelesaikan aktivitas teratas, terlepas dari arah geser, sehingga memberikan pengalaman yang lebih terpadu.
- Aktivitas dalam aplikasi (overlay) yang berbeda: Peristiwa kembali akan membuka aktivitas terakhir yang difokuskan, yang selaras dengan perilaku navigasi tombol.
Menjalankan aplikasi
Bangun dan jalankan aplikasi contoh.
Menyematkan aktivitas
- Buka layar
DetailActivity
. - Ketuk tombol PIN THIS ACTIVITY.
6. Peredupan dialog layar penuh
Meskipun penyematan aktivitas memfasilitasi tata letak layar terpisah, dialog dalam versi sebelumnya hanya meredupkan penampung aktivitasnya sendiri. Hal ini dapat menciptakan pengalaman visual yang terputus-putus, terutama jika Anda ingin dialog menjadi sorotan.
Solusi: WindowManager 1.4
- Kami siap membantu. Dengan WindowManager 1.4, dialog kini meredupkan seluruh jendela aplikasi secara default (
DimAreaBehavior.Companion.ON_TASK
), sehingga memberikan nuansa yang lebih imersif dan fokus. - Ingin kembali ke perilaku lama? Tidak masalah. Anda masih dapat memilih untuk meredupkan hanya penampung aktivitas menggunakan
ON_ACTIVITY_STACK
.
|
|
Berikut cara menggunakan ActivityEmbeddingController
untuk mengelola perilaku peredupan layar penuh:
Catatan: Peredupan dialog layar penuh tersedia dengan Ekstensi WindowManager 5 atau yang lebih baru.
SplitManager.kt / createSplit()
with(ActivityEmbeddingController.getInstance(context)) {
if (WindowSdkExtensions.getInstance().extensionVersion >= 5) {
setEmbeddingConfiguration(
EmbeddingConfiguration.Builder()
.setDimAreaBehavior(ON_TASK)
.build()
)
}
}
SplitManager.java / createSplit()
ActivityEmbeddingController controller = ActivityEmbeddingController.getInstance(context);
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 5) {
controller.setEmbeddingConfiguration(
new EmbeddingConfiguration.Builder()
.setDimAreaBehavior(EmbeddingConfiguration.DimAreaBehavior.ON_TASK)
.build()
);
}
Untuk menampilkan fitur peredupan layar penuh, kita akan membuat dialog pemberitahuan yang meminta konfirmasi pengguna sebelum menyematkan aktivitas. Setelah muncul, dialog ini akan meredupkan seluruh jendela aplikasi, bukan hanya penampung tempat aktivitas berada.
DetailActivity.kt
pinButton.setOnClickListener {
showAlertDialog(taskId)
}
...
private fun showAlertDialog(taskId: Int) {
val builder = AlertDialog.Builder(this)
builder.setTitle(getString(R.string.dialog_title))
builder.setMessage(getString(R.string.dialog_message))
builder.setPositiveButton(getString(R.string.button_yes)) { _, _ ->
if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
pinActivityStackExample(taskId)
}
}
builder.setNegativeButton(getString(R.string.button_cancel)) { _, _ ->
// Cancel
}
val dialog: AlertDialog = builder.create()
dialog.show()
}
DetailActivity.java
pinButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showAlertDialog(getTaskId());
}
});
...
private void showAlertDialog(int taskId) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.dialog_title));
builder.setMessage(getString(R.string.dialog_message));
builder.setPositiveButton(getString(R.string.button_yes), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
pinActivityStackExample(taskId);
}
}
});
builder.setNegativeButton(getString(R.string.button_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Cancel
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
Tambahkan string berikut ke res/values/strings.xml
.
<!-- Dialog information -->
<string name="dialog_title">Activity Pinning</string>
<string name="dialog_message">Confirm to pin this activity</string>
<string name="button_yes">Yes</string>
<string name="button_cancel">Cancel</string>
Menjalankan aplikasi
Bangun dan jalankan aplikasi contoh.
Klik tombol sematkan aktivitas:
- Dialog pemberitahuan akan muncul, yang meminta Anda mengonfirmasi tindakan penyematan.
- Perhatikan bagaimana seluruh layar, termasuk kedua panel terpisah, diredupkan, sehingga memfokuskan perhatian pada dialog.
7. Praktik terbaik
Mengizinkan pengguna menonaktifkan tata letak panel ganda
Agar transisi ke tata letak baru lebih lancar, mari beri pengguna kemampuan untuk beralih antara tampilan panel ganda dan kolom tunggal. Kita dapat melakukannya menggunakan SplitAttributesCalculator
dan SharedPreferences
untuk menyimpan preferensi pengguna.
Mengubah rasio pemisahan di Android 14 dan yang lebih rendah
Kita telah mempelajari perluasan panel, yang memberikan cara yang bagus bagi pengguna untuk menyesuaikan rasio pemisahan di Android 15 dan yang lebih tinggi. Namun, bagaimana kita dapat menawarkan tingkat fleksibilitas yang serupa kepada pengguna di versi Android yang lebih lama?
Mari pelajari cara SplitAttributesCalculator
dapat membantu kita mencapai hal ini dan memastikan pengalaman yang konsisten di berbagai perangkat.
Berikut ini contoh tampilannya:
Membuat layar setelan
Untuk memulai, mari buat layar setelan khusus untuk konfigurasi pengguna.
Dalam layar setelan ini, kita akan menyertakan tombol untuk mengaktifkan atau menonaktifkan fitur penyematan aktivitas untuk seluruh aplikasi. Selain itu, kita akan menyertakan status progres yang memungkinkan pengguna menyesuaikan rasio pemisahan tata letak panel ganda. Perhatikan bahwa nilai rasio pemisahan hanya akan diterapkan jika tombol penyematan aktivitas diaktifkan.
Setelah pengguna menetapkan nilai di SettingsActivity
, kita menyimpannya di SharedPreferences
untuk digunakan nanti di tempat lain dalam aplikasi.
build.gradle
Tambahkan dependensi preferensi.
implementation 'androidx.preference:preference-ktx:1.2.1' // Kotlin
Atau
implementation 'androidx.preference:preference:1.2.1' // Java
SettingsActivity.kt
package com.example.activity_embedding
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SeekBarPreference
import androidx.preference.SwitchPreferenceCompat
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.settings, SettingsFragment())
.commit()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) finishActivity()
return super.onOptionsItemSelected(item)
}
private fun finishActivity() { finish() }
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
findPreference<SwitchPreferenceCompat>("dual_pane")?.setOnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean) {
this.activity?.let {
SharePref(it.applicationContext).setAEFlag(true)
}
} else {
this.activity?.let {
SharePref(it.applicationContext).setAEFlag(false)
}
}
this.activity?.finish()
true
}
val splitRatioPreference: SeekBarPreference? = findPreference("split_ratio")
splitRatioPreference?.setOnPreferenceChangeListener { _, newValue ->
if (newValue is Int) {
this.activity?.let { SharePref(it.applicationContext).setSplitRatio(newValue.toFloat()/100) }
}
true
}
}
}
}
SettingsActivity.java
package com.example.activity_embedding;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SeekBarPreference;
import androidx.preference.SwitchPreferenceCompat;
public class SettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_activity);
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.settings, new SettingsFragment())
.commit();
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finishActivity();
return true;
}
return super.onOptionsItemSelected(item);
}
private void finishActivity() {
finish();
}
public static class SettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
SwitchPreferenceCompat dualPanePreference = findPreference("dual_pane");
if (dualPanePreference != null) {
dualPanePreference.setOnPreferenceChangeListener((preference, newValue) -> {
boolean isDualPane = (Boolean) newValue;
if (getActivity() != null) {
SharePref sharePref = new SharePref(getActivity().getApplicationContext());
sharePref.setAEFlag(isDualPane);
getActivity().finish();
}
return true;
});
}
SeekBarPreference splitRatioPreference = findPreference("split_ratio");
if (splitRatioPreference != null) {
splitRatioPreference.setOnPreferenceChangeListener((preference, newValue) -> {
if (newValue instanceof Integer) {
float splitRatio = ((Integer) newValue) / 100f;
if (getActivity() != null) {
SharePref sharePref = new SharePref(getActivity().getApplicationContext());
sharePref.setSplitRatio(splitRatio);
}
}
return true;
});
}
}
}
}
Tambahkan settings_activity.xml
di folder tata letak
settings_activity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Tambahkan SettingsActivity
ke file manifes Anda.
<activity
android:name=".SettingsActivity"
android:exported="false"
android:label="@string/title_activity_settings" />
Konfigurasi aturan pemisahan untuk SettingsActivity
.
SplitManager.kt / createSplit()
val settingActivityFilter = ActivityFilter(
ComponentName(context, SettingsActivity::class.java),
null
)
val settingActivityFilterSet = setOf(settingActivityFilter)
val settingActivityRule = ActivityRule.Builder(settingActivityFilterSet)
.setAlwaysExpand(true)
.build()
ruleController.addRule(settingActivityRule)
SplitManager.java / createSplit()
Set<ActivityFilter> settingActivityFilterSet = new HashSet<>();
ActivityFilter settingActivityFilter = new ActivityFilter(
new ComponentName(context, SettingsActivity.class),
null
);
settingActivityFilterSet.add(settingActivityFilter);
ActivityRule settingActivityRule = new ActivityRule.Builder(settingActivityFilterSet)
.setAlwaysExpand(true).build();
ruleController.addRule(settingActivityRule);
Berikut adalah kode untuk menyimpan setelan pengguna di SharedPreferences
.
SharedPref.kt
package com.example.activity_embedding
import android.content.Context
import android.content.SharedPreferences
class SharePref(context: Context) {
private val sharedPreferences: SharedPreferences =
context.getSharedPreferences("my_app_preferences", Context.MODE_PRIVATE)
companion object {
private const val AE_FLAG = "is_activity_embedding_enabled"
private const val SPLIT_RATIO = "activity_embedding_split_ratio"
const val DEFAULT_SPLIT_RATIO = 0.3f
}
fun setAEFlag(isEnabled: Boolean) {
sharedPreferences.edit().putBoolean(AE_FLAG, isEnabled).apply()
}
fun getAEFlag(): Boolean = sharedPreferences.getBoolean(AE_FLAG, true)
fun getSplitRatio(): Float = sharedPreferences.getFloat(SPLIT_RATIO, DEFAULT_SPLIT_RATIO)
fun setSplitRatio(ratio: Float) {
sharedPreferences.edit().putFloat(SPLIT_RATIO, ratio).apply()
}
}
SharedPref.java
package com.example.activity_embedding;
import android.content.Context;
import android.content.SharedPreferences;
public class SharePref {
private static final String PREF_NAME = "my_app_preferences";
private static final String AE_FLAG = "is_activity_embedding_enabled";
private static final String SPLIT_RATIO = "activity_embedding_split_ratio";
public static final float DEFAULT_SPLIT_RATIO = 0.3f;
private final SharedPreferences sharedPreferences;
public SharePref(Context context) {
this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
}
public void setAEFlag(boolean isEnabled) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(AE_FLAG, isEnabled);
editor.apply();
}
public boolean getAEFlag() {
return sharedPreferences.getBoolean(AE_FLAG, true);
}
public float getSplitRatio() {
return sharedPreferences.getFloat(SPLIT_RATIO, DEFAULT_SPLIT_RATIO);
}
public void setSplitRatio(float ratio) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putFloat(SPLIT_RATIO, ratio);
editor.apply();
}
}
Anda juga memerlukan xml tata letak layar preferensi. Buat root_preferences.xml
di bagian res/xml dengan kode berikut.
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory app:title="@string/split_setting_header">
<SwitchPreferenceCompat
app:key="dual_pane"
app:title="@string/dual_pane_title" />
<SeekBarPreference
app:key="split_ratio"
app:title="@string/split_ratio_title"
android:min="0"
android:max="100"
app:defaultValue="50"
app:showSeekBarValue="true" />
</PreferenceCategory>
</PreferenceScreen>
Lalu, tambahkan kode berikut ke res/values/strings.xml
.
<string name="title_activity_settings">SettingsActivity</string>
<string name="split_setting_header">Dual Pane Display</string>
<string name="dual_pane_title">Dual Pane</string>
<string name="split_ratio_title">Split Ratio</string>
Tambahkan SettingsActivity
ke menu
Mari hubungkan SettingsActivity
yang baru dibuat ke tujuan navigasi, sehingga pengguna dapat dengan mudah mengaksesnya dari antarmuka utama aplikasi.
- Dalam file
ListActivity
, deklarasikan variabel untuk menu navigasi bawah dan kolom samping navigasi kiri:
ListActivity.kt
private lateinit var navRail: NavigationRailView private lateinit var bottomNav: BottomNavigationView
ListActivity.java
private NavigationRailView navRail; private BottomNavigationView bottomNav;
- Di dalam metode
onCreate()
ListActivity
, gunakanfindViewById
untuk menghubungkan variabel ini ke tampilan yang sesuai dalam tata letak Anda; - Tambahkan
OnItemSelectedListener
ke menu navigasi bawah dan kolom samping navigasi untuk menangani peristiwa pemilihan item:
ListActivity.kt / onCreate()
navRail = findViewById(R.id.navigationRailView)
bottomNav = findViewById(R.id.bottomNavigationView)
val menuListener = NavigationBarView.OnItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
true
}
R.id.navigation_dashboard -> {
true
}
R.id.navigation_settings -> {
startActivity(Intent(this, SettingsActivity::class.java))
true
}
else -> false
}
}
navRail.setOnItemSelectedListener(menuListener)
bottomNav.setOnItemSelectedListener(menuListener)
ListActivity.java / onCreate()
NavigationRailView navRail = findViewById(R.id.navigationRailView);
BottomNavigationView bottomNav = findViewById(R.id.bottomNavigationView);
NavigationBarView.OnItemSelectedListener menuListener = new NavigationBarView.OnItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
// Handle navigation_home selection
return true;
case R.id.navigation_dashboard:
// Handle navigation_dashboard selection
return true;
case R.id.navigation_settings:
startActivity(new Intent(ListActivity.this, SettingsActivity.class));
return true;
default:
return false;
}
}
};
navRail.setOnItemSelectedListener(menuListener);
bottomNav.setOnItemSelectedListener(menuListener);
Aplikasi membaca SharedPreferences
dan merender aplikasi dalam mode terpisah atau mode SPLIT_TYPE_EXPAND
.
- Saat konfigurasi jendela berubah, program akan memeriksa apakah batasan jendela terpisah terpenuhi (jika lebar > 840 dp)
- Selain itu, aplikasi akan memeriksa nilai
SharedPreferences
untuk melihat apakah pengguna telah mengaktifkan tampilan jendela terpisah. Jika tidak, aplikasi akan menampilkanSplitAttribute
dengan jenisSPLIT_TYPE_EXPAND
. - Jika jendela terpisah diaktifkan, aplikasi akan membaca nilai
SharedPreferences
untuk mendapatkan rasio pemisahan. Hal ini hanya berfungsi jika versiWindowSDKExtensions
kurang dari 6, karena versi 6 sudah mendukung perluasan panel dan mengabaikan setelan rasio pemisahan. Sebagai gantinya, developer dapat mengizinkan pengguna menarik pemisah di UI.
ListActivity.kt / onCreate()
...
SplitController.getInstance(this).setSplitAttributesCalculator{
params -> params.defaultSplitAttributes
if (params.areDefaultConstraintsSatisfied) {
setWiderScreenNavigation(true)
if (SharePref(this.applicationContext).getAEFlag()) {
if (WindowSdkExtensions.getInstance().extensionVersion < 6) {
// Read a dynamic split ratio from shared preference.
val currentSplit = SharePref(this.applicationContext).getSplitRatio()
if (currentSplit != SharePref.DEFAULT_SPLIT_RATIO) {
return@setSplitAttributesCalculator SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.ratio(SharePref(this.applicationContext).getSplitRatio()))
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
.build()
}
}
return@setSplitAttributesCalculator params.defaultSplitAttributes
} else {
SplitAttributes.Builder()
.setSplitType(SPLIT_TYPE_EXPAND)
.build()
}
} else {
setWiderScreenNavigation(false)
SplitAttributes.Builder()
.setSplitType(SPLIT_TYPE_EXPAND)
.build()
}
}
...
ListActivity.java / onCreate()
...
SplitController.getInstance(this).setSplitAttributesCalculator(params -> {
if (params.areDefaultConstraintsSatisfied()) {
setWiderScreenNavigation(true);
SharePref sharedPreference = new SharePref(this.getApplicationContext());
if (sharedPreference.getAEFlag()) {
if (WindowSdkExtensions.getInstance().getExtensionVersion() < 6) {
// Read a dynamic split ratio from shared preference.
float currentSplit = sharedPreference.getSplitRatio();
if (currentSplit != SharePref.DEFAULT_SPLIT_RATIO) {
return new SplitAttributes.Builder()
.setSplitType(SplitAttributes.SplitType.ratio(sharedPreference.getSplitRatio()))
.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
.build();
}
}
return params.getDefaultSplitAttributes();
} else {
return new SplitAttributes.Builder()
.setSplitType(SPLIT_TYPE_EXPAND)
.build();
}
} else {
setWiderScreenNavigation(false);
return new SplitAttributes.Builder()
.setSplitType(SPLIT_TYPE_EXPAND)
.build();
}
});
...
Untuk memicu SplitAttributesCalculator
setelah perubahan setelan, kita perlu membatalkan validasi atribut saat ini. Kita melakukannya dengan memanggil invalidateVisibleActivityStacks
()
dari ActivityEmbeddingController
;
sebelum WindowManager 1.4, metode ini bernama
invalidateTopVisibleSplitAttributes
.
ListActivity.kt / onResume()
override fun onResume() {
super.onResume()
ActivityEmbeddingController.getInstance(this).invalidateVisibleActivityStacks()
}
ListActivity.java / onResume()
@Override
public void onResume() {
super.onResume();
ActivityEmbeddingController.getInstance(this).invalidateVisibleActivityStacks();
}
Menjalankan aplikasi
Bangun dan jalankan aplikasi contoh.
Mempelajari setelan:
- Buka layar setelan.
- Aktifkan dan nonaktifkan tombol Enable Split Window.
- Sesuaikan penggeser rasio pemisahan (jika tersedia di perangkat Anda).
Memperhatikan perubahan tata letak:
- Di perangkat yang menjalankan Android 14 dan yang lebih rendah: Tata letak akan beralih antara mode panel tunggal dan panel ganda berdasarkan tombol, dan rasio pemisahan akan berubah saat Anda menyesuaikan penggeser.
- Di perangkat yang menjalankan Android 15 dan yang lebih tinggi: Perluasan panel akan memungkinkan Anda mengubah ukuran panel secara dinamis, terlepas dari setelan penggeser.
8. Selamat!
Bagus! Anda telah berhasil meningkatkan kualitas aplikasi dengan fitur baru yang canggih menggunakan penyematan aktivitas dan WindowManager. Pengguna Anda kini akan menikmati pengalaman yang lebih fleksibel, intuitif, dan menarik di perangkat layar besar, terlepas dari versi Android mereka.
9. Pelajari lebih lanjut
- Panduan developer — Penyematan aktivitas
- Dokumentasi referensi — androidx.window.embedding