Để bắt đầu cung cấp thẻ thông tin từ ứng dụng, hãy đưa các phần phụ thuộc sau đây vào tệp build.gradle
của ứng dụng.
Groovy
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" }
Kotlin
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") }
Tạo thẻ thông tin
Để cung cấp thẻ thông tin từ ứng dụng, hãy tạo một class (lớp) giúp mở rộng TileService
và triển khai các phương thức, như trong mã mẫu sau:
Kotlin
// 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() ) }
Java
// 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() ); } }
Tiếp theo, hãy thêm một dịch vụ bên trong thẻ <application>
của tệp 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>
Bộ lọc quyền và ý định sẽ đăng ký dịch vụ này làm trình cung cấp thẻ thông tin.
Người dùng sẽ thấy biểu tượng, nhãn và nội dung mô tả khi định cấu hình thẻ thông tin trên điện thoại hoặc đồng hồ.
Hãy dùng thẻ siêu dữ liệu xem trước để hiện bản xem trước của thẻ thông tin khi bạn định cấu hình thẻ thông tin trên điện thoại.
Tổng quan về vòng đời của dịch vụ thẻ thông tin
Sau khi tạo và khai báo TileService
trong tệp kê khai ứng dụng, bạn có thể phản hồi các thay đổi về trạng thái của dịch vụ thẻ thông tin.
TileService
là một dịch vụ ràng buộc. TileService
của bạn bị liên kết do yêu cầu của ứng dụng hoặc trong trường hợp hệ thống cần giao tiếp với ứng dụng đó. Vòng đời dịch vụ ràng buộc thông thường chứa bốn phương thức gọi lại sau: onCreate()
, onBind()
, onUnbind()
và onDestroy()
. Hệ thống sẽ gọi các phương thức này mỗi khi dịch vụ bước vào giai đoạn vòng đời mới.
Ngoài các lệnh gọi lại kiểm soát vòng đời dịch vụ ràng buộc, bạn có thể triển khai các phương thức khác dành riêng cho vòng đời TileService
. Tất cả các dịch vụ thẻ thông tin phải triển khai onTileRequest()
và onTileResourcesRequest()
để phản hồi các yêu cầu cập nhật của hệ thống.
onTileAddEvent()
: Hệ thống chỉ gọi phương thức này khi người dùng thêm thẻ thông tin của bạn lần đầu tiên, và nếu người dùng xoá rồi thêm lại thẻ thông tin đó. Đây là thời điểm tốt nhất để thực hiện mọi hoạt động khởi chạy một lần.onTileAddEvent()
chỉ được gọi khi tập hợp thẻ thông tin được định cấu hình lại, không phải khi nào một thẻ thông tin được hệ thống tạo. Ví dụ: khi thiết bị khởi động lại hoặc bật nguồn,onTileAddEvent()
không được gọi cho các thẻ thông tin đã được thêm vào. Thay vào đó, bạn có thể sử dụnggetActiveTilesAsync()
để xem thông tin tổng quan nhanh về những thẻ thông tin thuộc bạn đang hoạt động.onTileRemoveEvent()
: Hệ thống chỉ gọi phương thức này nếu người dùng xoá thẻ thông tin của bạn.onTileEnterEvent()
: Hệ thống gọi phương thức này khi một thẻ thông tin do trình cung cấp này cung cấp xuất hiện trên màn hình.onTileLeaveEvent()
: Hệ thống gọi phương thức này khi một thẻ thông tin do trình cung cấp này cung cấp nằm ngoài khung hiển thị trên màn hình.onTileRequest()
: Hệ thống gọi phương thức này khi hệ thống yêu cầu một tiến trình mới từ trình cung cấp này.onTileResourcesRequest()
: Hệ thống gọi phương thức này khi yêu cầu nhà cung cấp này cung cấp một gói tài nguyên. Điều này có thể xảy ra vào lần đầu tiên một Thẻ thông tin được tải hoặc bất cứ khi nào phiên bản tài nguyên thay đổi.
Truy vấn thẻ thông tin đang hoạt động
Thẻ thông tin đang hoạt động là các thẻ thông tin đã được thêm để hiển thị trên đồng hồ. Sử dụng phương thức tĩnh của TileService
getActiveTilesAsync()
để truy vấn thẻ thông tin thuộc ứng dụng của bạn đang hoạt động.
Tạo giao diện người dùng cho thẻ thông tin
Bố cục của thẻ thông tin được viết bằng mẫu trình tạo. Bố cục của thẻ thông tin được thiết kế giống một cây bao gồm các vùng chứa bố cục và các phần tử của bố cục cơ bản. Mỗi phần tử của bố cục có các thuộc tính mà bạn có thể thiết lập thông qua nhiều phương thức setter.
Phần tử của bố cục cơ bản
Dưới đây là các phần tử hình ảnh từ thư viện protolayout
được hỗ trợ, cùng với các thành phần Material:
Text
: hiển thị một chuỗi văn bản (không bắt buộc phải gói văn bản đó).Image
: hiển thị một hình ảnh.Spacer
: cung cấp khoảng đệm giữa các phần tử hoặc có thể hoạt động như một đường phân chia khi bạn đặt màu nền.
Thành phần Material
Ngoài các phần tử cơ bản, thư viện protolayout-material
còn cung cấp các thành phần đảm bảo thiết kế thẻ thông tin phù hợp với các đề xuất về giao diện người dùng của Material Design.
Button
: thành phần hình tròn có thể nhấp vào, được thiết kế để chứa một biểu tượng.Chip
: thành phần có hình dạng sân vận động có thể nhấp vào, được thiết kế để chứa tối đa 2 dòng văn bản và một biểu tượng không bắt buộc.CompactChip
: thành phần có hình dạng sân vận động có thể nhấp vào, được thiết kế để chứa một dòng văn bản.TitleChip
: thành phần có hình dạng sân vận động có thể nhấp vào, tương tự nhưChip
nhưng có chiều cao lớn hơn để chứa đủ văn bản tiêu đề.CircularProgressIndicator
: chỉ báo tiến trình hình tròn có thể đặt vào bên trongEdgeContentLayout
để hiển thị tiến trình xung quanh các cạnh của màn hình.
Vùng chứa bố cục
Các vùng chứa sau đây được hỗ trợ, cùng với Bố cục Material:
Row
: sắp xếp lần lượt từng phần tử con theo chiều ngang.Column
: sắp xếp lần lượt từng phần tử con theo chiều dọc.Box
: xếp chồng các phần tử con lên nhau.Arc
: sắp xếp các phần tử con trong một hình tròn.Spannable
: áp dụngFontStyles
cụ thể cho các phần văn bản cùng với văn bản và hình ảnh xen kẽ. Để biết thêm thông tin, hãy xem phần Spannable.
Mỗi vùng chứa có thể chứa một hoặc nhiều phần tử con, mà chính các phần tử đó cũng có thể là vùng chứa. Ví dụ: Column
có thể chứa nhiều phần tử Row
dưới dạng phần tử con, từ đó tạo ra một bố cục giống như lưới.
Ví dụ: thẻ thông tin có 1 bố cục vùng chứa và 2 phần tử bố cục con có thể có dạng như sau:
Kotlin
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()
Java
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(); }
Bố cục Material
Ngoài bố cục cơ bản, thư viện protolayout-material
còn cung cấp một số bố cục ổn định để giữ những phần tử trong các "khung" cụ thể.
PrimaryLayout
: đặt một hành động chínhCompactChip
ở dưới cùng với nội dung nằm ở giữa phía trên.MultiSlotLayout
: đặt nhãn chính và nhãn phụ có nội dung tuỳ ý nằm ở giữa và mộtCompactChip
không bắt buộc ở dưới cùng.MultiButtonLayout
: đặt một nhóm các nút được sắp xếp theo nguyên tắc Material.EdgeContentLayout
: đặt nội dung xung quanh cạnh màn hình, chẳng hạn nhưCircularProgressIndicator
. Khi dùng bố cục này, nội dung trong bố cục sẽ tự động có lề và khoảng đệm thích hợp.
Vòng cung
Dưới đây là các vùng chứa con Arc
được hỗ trợ:
ArcLine
: hiển thị một đường cong xung quanh Vòng cung.ArcText
: hiển thị văn bản cong trong Vòng cung.ArcAdapter
: hiển thị một phần tử bố cục cơ bản trong vòng cung, được vẽ tại tiếp tuyến của vòng cung.
Để biết thêm thông tin, hãy xem tài liệu tham khảo cho từng loại phần tử.
Đối tượng sửa đổi
Bạn có thể tuỳ ý áp dụng các công cụ sửa đổi cho mỗi phần tử bố cục có sẵn. Hãy sử dụng các công cụ sửa đổi này cho những mục đích sau:
- Thay đổi giao diện hình ảnh của bố cục. Ví dụ: thêm nền, đường viền hoặc khoảng đệm vào phần tử bố cục.
- Thêm siêu dữ liệu về bố cục. Ví dụ: thêm một đối tượng sửa đổi ngữ nghĩa vào phần tử bố cục để sử dụng với trình đọc màn hình.
- Thêm chức năng. Ví dụ: thêm vào phần tử bố cục một đối tượng sửa đổi có thể nhấp để thẻ thông tin có khả năng tương tác. Để biết thêm thông tin, hãy xem bài viết Tương tác với thẻ thông tin.
Ví dụ: chúng ta có thể tuỳ chỉnh giao diện và siêu dữ liệu mặc định của Image
, như trong mã mẫu sau:
Kotlin
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()
Java
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
Spannable
là loại vùng chứa đặc biệt có nhiệm vụ sắp xếp các phần tử tương tự như văn bản. Vùng chứa này hữu ích khi bạn muốn áp dụng kiểu khác cho duy nhất một chuỗi con trong khối văn bản lớn hơn – đây là điều không thể áp dụng với phần tử Text
.
Vùng chứa Spannable
chứa phần tử con Span
. Bạn không được phép dùng các phần tử con khác hoặc các thực thể Spannable
lồng nhau.
Có hai loại phần tử con Span
:
Ví dụ: bạn có thể in nghiêng từ "world" (mọi người) trong thẻ thông tin "Hello world" (Chào mọi người) và chèn một hình ảnh giữa các từ, như trong mã mẫu sau đây:
Kotlin
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()
Java
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(); }
Làm việc với các tài nguyên
Ô không có quyền truy cập vào bất kỳ tài nguyên nào trong ứng dụng của bạn. Điều này có nghĩa là bạn không thể chuyển mã nhận dạng hình ảnh Android vào phần tử bố cục Image
và kỳ vọng nó sẽ thực hiện phân giải. Thay vào đó, hãy ghi đè phương thức onTileResourcesRequest()
và cung cấp mọi tài nguyên theo cách thủ công.
Có 2 cách để cung cấp hình ảnh trong phương thức onTileResourcesRequest()
:
- Cung cấp tài nguyên có thể vẽ bằng cách dùng
setAndroidResourceByResId()
. - Cung cấp hình ảnh linh hoạt dưới dạng
ByteArray
bằng cách dùngsetInlineResource()
.
Kotlin
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() )
Java
@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() ); }
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Di chuyển sang không gian tên ProtoLayout
ConstraintLayout
trong Compose- Tạo ô tuỳ chỉnh trong phần Cài đặt nhanh cho ứng dụng