切片模板

本文档详细介绍了如何使用 Android Jetpack 中的模板构建器来构建 切片

定义您的切片模板

切片是使用 ListBuilder 构建的。`ListBuilder` 允许您添加在列表中显示的不同类型的行。本节描述每种类型的行以及它们的构建方式。

SliceAction

切片模板最基本的元素是 SliceAction。一个 SliceAction 包含一个标签以及一个 PendingIntent,并且是以下几种类型之一:

  • 图标按钮
  • 默认切换按钮
  • 自定义切换按钮(带开/关状态的可绘制对象)

SliceAction 由本节其余部分中描述的模板构建器使用。 SliceAction 可以定义图像模式,该模式确定如何呈现操作的图像

  • ICON_IMAGE:很小且可着色
  • SMALL_IMAGE:较小且不可着色
  • LARGE_IMAGE:最大且不可着色

HeaderBuilder

在大多数情况下,您应该使用 HeaderBuilder 为模板设置标题。标题可以支持以下内容:

  • 标题
  • 副标题
  • 摘要副标题
  • 主要操作

下面显示了一些示例标题配置。请注意,灰色框显示了潜在的图标和填充位置

不同界面上的标题渲染

当需要切片时,显示界面决定如何渲染切片。请注意,不同托管界面之间的渲染可能略有不同。

在较小的格式中,通常只显示标题(如果存在)。如果您为标题指定了摘要,则会显示摘要文本而不是副标题文本。

如果您未在模板中指定标题,则通常会改为显示添加到 ListBuilder 的第一行。

HeaderBuilder 示例 - 带标题的简单列表切片

Kotlin

fun createSliceWithHeader(sliceUri: Uri) =
    list(context, sliceUri, ListBuilder.INFINITY) {
        setAccentColor(0xff0F9D) // Specify color for tinting icons
        header {
            title = "Get a ride"
            subtitle = "Ride in 4 min"
            summary = "Work in 1 hour 45 min | Home in 12 min"
        }
        row {
            title = "Home"
            subtitle = "12 miles | 12 min | $9.00"
            addEndItem(
                IconCompat.createWithResource(context, R.drawable.ic_home),
                ListBuilder.ICON_IMAGE
            )
        }
    }

Java

public Slice createSliceWithHeader(Uri sliceUri) {
    if (getContext() == null) {
        return null;
    }

    // Construct the parent.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
            .setAccentColor(0xff0F9D58) // Specify color for tinting icons.
            .setHeader( // Create the header and add to slice.
                    new HeaderBuilder()
                            .setTitle("Get a ride")
                            .setSubtitle("Ride in 4 min.")
                            .setSummary("Work in 1 hour 45 min | Home in 12 min.")
            ).addRow(new RowBuilder() // Add a row.
                    .setPrimaryAction(
                            createActivityAction()) // A slice always needs a SliceAction.
                    .setTitle("Home")
                    .setSubtitle("12 miles | 12 min | $9.00")
                    .addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_home),
                            SliceHints.ICON_IMAGE)
            ); // Add more rows if needed...
    return listBuilder.build();
}

标题中的 SliceActions

切片标题也可以显示 SliceActions

Kotlin

fun createSliceWithActionInHeader(sliceUri: Uri): Slice {
    // Construct our slice actions.
    val noteAction = SliceAction.create(
        takeNoteIntent,
        IconCompat.createWithResource(context, R.drawable.ic_pencil),
        ICON_IMAGE,
        "Take note"
    )

    val voiceNoteAction = SliceAction.create(
        voiceNoteIntent,
        IconCompat.createWithResource(context, R.drawable.ic_mic),
        ICON_IMAGE,
        "Take voice note"
    )

    val cameraNoteAction = SliceAction.create(
        cameraNoteIntent,
        IconCompat.createWithResource(context, R.drawable.ic_camera),
        ICON_IMAGE,
        "Create photo note"
    )

    // Construct the list.
    return list(context, sliceUri, ListBuilder.INFINITY) {
        setAccentColor(0xfff4b4) // Specify color for tinting icons
        header {
            title = "Create new note"
            subtitle = "Easily done with this note taking app"
        }
        addAction(noteAction)
        addAction(voiceNoteAction)
        addAction(cameraNoteAction)
    }
}

Java

