開始使用

本頁面說明如何設定環境並建構 應用程式中的 Slice

注意:Android Studio 3.2 以上版本包含額外 提供有助於開發 Slice 的工具和功能:

  • AndroidX 重構工具:如果您處理的專案 使用了 AndroidX 程式庫
  • Slices Lint 檢查:在建構時找出常見的反實踐 配量
  • SliceProvider 範本:處理樣板 建立SliceProvider

下載並安裝 Slice 檢視器

下載最新試聽內容 Slice Viewer APK 版本 ;您不需要實作 SliceView API。

如果您的環境中沒有正確設定 ADB,請參閱 ADB 指南

如要在裝置上安裝 Slice Viewer,請在 與下載的 slice-viewer.apk 相同的目錄:

adb install -r -t slice-viewer.apk

執行 Slice 檢視器

您可以透過 Android Studio 專案或 指令列:

從 Android Studio 專案啟動 Slice Viewer

  1. 在專案中選取 「執行」>編輯設定...
  2. 按一下左上角的綠色加號
  3. 選取「Android App」

  4. 在名稱欄位中輸入 slice

  5. 在「Module」下拉式選單中選取應用程式模組

  6. 在「Launch Options」下方,從「Launch」下拉式選單中選取「URL」

  7. 在網址欄位中輸入 slice-<your slice URI>

    範例:slice-content://com.example.your.sliceuri

  8. 按一下 [確定]。

,瞭解如何調查及移除這項存取權。

透過 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 手動搜尋 Slice (例如 content://com.example.android.app/hello)。每次搜尋時,[Slice] 會比較 已加入清單
  • 每次使用 Slice URI 啟動 Slice Viewer 工具時,系統就會新增 Slice 然後再加入清單中
  • 只要滑動 Slice,即可從清單中移除 Slice。
  • 輕觸 Slice 的 URI,查看只包含該 Slice 的頁面。這有 效果與使用 Slice URI 啟動 Slice 檢視器的效果相同。
,瞭解如何調查及移除這項存取權。

顯示 Slice 清單的 Slice 檢視器

以不同模式查看 Slice

顯示 Slice 的應用程式可以修改 SliceView#mode敬上 所以您應確保 Slice 在每種模式下都能正常顯示。 選取頁面右上方的「選單」圖示,即可變更模式。

單項 Slice 檢視器,模式設為「小」

建構第一個 Slice

如要建立 Slice,請開啟 Android Studio 專案,在 src 上按一下滑鼠右鍵 然後選取 [New... (新增...)>其他 >Slice 提供者。這項操作會建立類別 擴充 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。

以下 onBindSlice 方法範例會檢查 /hello URI 並傳回 Hello World 配量:

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 執行設定 傳入您的 Slice URI (例如 slice-content://com.android.example.slicesample/hello) 的 Hello World 以便在 Slice 檢視器中查看。

互動式配量

與通知類似,您可以在 Slice 中附加 PendingIntent 物件, 會在使用者互動時觸發以下範例會從 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 支援多種範本。進一步瞭解範本選項和 請參閱範本一文