開始使用

本頁面說明如何設定環境,並在應用程式中建構 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

  1. 在專案中,依序選取「Run」>「Edit Configurations...」
  2. 按一下左上角的綠色加號
  3. 選取「Android 應用程式」

  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 (例如 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 支援多種範本。如要進一步瞭解範本選項和行為,請參閱範本