Начало работы с плиткой

Чтобы начать предоставлять плитки из вашего приложения, включите следующие зависимости в файл build.gradle вашего приложения.

классный

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.4.0"

    // Use to utilize standard components and layouts in your tiles
    implementation "androidx.wear.protolayout:protolayout:1.2.0"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.protolayout:protolayout-material:1.2.0"

    // Use to include dynamic expressions in your tiles
    implementation "androidx.wear.protolayout:protolayout-expression:1.2.0"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.4.0"

    // Use to fetch tiles from a tile provider in your tests
    testImplementation "androidx.wear.tiles:tiles-testing:1.4.0"
}

Котлин

dependencies {
    // Use to implement support for wear tiles
    implementation("androidx.wear.tiles:tiles:1.4.0")

    // Use to utilize standard components and layouts in your tiles
    implementation("androidx.wear.protolayout:protolayout:1.2.0")

    // Use to utilize components and layouts with Material Design in your tiles
    implementation("androidx.wear.protolayout:protolayout-material:1.2.0")

    // Use to include dynamic expressions in your tiles
    implementation("androidx.wear.protolayout:protolayout-expression:1.2.0")

    // Use to preview wear tiles in your own app
    debugImplementation("androidx.wear.tiles:tiles-renderer:1.4.0")

    // Use to fetch tiles from a tile provider in your tests
    testImplementation("androidx.wear.tiles:tiles-testing:1.4.0")
}

Создать плитку

Чтобы предоставить плитку из вашего приложения, создайте класс, расширяющий TileService , и реализуйте методы, как показано в следующем примере кода:

Котлин

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline
import androidx.wear.protolayout.material.Text
import androidx.wear.tiles.TileBuilders.Tile

private val RESOURCES_VERSION = "1"
class MyTileService : TileService() {
    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(argb(0xFF000000.toInt()))
                        .build()))
            .build())

    override fun onTileResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(Resources.Builder()
            .setVersion(RESOURCES_VERSION)
            .build()
        )
}

Ява

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline;
import androidx.wear.protolayout.material.Text;
import androidx.wear.tiles.TileBuilders.Tile;

public class MyTileService extends TileService {
    private static final String RESOURCES_VERSION = "1";

    @NonNull
    @Override
    protected ListenableFuture<Tile> onTileRequest(
        @NonNull TileRequest requestParams
    ) {
        return Futures.immediateFuture(new Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    new Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(ColorBuilders.argb(0xFF000000))
                        .build()))
            .build()
        );
   }

   @NonNull
   @Override
   protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
   ) {
       return Futures.immediateFuture(new Resources.Builder()
               .setVersion(RESOURCES_VERSION)
               .build()
       );
   }
}

Затем добавьте службу в тег <application> вашего файла AndroidManifest.xml .

<service
   android:name=".MyTileService"
   android:label="@string/tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/tile_icon_round"
   android:roundIcon="@drawable/tile_icon_round"
   android:exported="true"
   android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
   <intent-filter>
       <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
   </intent-filter>

   <meta-data android:name="androidx.wear.tiles.PREVIEW"
       android:resource="@drawable/tile_preview" />
</service>

Фильтр разрешений и намерений регистрирует эту службу в качестве поставщика плиток.

Значок, метка и описание отображаются пользователю при настройке плиток на телефоне или часах.

Используйте тег метаданных предварительного просмотра, чтобы отобразить предварительный просмотр плитки при ее настройке на телефоне.

Обзор жизненного цикла службы плиток

После того как вы создали и объявили свой TileService в манифесте приложения, вы можете реагировать на изменения состояния сервиса плиток.

TileService — это привязанный сервис . Ваш TileService привязывается в результате запроса вашего приложения или если системе необходимо взаимодействовать с ним. Типичный жизненный цикл связанного сервиса содержит следующие четыре метода обратного вызова: onCreate() , onBind() , onUnbind() и onDestroy() . Система вызывает эти методы каждый раз, когда служба переходит в новую фазу жизненного цикла.