public Slice createSliceWithActionInHeader(Uri sliceUri) {
    if (getContext() == null) {
        return null;
    }
    // Construct our slice actions.
    SliceAction noteAction = SliceAction.create(takeNoteIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_pencil),
            ListBuilder.ICON_IMAGE, "Take note");

    SliceAction voiceNoteAction = SliceAction.create(voiceNoteIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_mic),
            ListBuilder.ICON_IMAGE,
            "Take voice note");

    SliceAction cameraNoteAction = SliceAction.create(cameraNoteIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_camera),
            ListBuilder.ICON_IMAGE,
            "Create photo note");


    // Construct the list.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
            .setAccentColor(0xfff4b400) // Specify color for tinting icons
            .setHeader(new HeaderBuilder() // Construct the header.
                    .setTitle("Create new note")
                    .setSubtitle("Easily done with this note taking app")
            )
            .addRow(new RowBuilder()
                    .setTitle("Enter app")
                    .setPrimaryAction(createActivityAction())
            )
            // Add the actions to the ListBuilder.
            .addAction(noteAction)
            .addAction(voiceNoteAction)
            .addAction(cameraNoteAction);
    return listBuilder.build();
}

RowBuilder

您可以使用 RowBuilder 构建一行内容。一行可以支持以下任何内容:

  • 标题
  • 副标题
  • 起始项:SliceAction、图标或时间戳
  • 结束项:SliceAction、图标或时间戳
  • 主要操作

您可以多种方式组合行内容,但需遵守以下限制:

  • 起始项不会显示在切片的首行
  • 结束项不能混合使用 SliceAction 对象和 Icon 对象
  • 一行只能包含一个时间戳

以下图像显示了内容示例行。请注意,灰色框显示了潜在的图标和填充位置

RowBuilder 示例 - Wi-Fi 切换按钮

以下示例演示了一行内容,其中包含主要操作和默认切换按钮。

Kotlin

fun createActionWithActionInRow(sliceUri: Uri): Slice {
    // Primary action - open wifi settings.
    val wifiAction = SliceAction.create(
        wifiSettingsPendingIntent,
        IconCompat.createWithResource(context, R.drawable.ic_wifi),
        ICON_IMAGE,
        "Wi-Fi Settings"
    )

    // Toggle action - toggle wifi.
    val toggleAction = SliceAction.createToggle(
        wifiTogglePendingIntent,
        "Toggle Wi-Fi",
        isConnected /* isChecked */
    )

    // Create the parent builder.
    return list(context, wifiUri, ListBuilder.INFINITY) {
        setAccentColor(0xff4285) // Specify color for tinting icons / controls.
        row {
            title = "Wi-Fi"
            primaryAction = wifiAction
            addEndItem(toggleAction)
        }
    }
}

Java

public Slice createActionWithActionInRow(Uri sliceUri) {
    if (getContext() == null) {
        return null;
    }
    // Primary action - open wifi settings.
    SliceAction primaryAction = SliceAction.create(wifiSettingsPendingIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_wifi),
            ListBuilder.ICON_IMAGE,
            "Wi-Fi Settings"
    );

    // Toggle action - toggle wifi.
    SliceAction toggleAction = SliceAction.createToggle(wifiTogglePendingIntent,
            "Toggle Wi-Fi", isConnected /* isChecked */);

    // Create the parent builder.
    ListBuilder listBuilder = new ListBuilder(getContext(), wifiUri, ListBuilder.INFINITY)
            // Specify color for tinting icons / controls.
            .setAccentColor(0xff4285f4)
            // Create and add a row.
            .addRow(new RowBuilder()
                    .setTitle("Wi-Fi")
                    .setPrimaryAction(primaryAction)
                    .addEndItem(toggleAction));
    // Build the slice.
    return listBuilder.build();
}

GridBuilder

您可以使用 GridBuilder 构建内容网格。网格可以支持以下图像类型:

  • ICON_IMAGE:很小且可着色
  • SMALL_IMAGE:较小且不可着色
  • LARGE_IMAGE:最大且不可着色

网格单元格是使用 CellBuilder 构建的。一个单元格最多可以支持两行文本和一个图像。单元格不能为空。

以下图像显示了网格示例

GridRowBuilder 示例 - 附近的餐馆

以下示例演示了一个包含图像和文本的网格行。

Kotlin

