Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Kể từ Thẻ thông tin 1.2, bạn có thể truyền trực tuyến nội dung cập nhật dữ liệu nền tảng bằng cách sử dụng biểu thức động. Sau đó, bạn có thể liên kết các nội dung cập nhật này với ảnh động trong thẻ thông tin của mình. Ứng dụng của bạn sẽ nhận được nội dung cập nhật cho giá trị này mỗi giây.
Khi sử dụng biểu thức động, bạn không cần làm mới toàn bộ thẻ thông tin khi nội dung của thẻ thay đổi. Để đem lại trải nghiệm hấp dẫn hơn trong các thẻ thông tin của bạn, hãy tạo ảnh động cho các đối tượng động đó.
Liên kết biểu thức động với nguồn dữ liệu
Không gian tên androidx.wear.protolayout và androidx.wear.protolayout.material chứa nhiều lớp có các trường chấp nhận biểu thức động.
Sau đây là một số ví dụ:
Để dùng biểu thức động làm giá trị có thể có cho một phần tử trong thẻ thông tin của bạn, hãy sử dụng loại thuộc tính động *Prop tương ứng của phần tử đó và chuyển nguồn dữ liệu vào phương thức setDynamicValue() của lớp trình tạo của loại thuộc tính động.
Thẻ thông tin hỗ trợ các loại thuộc tính động sau đây:
Đối với các kích thước tuyến tính, được đo bằng pixel và không phụ thuộc vào màn hình, hãy sử dụng DimensionBuilders.DpProp.
Khi dùng một biểu thức động ảnh hưởng đến kích thước thực (bất kỳ giá trị nào trong thẻ thông tin ngoại trừ giá trị màu), bạn còn phải chỉ định một tập hợp các điều kiện ràng buộc có liên quan, chẳng hạn như định dạng chuỗi. Những điều kiện ràng buộc này cho phép trình kết xuất hệ thống xác định khoảng không gian tối đa mà một giá trị có thể chiếm trong thẻ thông tin của bạn. Thông thường, bạn chỉ định các điều kiện ràng buộc này ở cấp phần tử, chứ không phải ở cấp biểu thức động, bằng cách gọi một phương thức bắt đầu bằng setLayoutConstraintsForDynamic*.
Đoạn mã sau đây chỉ ra cách hiện nội dung cập nhật nhịp tim bằng 3 chữ số, với giá trị dự phòng là --:
Wear OS đặt ra giới hạn cho số lượng biểu thức mà một thẻ thông tin có thể có. Nếu một thẻ thông tin có tổng cộng quá nhiều biểu thức động, thì các giá trị động sẽ bị bỏ qua và hệ thống sẽ chuyển về các giá trị tĩnh mà bạn cung cấp cho các loại thuộc tính động tương ứng.
Hợp nhất dữ liệu động thành một đối tượng trạng thái
Bạn có thể hợp nhất tập hợp nội dung cập nhật mới nhất từ nguồn dữ liệu thành đối tượng trạng thái mà bạn sẽ chuyển vào thẻ thông tin của mình để hiển thị giá trị.
Để dùng thông tin trạng thái trong thẻ thông tin của bạn, hãy hoàn tất các bước sau:
Thiết lập một tập hợp khoá đại diện cho các giá trị khác nhau của trạng thái thẻ thông tin. Sau đây là ví dụ về cách tạo khoá cho lượng nước uống và ghi chú:
Trong quá trình triển khai onTileRequest(), hãy gọi setState() và thiết lập mối liên kết ban đầu từ mỗi khoá đến một giá trị dữ liệu động cụ thể:
Kotlin
overridefunonTileRequest(requestParams:TileRequest):ListenableFuture<Tile>{valstate=State.Builder().addKeyToValueMapping(KEY_WATER_INTAKE,DynamicDataBuilders.DynamicDataValue.fromInt(200)).addKeyToValueMapping(KEY_NOTE,DynamicDataBuilders.DynamicDataValue.fromString("Note about day")).build()// ...returnFutures.immediateFuture(Tile.Builder()// Set resources, timeline, and other tile properties..setState(state).build())
Java
@OverrideprotectedListenableFuture<Tile>onTileRequest(ListenableFuture<Tile>{Statestate=newState.Builder().addKeyToValueMapping(KEY_WATER_INTAKE,DynamicDataBuilders.DynamicDataValue.fromInt(200)).addKeyToValueMapping(KEY_NOTE,DynamicDataBuilders.DynamicDataValue.fromString("Note about day")).build();// ...returnFutures.immediateFuture(Tile.Builder()// Set resources, timeline, and other tile properties..setState(state).build());}
Khi bạn tạo bố cục, ở vị trí mà bạn muốn hiện dữ liệu này từ trạng thái, hãy sử dụng một đối tượng loại Dynamic*. Bạn cũng có thể gọi animate() để hiện một ảnh động từ giá trị trước đó đến giá trị hiện tại:
Kotlin
DynamicInt32.from(KEY_WATER_INTAKE).animate()
Java
DynamicInt32.from(KEY_WATER_INTAKE).animate();
Khi cần, bạn cũng có thể cập nhật trạng thái bằng các giá trị mới. Đây có thể là một phần của LoadAction của thẻ thông tin.
Trong ví dụ sau, giá trị lượng nước uống được cập nhật thành 400:
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-08-30 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-08-30 UTC."],[],[],null,["Starting in Tiles 1.2, you can stream platform data updates using\n[dynamic expressions](/training/wearables/data/dynamic). You can then associate these updates with animations\nin your tiles. Your app gets updates to this value every second.\n\nUsing dynamic expressions, you don't need to refresh the entire tile when its\ncontent changes. To create a more engaging experience in your tiles, animate\nthose dynamic objects.\n\nAssociate dynamic expressions with data sources\n\nThe `androidx.wear.protolayout` and `androidx.wear.protolayout.material`\nnamespaces contain many classes whose fields accept dynamic expressions.\nSeveral examples include the following:\n\n- Several length values, including the [length of an `Arc` object](/reference/androidx/wear/protolayout/LayoutElementBuilders.ArcLine#getLength()) and the [length of a `CircularProgressIndicator`](/reference/androidx/wear/protolayout/material/CircularProgressIndicator#getProgress()) object.\n- Any color, such as the [content color of a `Button` object](/reference/androidx/wear/protolayout/material/ButtonColors#getContentColor()).\n- Many string values, including the [content of a `Text` object](/reference/androidx/wear/protolayout/material/Text#getText()), the [content of a `LayoutElementsBuilders.Text` object](/reference/androidx/wear/protolayout/LayoutElementBuilders.Text#getText()), and the [content\n description of a `CircularProgressIndicator` object](/reference/androidx/wear/protolayout/material/CircularProgressIndicator#getContentDescription()).\n\nTo use a dynamic expression as a possible value for an element in your tile, use\nthe element's corresponding `*Prop` dynamic property type and pass in the data\nsource to the dynamic property type's builder class's `setDynamicValue()`\nmethod.\n\nTiles support these dynamic property types:\n\n- For linear dimensions, measured in display-independent pixels, use [`DimensionBuilders.DpProp`](/reference/androidx/wear/tiles/DimensionBuilders.DpProp).\n- For angular dimensions, measured in degrees, use [`DimensionBuilders.DegreesProp`](/reference/androidx/wear/tiles/DimensionBuilders.DegreesProp).\n- For string values, use [`TypeBuilders.StringProp`](/reference/androidx/wear/protolayout/TypeBuilders.StringProp).\n- For color values, use [`ColorBuilders.ColorProp`](/reference/androidx/wear/protolayout/ColorBuilders.ColorProp).\n- For floating-point values, use [`TypeBuilders.FloatProp`](/reference/androidx/wear/protolayout/TypeBuilders.FloatProp).\n\nWhen you use a dynamic expression that affects physical dimensions---any value in\na tile except for color---you must also specify a set of related constraints, such\nas a string format. These constraints allow the system renderer to determine the\nmaximum amount of space that a value could occupy within your tile. Usually, you\nspecify these constraints at the element level, not at the dynamic expression\nlevel, by calling a method that starts with `setLayoutConstraintsForDynamic*`.\n| **Note:** The Material components set these layout constraints automatically.\n\nThe following code snippet shows how to display updates to a heart rate using 3\ndigits, with a fallback value of `--`: \n\n```kotlin\noverride fun onTileRequest(requestParams: RequestBuilders.TileRequest) =\n Futures.immediateFuture(\n Tile.Builder()\n .setResourcesVersion(RESOURCES_VERSION)\n .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes\n .setTileTimeline(\n Timeline.fromLayoutElement(\n Text.Builder(\n this,\n TypeBuilders.StringProp.Builder(\"--\")\n .setDynamicValue(\n PlatformHealthSources.heartRateBpm()\n .format()\n .concat(DynamicBuilders.DynamicString.constant(\" bpm\"))\n )\n .build(),\n TypeBuilders.StringLayoutConstraint.Builder(\"000\").build(),\n )\n .build()\n )\n )\n .build()\n )https://github.com/android/snippets/blob/f95ab59fad80aeaf5d6a90bab8a01a126f20f44e/wear/src/main/java/com/example/wear/snippets/tile/Tile.kt#L178-L200\n```\n\nUse a small number of expressions within a single tile\n\nWear OS [places a limit](/training/wearables/data/dynamic#use-limited-number-per-screen) on the number of expressions that a single tile can\nhave. If a tile contains too many total dynamic expressions, dynamic values are\nignored, and the system falls back to the static values that you provide to the\nrespective dynamic property types.\n\nConsolidate dynamic data into a state object\n\nYou can consolidate the latest set of updates from data sources into a *state*,\nwhich you pass over to your tile for value rendering.\n\nTo use state information in your tiles, complete these steps:\n\n1. Establish a set of keys that represent the different values of your tile's\n state. This example creates keys for water intake and a note:\n\n Kotlin \n\n ```kotlin\n companion object {\n val KEY_WATER_INTAKE = AppDataKey\u003cDynamicInt32\u003e(\"water_intake\")\n val KEY_NOTE = AppDataKey\u003cDynamicString\u003e(\"note\")\n }\n ```\n\n Java \n\n ```java\n private static final AppDataKey\u003cDynamicInt32\u003e KEY_WATER_INTAKE =\n new AppDataKey\u003cDynamicInt32\u003e(\"water_intake\");\n private static final AppDataKey\u003cDynamicString\u003e KEY_NOTE =\n new AppDataKey\u003cDynamicString\u003e(\"note\");\n ```\n2. In your implementation of `onTileRequest()`, call `setState()` and establish\n initial mappings from each key to a particular dynamic data value:\n\n Kotlin \n\n ```kotlin\n override fun onTileRequest(requestParams: TileRequest):\n ListenableFuture\u003cTile\u003e {\n val state = State.Builder()\n .addKeyToValueMapping(KEY_WATER_INTAKE,\n DynamicDataBuilders.DynamicDataValue.fromInt(200))\n .addKeyToValueMapping(KEY_NOTE,\n DynamicDataBuilders.DynamicDataValue.fromString(\"Note about day\"))\n .build()\n // ...\n\n return Futures.immediateFuture(Tile.Builder()\n // Set resources, timeline, and other tile properties.\n .setState(state)\n .build()\n )\n ```\n\n Java \n\n ```java\n @Override\n protected ListenableFuture\u003cTile\u003e onTileRequest(\n ListenableFuture\u003cTile\u003e {\n State state = new State.Builder()\n .addKeyToValueMapping(KEY_WATER_INTAKE,\n DynamicDataBuilders.DynamicDataValue.fromInt(200))\n .addKeyToValueMapping(KEY_NOTE,\n DynamicDataBuilders.DynamicDataValue.fromString(\"Note about day\"))\n .build();\n // ...\n\n return Futures.immediateFuture(Tile.Builder()\n // Set resources, timeline, and other tile properties.\n .setState(state)\n .build()\n );\n }\n ```\n3. When you create your layout, in a place where you want to show this data\n from state, use a `Dynamic*` type object. You can also call `animate()` to\n show an animation from the previous value to the current value:\n\n Kotlin \n\n ```kotlin\n DynamicInt32.from(KEY_WATER_INTAKE).animate()\n ```\n\n Java \n\n ```java\n DynamicInt32.from(KEY_WATER_INTAKE).animate();\n ```\n4. When needed, you can also update the state with new values. This can be\n part of a tile's [`LoadAction`](/reference/androidx/wear/protolayout/ActionBuilders.LoadAction).\n\n In this example, the water intake value is updated to `400`: \n\n Kotlin \n\n ```kotlin\n val loadAction = LoadAction.Builder()\n .setRequestState(\n State.Builder()\n .addKeyToValueMapping(\n KEY_WATER_INTAKE,\n DynamicDataBuilders.DynamicDataValue.fromInt(400)\n )\n .build()\n )\n .build()\n ```\n\n Java \n\n ```java\n LoadAction loadAction = new LoadAction.Builder()\n .setRequestState(\n new State.Builder()\n .addKeyToValueMapping(\n KEY_WATER_INTAKE,\n DynamicDataBuilders.DynamicDataValue.fromInt(400)\n ).build()\n ).build();\n ```\n\nRecommended for you\n\n- Note: link text is displayed when JavaScript is off\n- [Migrate to ProtoLayout namespaces](/training/wearables/tiles/migrate-to-protolayout)\n- [Get started with tiles](/training/wearables/tiles/get_started)\n- [Other considerations](/develop/ui/compose/migrate/other-considerations)"]]