Помимо обратных вызовов, управляющих жизненным циклом привязанного сервиса, вы можете реализовать другие методы, специфичные для жизненного цикла TileService . Все службы плиток должны реализовывать onTileRequest() и onTileResourcesRequest() чтобы отвечать на запросы обновлений от системы.

  • onTileAddEvent() : система вызывает этот метод только тогда, когда пользователь добавляет плитку в первый раз, а также если пользователь удаляет и добавляет плитку снова. Это лучшее время для однократной инициализации.

    onTileAddEvent() вызывается только при перенастройке набора плиток, а не всякий раз, когда плитка создается системой. Например, когда устройство перезагружается или включается, onTileAddEvent() не вызывается для уже добавленных плиток. Вместо этого вы можете использовать getActiveTilesAsync() чтобы получить снимок того, какие принадлежащие вам плитки активны.

  • onTileRemoveEvent() : система вызывает этот метод, только если пользователь удаляет вашу плитку.

  • onTileEnterEvent() : система вызывает этот метод, когда плитка, предоставленная этим провайдером, появляется на экране.

  • onTileLeaveEvent() : система вызывает этот метод, когда плитка, предоставленная этим провайдером, выходит из поля зрения на экране.

  • onTileRequest() : система вызывает этот метод, когда система запрашивает новую временную шкалу у этого провайдера.

  • onTileResourcesRequest() : система вызывает этот метод, когда система запрашивает пакет ресурсов у этого поставщика. Это может произойти при первой загрузке плитки или при изменении версии ресурса.

Запросить, какие плитки активны

Активные плитки — это плитки, добавленные для отображения на часах. Используйте статический метод TileService getActiveTilesAsync() чтобы узнать, какие плитки , принадлежащие вашему приложению, активны.

Создать пользовательский интерфейс для плиток

Компоновка плитки записывается с использованием шаблона строителя. Макет плитки построен как дерево, состоящее из контейнеров макета и основных элементов макета. Каждый элемент макета имеет свойства, которые вы можете установить с помощью различных методов установки.

Основные элементы макета

Вместе с компонентами Material поддерживаются следующие визуальные элементы из библиотеки protolayout :

  • Text : отображает строку текста, при необходимости перенося ее.
  • Image : отображает изображение.
  • Spacer : обеспечивает отступы между элементами или может выступать в качестве разделителя, когда вы устанавливаете цвет фона.

Компоненты материала

В дополнение к базовым элементам библиотека protolayout-material предоставляет компоненты, которые обеспечивают дизайн плитки в соответствии с рекомендациями пользовательского интерфейса Material Design.

  • Button : кликабельный круглый компонент, содержащий значок.
  • Chip : кликабельный компонент в форме стадиона, содержащий до двух строк текста и дополнительный значок.

  • CompactChip : кликабельный компонент в форме стадиона, предназначенный для хранения строки текста.

  • TitleChip : кликабельный компонент в форме стадиона, похожий на Chip , но с большей высотой для размещения текста заголовка.

  • CircularProgressIndicator : круговой индикатор прогресса, который можно разместить внутри EdgeContentLayout для отображения прогресса по краям экрана.

Контейнеры макета

Наряду с макетами материалов поддерживаются следующие контейнеры:

  • Row : размещает дочерние элементы горизонтально, один за другим.
  • Column : размещает дочерние элементы вертикально, один за другим.
  • Box : накладывает дочерние элементы друг на друга.
  • Arc : размещает дочерние элементы по кругу.
  • Spannable : применяет определенные FontStyles к разделам текста вместе с чередованием текста и изображений. Для получения дополнительной информации см. Spannables .

Каждый контейнер может содержать одного или нескольких дочерних элементов, которые сами по себе также могут быть контейнерами. Например, Column может содержать несколько дочерних элементов Row , что приводит к созданию макета, напоминающего сетку.

Например, плитка с макетом-контейнером и двумя дочерними элементами макета может выглядеть так:

Котлин

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build()

Ява

