本頁面說明如何設定環境,並在應用程式中建構 Slice。
注意:Android Studio 3.2 以上版本內有其他工具和功能,可協助您進行 Slice 開發作業:
- AndroidX 重構工具:如果您使用 AndroidX 程式庫的專案,就需要使用 AndroidX 重構工具。
- Slice Lint 檢查:在建構 Slice 時找出常見的反最佳做法
SliceProvider
範本:在建立SliceProvider
時處理樣板
下載並安裝 Slice 檢視器
下載最新的 Slice Viewer APK 版本範例,不必導入 SliceView
API,即可用來測試 Slice。
如果環境中未正確設定 ADB,請參閱 ADB 指南瞭解詳情。
在已下載 slice-viewer.apk
的目錄中執行下列指令,以便為裝置安裝 Slice Viewer:
adb install -r -t slice-viewer.apk
執行 Slice 檢視器
您可以透過 Android Studio 專案或指令列啟動 Slice Viewer:
透過 Android Studio 專案啟動 Slice Viewer
- 在專案中,依序選取「Run」>「Edit Configurations...」。
- 按一下左上角的綠色加號
選取「Android 應用程式」。
在名稱欄位中輸入 slice
在「Module」下拉式選單中選取應用程式模組
在「Launch Options」下,從「Launch」下拉式選單中選取「URL」
在網址欄位中輸入
slice-<your slice URI>
範例:
slice-content://com.example.your.sliceuri
按一下 [確定]。
透過 ADB 啟動 Slice Viewer 工具
透過 Android Studio 執行應用程式:
adb install -t -r <yourapp>.apk
請執行下列指令來查看 Slice:
adb shell am start -a android.intent.action.VIEW -d slice-<your slice URI>
顯示單一 Wi-Fi Slice 的 Slice 檢視器
集中查看所有 Slice
除了啟動單一 Slice 外,您還可以查看 Slice 的持續性清單。
- 使用搜尋列,透過 URI (例如
content://com.example.android.app/hello
) 手動搜尋 Slice。每次搜尋時,Slice 都會新增至清單中。 - 每次使用 Slice URI 啟動 Slice 檢視器工具時,系統都會將 Slice 加入清單中。
- 滑動 Slice 即可從清單中移除。
- 輕觸 Slice 的 URI,即可查看僅包含該 Slice 的頁面。這與使用 Slice URI 啟動 Slice 檢視器的效果相同。
顯示 Slice 清單的 Slice 檢視器
在不同模式下查看 Slice
顯示 Slice 的應用程式可以在執行階段修改 SliceView#mode
,因此您應確保 Slice 在各模式中都能正常顯示。如要切換模式,請選取頁面右上方的選單圖示。
單一 Slice 檢視器,模式已設為「small」
建構您的第一個 Slice
如要建構 Slice,請開啟 Android Studio 專案,在 src
套件上按一下滑鼠右鍵,然後選取「New...」>「Other」>「Slice Provider」。這項操作會建立可擴充 SliceProvider
的類別,將必要的提供者項目新增至 AndroidManifest.xml
,並修改 build.gradle
以新增必要的 Slice 依附元件。
AndroidManifest.xml
修改內容如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.app"> ... <application> ... <provider android:name="MySliceProvider" android:authorities="com.example.android.app" android:exported="true" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.app.slice.category.SLICE" /> </intent-filter> </provider> ... </application> </manifest>
下列依附元件已新增至 build.gradle
:
Kotlin
dependencies { // ... implementation "androidx.slice:slice-builders-ktx:(latest version)" // ... }
Java
dependencies { // ... implementation "androidx.slice:slice-builders:(latest version)" // ... }
每個 Slice 都有相關聯的 URI。當途徑想要顯示 Slice 時,會使用此 URI 向應用程式傳送繫結要求。接著,應用程式會處理這項要求,並透過 onBindSlice
方法動態建構 Slice。表面便可以視情況顯示 Slice。
以下是 onBindSlice
方法的範例,用於檢查 /hello
URI 路徑並傳回 Hello World Slice:
Kotlin
override fun onBindSlice(sliceUri: Uri): Slice? { val activityAction = createActivityAction() return if (sliceUri.path == "/hello") { list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = activityAction title = "Hello World." } } } else { list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = activityAction title = "URI not recognized." } } } }
Java
@Override public Slice onBindSlice(Uri sliceUri) { if (getContext() == null) { return null; } SliceAction activityAction = createActivityAction(); ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY); // Create parent ListBuilder. if ("/hello".equals(sliceUri.getPath())) { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle("Hello World") .setPrimaryAction(activityAction) ); } else { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle("URI not recognized") .setPrimaryAction(activityAction) ); } return listBuilder.build(); }
使用您在上方 Slice Viewer 區段建立的 slice 執行設定,傳入 Hello World Slice 的 Slice URI (例如 slice-content://com.android.example.slicesample/hello
),以便在 Slice 檢視器中查看。
互動式配量
與通知類似,您可以附加在使用者互動時觸發的 PendingIntent
物件,以處理 Slice 中的點擊動作。以下範例會啟動可接收及處理這些意圖的 Activity
:
Kotlin
fun createSlice(sliceUri: Uri): Slice { val activityAction = createActivityAction() return list(context, sliceUri, INFINITY) { row { title = "Perform action in app" primaryAction = activityAction } } } fun createActivityAction(): SliceAction { val intent = Intent(context, MainActivity::class.java) return SliceAction.create( PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0), IconCompat.createWithResource(context, R.drawable.ic_home), ListBuilder.ICON_IMAGE, "Enter app" ) }
Java
public Slice createSlice(Uri sliceUri) { if (getContext() == null) { return null; } SliceAction activityAction = createActivityAction(); return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY) .addRow(new ListBuilder.RowBuilder() .setTitle("Perform action in app.") .setPrimaryAction(activityAction) ).build(); } public SliceAction createActivityAction() { if (getContext() == null) { return null; } return SliceAction.create( PendingIntent.getActivity( getContext(), 0, new Intent(getContext(), MainActivity.class), 0 ), IconCompat.createWithResource(getContext(), R.drawable.ic_home), ListBuilder.ICON_IMAGE, "Enter app" ); }
Slice 也支援其他輸入類型 (例如切換),在傳送至應用程式的意圖中包含狀態。
Kotlin
fun createBrightnessSlice(sliceUri: Uri): Slice { val toggleAction = SliceAction.createToggle( createToggleIntent(), "Toggle adaptive brightness", true ) return list(context, sliceUri, ListBuilder.INFINITY) { row { title = "Adaptive brightness" subtitle = "Optimizes brightness for available light" primaryAction = toggleAction } inputRange { inputAction = (brightnessPendingIntent) max = 100 value = 45 } } } fun createToggleIntent(): PendingIntent { val intent = Intent(context, MyBroadcastReceiver::class.java) return PendingIntent.getBroadcast(context, 0, intent, 0) }
Java
public Slice createBrightnessSlice(Uri sliceUri) { if (getContext() == null) { return null; } SliceAction toggleAction = SliceAction.createToggle( createToggleIntent(), "Toggle adaptive brightness", true ); ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY) .addRow(new ListBuilder.RowBuilder() .setTitle("Adaptive brightness") .setSubtitle("Optimizes brightness for available light.") .setPrimaryAction(toggleAction) ).addInputRange(new ListBuilder.InputRangeBuilder() .setInputAction(brightnessPendingIntent) .setMax(100) .setValue(45) ); return listBuilder.build(); } public PendingIntent createToggleIntent() { Intent intent = new Intent(getContext(), MyBroadcastReceiver.class); return PendingIntent.getBroadcast(getContext(), 0, intent, 0); }
接著,接收端即可查看其接收的狀態:
Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.hasExtra(Slice.EXTRA_TOGGLE_STATE)) { Toast.makeText(context, "Toggled: " + intent.getBooleanExtra( Slice.EXTRA_TOGGLE_STATE, false), Toast.LENGTH_LONG).show() } } companion object { const val EXTRA_MESSAGE = "message" } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { public static String EXTRA_MESSAGE = "message"; @Override public void onReceive(Context context, Intent intent) { if (intent.hasExtra(EXTRA_TOGGLE_STATE)) { Toast.makeText(context, "Toggled: " + intent.getBooleanExtra( EXTRA_TOGGLE_STATE, false), Toast.LENGTH_LONG).show(); } } }
動態配量
Slice 也可以包含動態內容。在以下範例中,Slice 現在包含在其內容中接收的廣播數量:
Kotlin
fun createDynamicSlice(sliceUri: Uri): Slice { return when (sliceUri.path) { "/count" -> { val toastAndIncrementAction = SliceAction.create( createToastAndIncrementIntent("Item clicked."), actionIcon, ListBuilder.ICON_IMAGE, "Increment." ) list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = toastAndIncrementAction title = "Count: ${MyBroadcastReceiver.receivedCount}" subtitle = "Click me" } } } else -> { list(context, sliceUri, ListBuilder.INFINITY) { row { primaryAction = createActivityAction() title = "URI not found." } } } } }
Java
public Slice createDynamicSlice(Uri sliceUri) { if (getContext() == null || sliceUri.getPath() == null) { return null; } ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY); switch (sliceUri.getPath()) { case "/count": SliceAction toastAndIncrementAction = SliceAction.create( createToastAndIncrementIntent("Item clicked."), actionIcon, ListBuilder.ICON_IMAGE, "Increment." ); listBuilder.addRow( new ListBuilder.RowBuilder() .setPrimaryAction(toastAndIncrementAction) .setTitle("Count: " + MyBroadcastReceiver.sReceivedCount) .setSubtitle("Click me") ); break; default: listBuilder.addRow( new ListBuilder.RowBuilder() .setPrimaryAction(createActivityAction()) .setTitle("URI not found.") ); break; } return listBuilder.build(); } public PendingIntent createToastAndIncrementIntent(String s) { Intent intent = new Intent(getContext(), MyBroadcastReceiver.class) .putExtra(MyBroadcastReceiver.EXTRA_MESSAGE, s); return PendingIntent.getBroadcast(getContext(), 0, intent, 0); }
在本例中,雖然顯示計數,卻沒有自行更新。您可以使用 ContentResolver#notifyChange
修改廣播接收器,向系統通知發生變更。
Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.hasExtra(Slice.EXTRA_TOGGLE_STATE)) { Toast.makeText( context, "Toggled: " + intent.getBooleanExtra( Slice.EXTRA_TOGGLE_STATE, false ), Toast.LENGTH_LONG ).show() receivedCount++; context.contentResolver.notifyChange(sliceUri, null) } } companion object { var receivedCount = 0 val sliceUri = Uri.parse("content://com.android.example.slicesample/count") const val EXTRA_MESSAGE = "message" } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { public static int sReceivedCount = 0; public static String EXTRA_MESSAGE = "message"; private static Uri sliceUri = Uri.parse("content://com.android.example.slicesample/count"); @Override public void onReceive(Context context, Intent intent) { if (intent.hasExtra(EXTRA_TOGGLE_STATE)) { Toast.makeText(context, "Toggled: " + intent.getBooleanExtra( EXTRA_TOGGLE_STATE, false), Toast.LENGTH_LONG).show(); sReceivedCount++; context.getContentResolver().notifyChange(sliceUri, null); } } }
範本
Slice 支援多種範本。如要進一步瞭解範本選項和行為,請參閱範本。