fun createSliceWithGridRow(sliceUri: Uri): Slice {
    // Create the parent builder.
    return list(context, sliceUri, ListBuilder.INFINITY) {
        header {
            title = "Famous restaurants"
            primaryAction = SliceAction.create(
                pendingIntent, icon, ListBuilder.ICON_IMAGE, "Famous restaurants"
            )
        }
        gridRow {
            cell {
                addImage(image1, LARGE_IMAGE)
                addTitleText("Top Restaurant")
                addText("0.3 mil")
                contentIntent = intent1
            }
            cell {
                addImage(image2, LARGE_IMAGE)
                addTitleText("Fast and Casual")
                addText("0.5 mil")
                contentIntent = intent2
            }
            cell {
                addImage(image3, LARGE_IMAGE)
                addTitleText("Casual Diner")
                addText("0.9 mi")
                contentIntent = intent3
            }
            cell {
                addImage(image4, LARGE_IMAGE)
                addTitleText("Ramen Spot")
                addText("1.2 mi")
                contentIntent = intent4
            }
        }
    }
}

Java

public Slice createSliceWithGridRow(Uri sliceUri) {
      if (getContext() == null) {
          return null;
      }
      // Create the parent builder.
      ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
              .setHeader(
                      // Create the header.
                      new HeaderBuilder()
                              .setTitle("Famous restaurants")
                              .setPrimaryAction(SliceAction
                                      .create(pendingIntent, icon, ListBuilder.ICON_IMAGE,
                                              "Famous restaurants"))
              )
              // Add a grid row to the list.
              .addGridRow(new GridRowBuilder()
                      // Add cells to the grid row.
                      .addCell(new CellBuilder()
                              .addImage(image1, ListBuilder.LARGE_IMAGE)
                              .addTitleText("Top Restaurant")
                              .addText("0.3 mil")
                              .setContentIntent(intent1)
                      ).addCell(new CellBuilder()
                              .addImage(image2, ListBuilder.LARGE_IMAGE)
                              .addTitleText("Fast and Casual")
                              .addText("0.5 mil")
                              .setContentIntent(intent2)
                      )
                      .addCell(new CellBuilder()
                              .addImage(image3, ListBuilder.LARGE_IMAGE)
                              .addTitleText("Casual Diner")
                              .addText("0.9 mi")
                              .setContentIntent(intent3))
                      .addCell(new CellBuilder()
                              .addImage(image4, ListBuilder.LARGE_IMAGE)
                              .addTitleText("Ramen Spot")
                              .addText("1.2 mi")
                              .setContentIntent(intent4))
                      // Every slice needs a primary action.
                      .setPrimaryAction(createActivityAction())
              );
      return listBuilder.build();
  }

RangeBuilder

使用 RangeBuilder,您可以创建一行内容,其中包含进度条或输入范围,例如滑块。

以下图像显示了进度条和滑块示例

RangeBuilder 示例 - 滑块

下面的示例演示了如何使用InputRangeBuilder构建包含音量滑块的Slice。要构建进度行,请使用addRange()

Kotlin

fun createSliceWithRange(sliceUri: Uri): Slice {
    return list(context, sliceUri, ListBuilder.INFINITY) {
        inputRange {
            title = "Ring Volume"
            inputAction = volumeChangedPendingIntent
            max = 100
            value = 30
        }
    }
}

Java

public Slice createSliceWithRange(Uri sliceUri) {
    if (getContext() == null) {
        return null;
    }
    // Construct the parent.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
            .addRow(new RowBuilder() // Every slice needs a row.
                    .setTitle("Enter app")
                      // Every slice needs a primary action.
                    .setPrimaryAction(createActivityAction())
            )
            .addInputRange(new InputRangeBuilder() // Create the input row.
                    .setTitle("Ring Volume")
                    .setInputAction(volumeChangedPendingIntent)
                    .setMax(100)
                    .setValue(30)
            );
    return listBuilder.build();
}

延迟内容

你应该尽快从SliceProvider.onBindSlice()返回Slice。耗时的调用可能会导致显示问题,例如闪烁和突然调整大小。

如果你的Slice内容无法快速加载,你可以在构建器中标注内容正在加载的同时,使用占位符内容构建你的Slice。内容准备好显示后,使用你的Slice URI调用getContentResolver().notifyChange(sliceUri, null)。这将导致再次调用SliceProvider.onBindSlice(),你可以在其中使用新内容再次构建Slice。

延迟内容示例 - 前往上班

在下面的“前往上班”行中,上班距离是动态确定的,可能无法立即获得。示例代码演示了在内容加载时使用空副标题作为占位符。

Kotlin

