1. Trước khi bắt đầu
Ngay từ đầu, Jetpack Compose đã được thiết kế để có khả năng tương tác với Khung hiển thị, tức là Compose và hệ thống Khung hiển thị có thể chia sẻ các tài nguyên và hoạt động bổ trợ cho nhau để hiển thị giao diện người dùng. Chức năng này cho phép bạn thêm Compose vào ứng dụng hiện có dựa trên Khung hiển thị. Tức là, Compose và Khung hiển thị có thể cùng tồn tại trong cơ sở mã của bạn cho đến khi toàn bộ ứng dụng hoàn toàn nằm trong Compose.
Trong lớp học lập trình này, bạn sẽ chuyển mục danh sách dựa trên khung hiển thị trong ứng dụng Juice Tracker (Theo dõi nước ép) sang Compose. Bạn có thể tự chuyển đổi các khung hiển thị còn lại của Juice Tracker nếu muốn.
Nếu sở hữu một ứng dụng có giao diện người dùng dựa trên Khung hiển thị, có thể bạn sẽ không muốn viết lại toàn bộ giao diện người dùng cho ứng dụng đó cùng một lúc. Lớp học lập trình này giúp bạn chuyển đổi một khung hiển thị trong giao diện người dùng dựa trên khung hiển thị thành một thành phần Compose.
Điều kiện tiên quyết
- Đã làm quen với giao diện người dùng dựa trên Khung hiển thị.
- Nắm được cách tạo ứng dụng bằng giao diện người dùng dựa trên Khung hiển thị.
- Có kinh nghiệm về cú pháp Kotlin, bao gồm cả hàm lambda.
- Nắm được cách tạo ứng dụng trong Jetpack Compose.
Kiến thức bạn sẽ học được
- Cách thêm Compose vào màn hình hiện có được tạo bằng khung hiển thị Android.
- Cách xem trước một Thành phần kết hợp được thêm vào ứng dụng dựa trên Khung hiển thị.
Sản phẩm bạn sẽ tạo ra
- Bạn sẽ chuyển đổi một mục danh sách dựa trên Khung hiển thị sang Compose trong ứng dụng Juice Tracker.
2. Tổng quan về ứng dụng khởi đầu
Lớp học lập trình này sử dụng mã giải pháp của ứng dụng Juice Tracker trong học phần Tạo ứng dụng Android bằng khung hiển thị làm mã khởi đầu. Ứng dụng khởi đầu lưu dữ liệu bằng cách sử dụng thư viện dữ liệu cố định Room. Người dùng có thể thêm thông tin về loại nước ép vào cơ sở dữ liệu của ứng dụng, chẳng hạn như tên nước ép, nội dung mô tả, màu sắc và điểm xếp hạng.
Trong lớp học lập trình này, bạn sẽ chuyển đổi mục danh sách dựa trên khung hiển thị sang Compose.
Tải mã khởi đầu cho lớp học lập trình này
Để bắt đầu, hãy tải đoạn mã khởi đầu xuống:
Ngoài ra, bạn có thể sao chép kho lưu trữ GitHub cho mã:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views
Bạn có thể duyệt xem mã này trong Kho lưu trữ GitHub JuiceTracker
.
3. Thêm thư viện Jetpack Compose
Recollet, Compose và các Khung hiển thị có thể cùng tồn tại trên một màn hình có sẵn; bạn có thể có một số phần tử trên giao diện người dùng trong Compose và một số phần tử khác trong hệ thống Khung hiển thị. Ví dụ: bạn có thể chỉ có danh sách trong Compose, trong khi phần còn lại của màn hình nằm trong hệ thống Khung hiển thị.
Hãy hoàn tất các bước sau để thêm thư viện Compose vào ứng dụng Juice Tracker.
- Mở Juice Tracker trong Android Studio.
- Mở
build.gradle.kts
cấp ứng dụng. - Bên trong khối
buildFeatures
, hãy thêm một cờcompose = true
.
buildFeatures {
//...
// Enable Jetpack Compose for this module
compose = true
}
Cờ này cho phép Android Studio hoạt động với Compose. Bạn chưa thực hiện bước này trong các lớp học lập trình trước đây vì Android Studio sẽ tự động tạo mã này khi bạn tạo một dự án mẫu mới dùng Compose trên Android Studio.
- Bên dưới
buildFeatures
, hãy thêm khốicomposeOptions
. - Bên trong khối, hãy đặt
kotlinCompilerExtensionVersion
thành"1.5.1"
để thiết lập phiên bản trình biên dịch Kotlin.
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
- Trong phần
dependencies
, hãy thêm phần phụ thuộc Compose. Bạn cần có các phần phụ thuộc sau để thêm Compose vào ứng dụng dựa trên Khung hiển thị. Các phần phụ thuộc này giúp tích hợp Compose vào Activity (Hoạt động), thêm thư viện thành phần thiết kế Compose, hỗ trợ giao diện Compose Jetpack và cung cấp các công cụ để hỗ trợ IDE tốt hơn.
dependencies {
implementation(platform("androidx.compose:compose-bom:2023.06.01"))
// other dependencies
// Compose
implementation("androidx.activity:activity-compose:1.7.2")
implementation("androidx.compose.material3:material3")
implementation("com.google.accompanist:accompanist-themeadapter-material3:0.28.0")
debugImplementation("androidx.compose.ui:ui-tooling")
}
Thêm ComposeView
ComposeView
là một Khung hiển thị Android có thể lưu trữ nội dung trên giao diện người dùng Jetpack Compose. Sử dụng setContent
để cung cấp hàm có khả năng kết hợp nội dung cho khung hiển thị này.
- Mở
layout/list_item.xml
và xem bản xem trước trong thẻ Split (Phân tách).
Khi kết thúc lớp học lập trình này, bạn sẽ thay thế khung hiển thị này bằng một khung hiển thị có khả năng kết hợp.
- Trong
JuiceListAdapter.kt
, hãy xoáListItemBinding
khỏi mọi vị trí. Trong lớpJuiceListViewHolder
, hãy thay thếbinding.root
bằngcomposeView
.
import androidx.compose.ui.platform.ComposeView
class JuiceListViewHolder(
private val onEdit: (Juice) -> Unit,
private val onDelete: (Juice) -> Unit
): RecyclerView.ViewHolder(composeView)
- Trong thư mục
onCreateViewHolder()
, hãy cập nhật hàmreturn()
sao cho khớp với đoạn mã sau:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JuiceListViewHolder {
return JuiceListViewHolder(
ComposeView(parent.context),
onEdit,
onDelete
)
}
- Trong lớp
JuiceListViewHolder
, hãy xoá mọi biếnprivate
và xoá toàn bộ mã khỏi hàmbind()
. Giờ đây, lớpJuiceListViewHolder
của bạn có dạng như đoạn mã sau:
class JuiceListViewHolder(
private val onEdit: (Juice) -> Unit,
private val onDelete: (Juice) -> Unit
) : RecyclerView.ViewHolder(composeView) {
fun bind(juice: Juice) {
}
}
- Khi đó, bạn có thể xoá các đối tượng nhập
com.example.juicetracker.databinding.ListItemBinding
vàandroid.view.LayoutInflater
.
// Delete
import com.example.juicetracker.databinding.ListItemBinding
import android.view.LayoutInflater
- Xoá tệp
layout/list_item.xml
. - Chọn OK trong hộp thoại Delete (Xoá).
4. Thêm hàm có khả năng kết hợp
Tiếp theo, bạn sẽ tạo một thành phần kết hợp để hình thành nên mục danh sách. Thành phần kết hợp này nhận Juice
và 2 hàm callback để chỉnh sửa và xoá mục danh sách.
- Trong
JuiceListAdapter.kt
, sau định nghĩa lớpJuiceListAdapter
, hãy tạo một hàm có khả năng kết hợp tên làListItem()
. - Đặt hàm
ListItem()
chấp nhận đối tượngJuice
và một hàm callback lambda để xoá.
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun ListItem(
input: Juice,
onDelete: (Juice) -> Unit,
modifier: Modifier = Modifier
) {
}
Xem bản xem trước của mục danh sách mà bạn muốn tạo. Hãy lưu ý rằng bản xem trước đó có biểu tượng nước ép, thông tin chi tiết về nước ép và biểu tượng nút xoá. Bạn sẽ sớm triển khai các thành phần này.
Tạo thành phần kết hợp cho biểu tượng Nước ép
- Trong
JuiceListAdapter.kt
, sau thành phần kết hợpListItem()
, hãy tạo một hàm có khả năng kết hợp khác có tênJuiceIcon()
. Hàm này chứacolor
vàModifier
.
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
}
- Bên trong hàm
JuiceIcon()
, hãy thêm các biến chocolor
và nội dung mô tả thành phần như trong đoạn mã sau:
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
val colorLabelMap = JuiceColor.values().associateBy { stringResource(it.label) }
val selectedColor = colorLabelMap[color]?.let { Color(it.color) }
val juiceIconContentDescription = stringResource(R.string.juice_color, color)
}
Khi sử dụng các biến colorLabelMap
và selectedColor
, bạn sẽ truy xuất tài nguyên màu liên kết với lựa chọn của người dùng.
- Thêm bố cục
Box
để thể hiện 2 biểu tượngic_juice_color
vàic_juice_clear
chồng lên nhau. Biểu tượngic_juice_color
được phủ màu và căn giữa.
import androidx.compose.foundation.layout.Box
Box(
modifier.semantics {
contentDescription = juiceIconContentDescription
}
) {
Icon(
painter = painterResource(R.drawable.ic_juice_color),
contentDescription = null,
tint = selectedColor ?: Color.Red,
modifier = Modifier.align(Alignment.Center)
)
Icon(painter = painterResource(R.drawable.ic_juice_clear), contentDescription = null)
}
Vì bạn đã biết cách triển khai thành phần kết hợp, nên chúng tôi sẽ không nêu chi tiết cách triển khai thành phần này.
- Thêm một hàm để xem trước
JuiceIcon()
. Truyền màu vào dưới dạngYellow
.
import androidx.compose.ui.tooling.preview.Preview
@Preview
@Composable
fun PreviewJuiceIcon() {
JuiceIcon("Yellow")
}
Tạo thành phần kết hợp cho thông tin chi tiết về nước ép
Trong JuiceListAdapter.kt
, bạn cần thêm một hàm có khả năng kết hợp khác để hiển thị thông tin chi tiết về nước ép. Bạn cũng cần một bố cục cột để hiển thị 2 thành phần kết hợp Text
cho tên và nội dung mô tả, cũng như một chỉ báo xếp hạng. Để làm được điều này, vui lòng hoàn thành các bước sau:
- Thêm một hàm có khả năng kết hợp có tên là
JuiceDetails()
. Hàm này sẽ nhận đối tượngJuice
vàModifier
, cũng như một thành phần kết hợp văn bản cho tên nước ép và một thành phần kết hợp để mô tả nước ép như trong đoạn mã sau:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.text.font.FontWeight
@Composable
fun JuiceDetails(juice: Juice, modifier: Modifier = Modifier) {
Column(modifier, verticalArrangement = Arrangement.Top) {
Text(
text = juice.name,
style = MaterialTheme.typography.h5.copy(fontWeight = FontWeight.Bold),
)
Text(juice.description)
RatingDisplay(rating = juice.rating, modifier = Modifier.padding(top = 8.dp))
}
}
- Để khắc phục lỗi tham chiếu chưa được giải quyết, hãy tạo một hàm có khả năng kết hợp tên là
RatingDisplay()
.
Trong hệ thống Khung hiển thị, bạn có một RatingBar
để hiển thị thanh điểm xếp hạng sau đây. Compose không có thành phần kết hợp cho thanh điểm xếp hạng. Vì vậy, bạn cần triển khai thành phần này từ đầu.
- Xác định hàm
RatingDisplay()
để thể hiện các dấu sao theo điểm xếp hạng. Hàm có khả năng kết hợp này cho thấy số sao dựa trên điểm xếp hạng.
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
@Composable
fun RatingDisplay(rating: Int, modifier: Modifier = Modifier) {
val displayDescription = pluralStringResource(R.plurals.number_of_stars, count = rating)
Row(
// Content description is added here to support accessibility
modifier.semantics {
contentDescription = displayDescription
}
) {
repeat(rating) {
// Star [contentDescription] is null as the image is for illustrative purpose
Image(
modifier = Modifier.size(32.dp),
painter = painterResource(R.drawable.star),
contentDescription = null
)
}
}
}
Để tạo dấu sao có thể vẽ được trong Compose, bạn cần tạo thành phần vectơ cho dấu sao đó.
- Trong ngăn Project (Dự án), hãy nhấp chuột phải vào drawable > New > Vector Asset (đối tượng có thể vẽ > Tạo mới > Thành phần vectơ).
- Trong hộp thoại Asset Studio, hãy tìm một biểu tượng dấu sao. Chọn biểu tượng dấu sao được tô kín.
- Thay đổi giá trị màu của dấu sao thành 625B71.
- Nhấp vào Next > Finish (Tiếp theo > Hoàn tất).
- Xin lưu ý rằng một đối tượng có thể vẽ sẽ xuất hiện trong thư mục
res/drawable
.
- Thêm thành phần kết hợp xem trước để xem trước thành phần kết hợp
JuiceDetails
.
@Preview
@Composable
fun PreviewJuiceDetails() {
JuiceDetails(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4))
}
Tạo thành phần kết hợp cho nút xoá
- Trong
JuiceListAdapter.kt
, hãy thêm một hàm có khả năng kết hợp khác có tênDeleteButton()
. Hàm này sẽ nhận một hàm callback lambda và một Đối tượng sửa đổi. - Đặt hàm lambda thành đối số
onClick
và truyền vàoIcon()
như trong đoạn mã sau:
import androidx.compose.ui.res.painterResource
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@Composable
fun DeleteButton(onDelete: () -> Unit, modifier: Modifier = Modifier) {
IconButton(
onClick = { onDelete() },
modifier = modifier
) {
Icon(
painter = painterResource(R.drawable.ic_delete),
contentDescription = stringResource(R.string.delete)
)
}
}
- Thêm hàm xem trước để xem trước nút xoá.
@Preview
@Composable
fun PreviewDeleteIcon() {
DeleteButton({})
}
5. Triển khai hàm ListItem
Giờ đây, khi đã có đủ thành phần kết hợp cần thiết để hiển thị mục danh sách, bạn có thể sắp xếp các thành phần này trong một bố cục. Hãy lưu ý hàm ListItem()
mà bạn đã xác định ở bước trước.
@Composable
fun ListItem(
input: Juice,
onEdit: (Juice) -> Unit,
onDelete: (Juice) -> Unit,
modifier: Modifier = Modifier
) {
}
Trong JuiceListAdapter.kt
, hãy hoàn tất các bước sau để triển khai hàm ListItem()
.
- Thêm bố cục
Row
bên trong hàm lambdaMdc3Theme {}
.
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import com.google.accompanist.themeadapter.material3.Mdc3Theme
Mdc3Theme {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween
) {
}
}
- Bên trong lambda
Row
, hãy gọi 3 thành phần kết hợp có tênJuiceIcon
,JuiceDetails
,DeleteButton
mà bạn tạo làm thành phần con.
JuiceIcon(input.color)
JuiceDetails(input, Modifier.weight(1f))
DeleteButton({})
Việc truyền Modifier.
weight
(1f)
đến thành phần kết hợp JuiceDetails()
đảm bảo các thông tin chi tiết về nước ép hiện trong phần không gian nằm ngang còn lại sau khi đo lường các thành phần con không tính trọng số.
- Truyền vào hàm lambda
onDelete(input)
và đối tượng sửa đổi các tham số căn chỉnh phần trên cùng cho thành phần kết hợpDeleteButton
.
DeleteButton(
onDelete = {
onDelete(input)
},
modifier = Modifier.align(Alignment.Top)
)
- Viết một hàm xem trước để xem trước thành phần kết hợp
ListItem
.
@Preview
@Composable
fun PreviewListItem() {
ListItem(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4), {})
}
- Liên kết thành phần kết hợp
ListItem
với trình lưu trữ khung hiển thị. GọionEdit(input)
bên trong hàm lambdaclickable()
để mở hộp thoại chỉnh sửa khi mục trong danh sách được nhấp vào.
Bạn cần lưu trữ thành phần kết hợp trong lớp JuiceListViewHolder
, bên trong hàm bind()
. Bạn sẽ sử dụng ComposeView
, một Khung hiển thị Android có thể lưu trữ nội dung trên giao diện người dùng Compose bằng phương thức setContent
.
fun bind(input: Juice) {
composeView.setContent {
ListItem(
input,
onDelete,
modifier = Modifier
.fillMaxWidth()
.clickable {
onEdit(input)
}
.padding(vertical = 8.dp, horizontal = 16.dp),
)
}
}
- Chạy ứng dụng. Thêm loại nước ép bạn thích. Chiêm ngưỡng mục danh sách mới trong Compose.
.
Xin chúc mừng! Bạn vừa tạo ứng dụng đầu tiên của mình có khả năng tương tác trong Compose (có sử dụng các thành phần Compose trong một ứng dụng dựa trên khung hiển thị).
6. Lấy mã giải pháp
Để tải mã này xuống khi lớp học lập trình đã kết thúc, bạn có thể sử dụng các lệnh git sau:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views-with-compose
Ngoài ra, bạn có thể tải kho lưu trữ xuống dưới dạng tệp zip rồi giải nén và mở trong Android Studio.
Nếu bạn muốn xem mã giải pháp, hãy xem mã đó trên GitHub.
7. Tìm hiểu thêm
Tài liệu dành cho nhà phát triển Android
- Công cụ dành cho Compose | Jetpack Compose | Nhà phát triển Android
- Interoperability API (API Khả năng tương tác) | Jetpack Compose | Nhà phát triển Android
- Chiến lược di chuyển | Jetpack Compose | Nhà phát triển Android
Lớp học lập trình [Trung cấp]