本页介绍如何设置环境并在应用中构建 Slice。
注意:Android Studio 3.2 或更高版本包含可帮助您进行 Slice 开发的额外工具和功能。
- AndroidX 重构工具:如果你的项目使用 AndroidX 库,则需要此工具。
- Slice lint 检查:可在构建 Slice 时捕捉常见的反模式。
SliceProvider
模板:处理构建SliceProvider
时的样板代码。
下载并安装 Slice 查看器
下载最新的示例 Slice Viewer APK 版本,你可以使用它来测试你的 Slice,而无需实现 SliceView
API。
如果你的环境中未正确设置 ADB,请参阅 ADB 指南以了解详情。
通过在下载的 slice-viewer.apk
所在的同一目录中运行以下命令,在设备上安装 Slice 查看器:
adb install -r -t slice-viewer.apk
运行 Slice 查看器
你可以从 Android Studio 项目或命令行启动 Slice 查看器
从 Android Studio 项目启动 Slice 查看器
- 在你的项目中,选择 Run > Edit Configurations...
- 在左上角,点击绿色加号图标
选择 Android App
在名称字段中输入 slice
在 Module 下拉列表中选择你的应用模块
在 Launch Options 下,从 Launch 下拉列表中选择 URL
在 URL 字段中输入
slice-<your slice URI>
示例:
slice-content://com.example.your.sliceuri
点击 OK
通过 ADB (命令行) 启动 Slice 查看器工具
从 Android Studio 运行你的应用
adb install -t -r <yourapp>.apk
通过运行以下命令查看你的 Slice:
adb shell am start -a android.intent.action.VIEW -d slice-<your slice URI>
Slice 查看器显示单个 WiFi Slice
在同一个位置查看所有 Slice
除了启动单个 Slice 之外,你还可以查看一个持久的 Slice 列表。
- 使用搜索栏通过 URI 手动搜索你的 Slice(例如
content://com.example.android.app/hello
)。每次搜索时,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 路径并返回 你好世界 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 查看器”部分创建的 slice 运行配置,传入 你好世界 Slice 的 Slice URI(例如 slice-content://com.android.example.slicesample/hello
),以便在 Slice 查看器中查看。
互动式 Slice
与通知类似,你可以通过附加在用户互动时触发的 PendingIntent
对象来处理 Slice 内的点击事件。下方示例启动了一个可以接收并处理这些 intent 的 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 还支持其他输入类型,例如开关,这些类型会在发送到应用的 intent 中包含状态。
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 还可以包含动态内容。在以下示例中,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 支持各种模板。如需详细了解模板选项和行为,请参阅模板。