fun createSliceShowingLoading(sliceUri: Uri): Slice {
    // We’re waiting to load the time to work so indicate that on the slice by
    // setting the subtitle with the overloaded method and indicate true.
    return list(context, sliceUri, ListBuilder.INFINITY) {
        row {
            title = "Ride to work"
            setSubtitle(null, true)
            addEndItem(IconCompat.createWithResource(context, R.drawable.ic_work), ICON_IMAGE)
        }
    }
}

Java

public Slice createSliceShowingLoading(Uri sliceUri) {
    if (getContext() == null) {
        return null;
    }
    // Construct the parent.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
            // Construct the row.
            .addRow(new RowBuilder()
                    .setPrimaryAction(createActivityAction())
                    .setTitle("Ride to work")
                    // We’re waiting to load the time to work so indicate that on the slice by
                    // setting the subtitle with the overloaded method and indicate true.
                    .setSubtitle(null, true)
                    .addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_work),
                            ListBuilder.ICON_IMAGE)
            );
    return listBuilder.build();
}

private SliceAction createActivityAction() {
    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内的禁用滚动

呈现Slice模板的界面可能不支持在模板内滚动。在这种情况下,部分内容可能不会显示。

例如,考虑一个显示WiFi网络列表的Slice。

如果WiFi列表很长,并且禁用了滚动,你可以添加一个**查看更多**按钮,以确保用户可以查看列表中的所有项目。你可以使用addSeeMoreAction()添加此按钮,如下例所示。

Kotlin

fun seeMoreActionSlice(sliceUri: Uri) =
    list(context, sliceUri, ListBuilder.INFINITY) {
        // [START_EXCLUDE]
        // [END_EXCLUDE]
        setSeeMoreAction(seeAllNetworksPendingIntent)
        // [START_EXCLUDE]
        // [END_EXCLUDE]
    }

Java

public Slice seeMoreActionSlice(Uri sliceUri) {
    if (getContext() == null) {
        return null;
    }
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY);
    // [START_EXCLUDE]
    listBuilder.addRow(new RowBuilder()
            .setTitle("Hello")
            .setPrimaryAction(createActivityAction())
    );
    // [END_EXCLUDE]
    listBuilder.setSeeMoreAction(seeAllNetworksPendingIntent);
    // [START_EXCLUDE]
    // [END_EXCLUDE]
    return listBuilder.build();
}

显示效果如下所示:

点击**查看更多**将发送seeAllNetworksPendingIntent

或者,如果你想提供自定义消息或行,可以考虑添加RowBuilder。

Kotlin

fun seeMoreRowSlice(sliceUri: Uri) =
    list(context, sliceUri, ListBuilder.INFINITY) {
        // [START_EXCLUDE]
        // [END_EXCLUDE]
        seeMoreRow {
            title = "See all available networks"
            addEndItem(
                IconCompat.createWithResource(context, R.drawable.ic_right_caret), ICON_IMAGE
            )
            primaryAction = SliceAction.create(
                seeAllNetworksPendingIntent,
                IconCompat.createWithResource(context, R.drawable.ic_wifi),
                ListBuilder.ICON_IMAGE,
                "Wi-Fi Networks"
            )
        }
    }

Java

public Slice seeMoreRowSlice(Uri sliceUri) {
    if (getContext() == null) {
        return null;
    }
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
            // [START_EXCLUDE]
            .addRow(new RowBuilder()
                    .setTitle("Hello")
                    .setPrimaryAction(createActivityAction())
            )
            // [END_EXCLUDE]
            .setSeeMoreRow(new RowBuilder()
                    .setTitle("See all available networks")
                    .addEndItem(IconCompat
                                    .createWithResource(getContext(), R.drawable
                                            .ic_right_caret),
                            ListBuilder.ICON_IMAGE)
                    .setPrimaryAction(SliceAction.create(seeAllNetworksPendingIntent,
                            IconCompat.createWithResource(getContext(), R.drawable.ic_wifi),
                            ListBuilder.ICON_IMAGE,
                            "Wi-Fi Networks"))
            );
    // [START_EXCLUDE]
    // [END_EXCLUDE]
    return listBuilder.build();
}

仅当满足以下条件之一时,才会显示通过此方法添加的行或操作。

  • Slice的呈现器已禁用视图上的滚动。
  • 并非所有行都可以在可用空间中显示。

组合模板

你可以通过组合多种行类型来创建一个丰富动态的Slice。例如,一个Slice可以包含标题行、包含单个图像的网格以及包含两个文本单元格的网格。

这是一个包含标题行以及包含三个单元格的网格的Slice。