private LayoutElement myLayout() {
    return new Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(new Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(new Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build();
}

Макеты материалов

В дополнение к базовым макетам библиотека protolayout-material предоставляет несколько самостоятельных макетов, предназначенных для размещения элементов в определенных «слотах».

  • PrimaryLayout : размещает одно основное действие CompactChip внизу, а содержимое центрирует над ним.

  • MultiSlotLayout : позиционирует первичные и вторичные метки с необязательным содержимым между ними и дополнительным CompactChip внизу.

  • MultiButtonLayout : позиционирует набор кнопок, расположенных в соответствии с рекомендациями по материалам.

  • EdgeContentLayout : позиционирует содержимое по краю экрана, например CircularProgressIndicator . При использовании этого макета к содержимому внутри него автоматически применяются соответствующие поля и отступы.

Дуги

Поддерживаются следующие дочерние элементы контейнера Arc :

  • ArcLine : отображает изогнутую линию вокруг дуги.
  • ArcText : отображает изогнутый текст в дуге.
  • ArcAdapter : отображает базовый элемент макета в дуге, нарисованной по касательной к дуге.

Дополнительную информацию см. в справочной документации для каждого типа элементов.

Модификаторы

К каждому доступному элементу макета при желании можно применить модификаторы. Используйте эти модификаторы для следующих целей:

  • Измените внешний вид макета. Например, добавьте фон, рамку или отступ к элементу макета.
  • Добавьте метаданные о макете. Например, добавьте модификатор семантики к элементу макета для использования с программами чтения с экрана.
  • Добавьте функциональность. Например, добавьте кликабельный модификатор к элементу макета, чтобы сделать плитку интерактивной. Дополнительные сведения см. в разделе Взаимодействие с плитками .

Например, мы можем настроить внешний вид и метаданные Image по умолчанию, как показано в следующем примере кода:

Котлин

private fun myImage(): LayoutElement =
    Image.Builder()
        .setWidth(dp(24f))
        .setHeight(dp(24f))
        .setResourceId("image_id")
        .setModifiers(Modifiers.Builder()
            .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build())
            .setPadding(Padding.Builder().setStart(dp(12f)).build())
            .setSemantics(Semantics.builder()
                .setContentDescription("Image description")
                .build()
            ).build()
        ).build()

Ява

private LayoutElement myImage() {
   return new Image.Builder()
           .setWidth(dp(24f))
           .setHeight(dp(24f))
           .setResourceId("image_id")
           .setModifiers(new Modifiers.Builder()
                   .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build())
                   .setPadding(new Padding.Builder().setStart(dp(12f)).build())
                   .setSemantics(new Semantics.Builder()
                           .setContentDescription("Image description")
                           .build()
                   ).build()
           ).build();
}

Спаннаблес

Spannable — это особый тип контейнера, в котором элементы размещаются аналогично тексту. Это полезно, когда вы хотите применить другой стиль только к одной подстроке в большом блоке текста, что невозможно с элементом Text .

Контейнер Spannable заполняется дочерними элементами Span . Другие дочерние элементы или вложенные экземпляры Spannable не допускаются.

Есть два типа детей Span :

  • SpanText : отображает текст в определенном стиле.
  • SpanImage : отображает изображение вместе с текстом.

Например, вы можете выделить слово «мир» курсивом на плитке «Привет, мир» и вставить изображение между словами, как показано в следующем примере кода:

Котлин

private fun mySpannable(): LayoutElement =
    Spannable.Builder()
        .addSpan(SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(SpanText.Builder()
            .setText("world")
            .setFontStyle(FontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build()

Ява

private LayoutElement mySpannable() {
   return new Spannable.Builder()
        .addSpan(new SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(new SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(new SpanText.Builder()
            .setText("world")
            .setFontStyle(newFontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build();
}

Работа с ресурсами

Плитки не имеют доступа ни к каким ресурсам вашего приложения. Это означает, что вы не можете передать идентификатор изображения Android элементу макета Image и ожидать его разрешения. Вместо этого переопределите метод onTileResourcesRequest() и предоставьте все ресурсы вручную.

Существует два способа предоставления изображений в методе onTileResourcesRequest() :

  • Предоставьте доступный для рисования ресурс, используя setAndroidResourceByResId() .
  • Предоставьте динамическое изображение в виде ByteArray , используя setInlineResource() .

Котлин

override fun onTileResourcesRequest(
    requestParams: ResourcesRequest
) = Futures.immediateFuture(
Resources.Builder()
    .setVersion("1")
    .addIdToImageMapping("image_from_resource", ImageResource.Builder()
        .setAndroidResourceByResId(AndroidImageResourceByResId.Builder()
            .setResourceId(R.drawable.image_id)
            .build()
        ).build()
    )
    .addIdToImageMapping("image_inline", ImageResource.Builder()
        .setInlineResource(InlineImageResource.Builder()
            .setData(imageAsByteArray)
            .setWidthPx(48)
            .setHeightPx(48)
            .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
            .build()
        ).build()
    ).build()
)

Ява

@Override
protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
) {
return Futures.immediateFuture(
    new Resources.Builder()
        .setVersion("1")
        .addIdToImageMapping("image_from_resource", new ImageResource.Builder()
            .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.image_id)
                .build()
            ).build()
        )
        .addIdToImageMapping("image_inline", new ImageResource.Builder()
            .setInlineResource(new InlineImageResource.Builder()
                .setData(imageAsByteArray)
                .setWidthPx(48)
                .setHeightPx(48)
                .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
                .build()
            ).build()
        ).build()
);
}
{% дословно %} {% дословно %} {% дословно %} {% дословно %}