Pierwsze kroki

Na tej stronie dowiesz się, jak skonfigurować środowisko i utworzyć wycinki w aplikacji.

Uwaga: Android Studio w wersji 3.2 lub nowszej zawiera dodatkowe narzędzia i funkcje, które mogą pomóc w tworzeniu wycinków:

  • Narzędzie do refaktoryzacji AndroidX: wymagane, jeśli pracujesz w projekcie, który korzysta z bibliotek AndroidX.
  • Sprawdzanie lintowania wycinków: wychwytywanie typowych elementów antypraktycznych podczas tworzenia wycinków
  • Szablon SliceProvider: obsługuje schematy podczas tworzenia obiektu SliceProvider

Pobieranie i instalowanie przeglądarki wycinków

Pobierz najnowszą przykładową wersję pliku APK z podglądem wycinków, której możesz używać do testowania wycinków bez implementowania interfejsu SliceView API.

Jeśli narzędzie ADB nie jest prawidłowo skonfigurowane w Twoim środowisku, zapoznaj się z przewodnikiem ADB, by dowiedzieć się więcej.

Zainstaluj na urządzeniu przeglądarkę wycinków, uruchamiając to polecenie w tym samym katalogu, w którym znajduje się pobrany plik slice-viewer.apk:

adb install -r -t slice-viewer.apk

Uruchamianie przeglądarki wycinków

Wyświetlający wycinki możesz uruchomić z poziomu projektu Android Studio lub z wiersza poleceń:

Uruchamianie przeglądarki wycinków z projektu Android Studio

  1. W projekcie wybierz Uruchom > Edytuj konfiguracje...
  2. W lewym górnym rogu kliknij zielony znak plus.
  3. Wybierz Aplikacja na Androida

  4. W polu nazwy wpisz slice

  5. Wybierz moduł aplikacji w menu Moduł.

  6. W sekcji Launch Options (Opcje uruchamiania) wybierz URL z menu Uruchom.

  7. Wpisz slice-<your slice URI> w polu adresu URL

    Przykład: slice-content://com.example.your.sliceuri

  8. Kliknij OK.

Uruchamianie narzędzia Wyświetlający wycinki za pomocą ADB (wiersz poleceń)

Uruchom aplikację z Android Studio:

adb install -t -r <yourapp>.apk

Wyświetl wycinek, uruchamiając to polecenie:

adb shell am start -a android.intent.action.VIEW -d slice-<your slice URI>

Podgląd wycinków przedstawiający pojedynczy wycinek sieci Wi-Fi

Wyświetlanie wszystkich wycinków w jednym miejscu

Oprócz uruchomienia pojedynczego wycinka możesz wyświetlić stałą listę wycinków.

  • Użyj paska wyszukiwania, aby ręcznie wyszukać wycinki za pomocą identyfikatora URI (na przykład content://com.example.android.app/hello). Za każdym razem, gdy przeprowadzasz wyszukiwanie, wycinek jest dodawany do listy.
  • Za każdym razem, gdy uruchamiasz narzędzie Wyświetlający wycinek z identyfikatorem URI wycinka, wycinek jest dodawany do listy.
  • Możesz przesunąć wycinek, aby usunąć go z listy.
  • Kliknij identyfikator URI wycinka, aby wyświetlić stronę zawierającą tylko ten wycinek. Działa to tak samo jak uruchomienie przeglądarki wycinków za pomocą identyfikatora URI wycinka.

Przeglądarka wycinków wyświetlająca listę wycinków

Wyświetlanie wycinka w różnych trybach

Aplikacja wyświetlająca wycinek może modyfikować SliceView#mode w czasie działania, dlatego upewnij się, że wygląda on zgodnie z oczekiwaniami w każdym trybie. Aby zmienić tryb, wybierz ikonę menu w prawym górnym rogu strony.

Przeglądarka pojedynczego wycinka z trybem „małym”

Tworzenie pierwszego wycinka

Aby utworzyć wycinek, otwórz projekt Android Studio, kliknij prawym przyciskiem myszy pakiet src i wybierz Nowy... > Inne > Dostawca wycinka. Spowoduje to utworzenie klasy, która rozszerza zakres SliceProvider, doda wymagany wpis dostawcy do AndroidManifest.xml i modyfikuje build.gradle, aby dodać wymagane zależności wycinka.

Poniżej przedstawiono modyfikację elementu 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 dodaje te zależności:

Kotlin

dependencies {
// ...
    implementation "androidx.slice:slice-builders-ktx:(latest version)"
// ...
}

Java

dependencies {
// ...
    implementation "androidx.slice:slice-builders:(latest version)"
// ...
}

Z każdym wycinkiem jest powiązany powiązany identyfikator URI. Gdy platforma chce wyświetlić wycinek, wysyła do aplikacji żądanie powiązania z tym identyfikatorem URI. Aplikacja obsługuje to żądanie i dynamicznie kompiluje wycinek za pomocą metody onBindSlice. Powierzchnia może następnie wyświetlić Wycinek w odpowiednich przypadkach.

Poniżej znajdziesz przykład metody onBindSlice, która sprawdza ścieżkę identyfikatora URI /hello i zwraca wycinek 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();
}

Użyj konfiguracji uruchamiania wycinka utworzonej w sekcji Wyświetlający wycinek powyżej, aby przekazać identyfikator URI wycinka (na przykład slice-content://com.android.example.slicesample/hello) wycinka Hello World, aby wyświetlić go w podglądzie wycinka.

Interaktywne wycinki

Podobnie jak w przypadku powiadomień, możesz obsługiwać kliknięcia w wycinku, dołączając obiekty PendingIntent wywoływane podczas interakcji użytkownika. Przykład poniżej rozpoczyna się Activity, który może odbierać i obsługiwać te intencje:

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"
    );
}

Wycinki obsługują też inne typy danych wejściowych, np. przełączniki, które uwzględniają stan w intencji wysyłanej do aplikacji.

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);
}

Odbiorca może wówczas sprawdzić stan otrzymanych danych:

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();
        }
    }
}

Wycinki dynamiczne

Wycinki mogą też zawierać zawartość dynamiczną. W poniższym przykładzie wycinek zawiera teraz liczbę transmisji odebranych w treści:

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);
}

W tym przykładzie liczba jest wyświetlana, ale nie aktualizuje się sama. Za pomocą ContentResolver#notifyChange możesz zmodyfikować odbiornik, aby powiadamiać system o zmianie.

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);
        }
    }
}

Szablony

Wycinki obsługują różne szablony. Więcej informacji o opcjach i działaniach szablonów znajdziesz w sekcji Szablony.