1. Trước khi bắt đầu
Trong lớp học lập trình này, bạn sẽ sử dụng mã nguồn giải pháp từ lớp học lập trình Giới thiệu về trạng thái trong Compose để tạo một công cụ tính tiền boa mang tính tương tác. Công cụ này có thể tự động tính toán và làm tròn số tiền boa khi bạn nhập số tiền trên hoá đơn và tỷ lệ phần trăm boa. Dưới đây là ảnh chụp màn hình khi ứng dụng đã hoàn thiện.
Điều kiện tiên quyết
- Lớp học lập trình Trạng thái Use trong Jetpack Compose
- Khả năng thêm thành phần kết hợp
Text
vàTextField
vào một ứng dụng. - Kiến thức về hàm
remember
, trạng thái, tính năng chuyển trạng thái lên trên (state hoisting) và sự khác biệt giữa các hàm kết hợp có trạng thái và không có trạng thái
Kiến thức bạn sẽ học được
- Cách thêm nút hành động vào bàn phím ảo.
- Cách thiết lập các thao tác với bàn phím.
- Thành phần kết hợp
Switch
là gì và cách sử dụng nó. - Layout Inspector là gì.
Sản phẩm bạn sẽ tạo ra
- Một ứng dụng Tip Time tính số tiền boa dựa trên chi phí dịch vụ và tỷ lệ boa mà người dùng nhập.
Bạn cần có
- Android Studio
- Mã nguồn giải pháp từ lớp học lập trình Trạng thái Use trong Jetpack Compose
2. Tổng quan về ứng dụng khởi đầu
Lớp học lập trình này bắt đầu bằng Ứng dụng Tip Time trong lớp học lập trình trước. Ứng dụng này cung cấp giao diện người dùng cần thiết để tính toán tiền boa dựa trên tỷ lệ phần trăm tiền boa cố định. Hộp văn bản Chi phí dịch vụ cho phép người sử dụng nhập chi phí dịch vụ. Ứng dụng sẽ tính toán và hiển thị số tiền boa trong thành phần kết hợp Text
.
Lấy mã nguồn ban đầu
Để bắt đầu, hãy tải 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-tip-calculator.git $ cd basic-android-kotlin-compose-training-tip-calculator $ git checkout state
Bạn có thể duyệt xem mã đó trong kho lưu trữ GitHub của Tip Calculator
.
Chạy ứng dụng Tip Time (Tính tiền boa)
- Mở dự án Tip Time trong Android Studio và chạy ứng dụng trên trình mô phỏng hoặc thiết bị.
- Nhập chi phí dịch vụ. Ứng dụng sẽ tự động tính toán và hiển thị số tiền boa.
Theo cách tính hiện tại, tỷ lệ phần trăm tiền boa được nhập trực tiếp vào mã nguồn là 15%. Trong lớp học lập trình này, bạn mở rộng tính năng này bằng một trường văn bản để ứng dụng có thể tùy chỉnh tỷ lệ phần trăm tiền boa và làm tròn số tiền boa.
Thêm các tài nguyên chuỗi cần thiết
- Trong thẻ Project (Dự án), hãy nhấp vào res > values > strings.xml (tài nguyên > giá trị > strings.xml).
- Ở giữa các thẻ
<resources>
của tệpstrings.xml
, hãy thêm các tài nguyên chuỗi sau:
<string name="how_was_the_service">Tip (%)</string>
<string name="round_up_tip">Round up tip?</string>
Tệp strings.xml
sẽ có dạng như đoạn mã này, bao gồm các chuỗi văn bản từ lớp học lập trình trước:
strings.xml
<resources>
<string name="app_name">TipTime</string>
<string name="calculate_tip">Calculate Tip</string>
<string name="cost_of_service">Cost of Service</string>
<string name="how_was_the_service">Tip (%)</string>
<string name="round_up_tip">Round up tip?</string>
<string name="tip_amount">Tip Amount: %s</string>
</resources>
- Thay đổi chuỗi
Cost Of Service
thành chuỗiBill Amount
. Ở một số quốc gia, dịch vụ có nghĩa là tiền boa, do đó, thay đổi này sẽ giúp tránh nhầm lẫn. - Trong chuỗi
Cost of Service
, hãy nhấp chuột phải vào thuộc tínhcost_of_service
củaname
, rồi chọn Refactor > Rename(Tái cấu trúc > Đổi tên). Một hộp thoại Rename (Đổi tên) sẽ mở ra.
- Trong hộp thoại Rename (Đổi tên), hãy thay thế
cost_of _service
bằngbill_amount
và nhấp vào Refactor (Tái cấu trúc). Thao tác này sẽ cập nhật tất cả các lần xuất hiện của chuỗicost_of_service
trong dự án, nhờ đó, bạn không cần phải thay đổi mã Compose theo cách thủ công.
- Trong tệp
strings.xml
hãy thay đổi giá trị chuỗi từCost of Service
thànhBill Amount
:
<string name="bill_amount">Bill Amount</string>
- Chuyển đến tệp
MainActivity.kt
rồi chạy ứng dụng. Nhãn được cập nhật trong hộp văn bản như trong hình sau:
3. Thêm trường văn bản tip-percentage
Khách hàng có thể muốn boa nhiều hoặc ít hơn tuỳ vào chất lượng dịch vụ họ nhận được và các lý do khác. Để đáp ứng yêu cầu này, ứng dụng phải cho phép người dùng tùy chỉnh số tiền boa. Trong phần này, bạn sẽ thêm một trường văn bản để người dùng nhập phần trăm tiền boa tuỳ chỉnh như trong hình sau:
Bạn đã có trường văn bản Bill Amount (Số tiền trên hoá đơn) trong ứng dụng, đây là hàm có khả năng kết hợp EditNumberField()
không có trạng thái. Trong lớp học lập trình trước, bạn đã đưa trạng thái amountInput
từ thành phần kết hợp EditNumberField()
vào hàm TipTimeScreen()
, điều này khiến thành phần kết hợp EditNumberField()
không có trạng thái.
Để thêm một trường văn bản, bạn có thể sử dụng lại thành phần kết hợp EditNumberField()
, nhưng với nhãn khác. Để thay đổi như vậy, bạn cần truyền nhãn dưới dạng một tham số, thay vì cố định giá trị trong mã của nhãn này vào trong hàm có khả năng kết hợp EditNumberField()
.
Giúp hàm kết hợp EditNumberField()
có thể được tái sử dụng:
- Ở tệp
MainActivity.kt
trong tham số của hàm kết hợpEditNumberField()
, hãy thêm tài nguyên chuỗilabel
thuộc kiểuInt
:
@Composable
fun EditNumberField(
label: Int,
value: String,
onValueChange: (String) -> Unit
)
- Thêm một đối số
modifier
thuộc kiểuModifier
vào hàm kết hợpEditNumberField()
:
@Composable
fun EditNumberField(
label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
)
- Trong phần thân hàm, hãy thay thế ID của tài nguyên chuỗi đã được nhập trực tiếp vào đoạn mã bằng tham số
label
:
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
label = { Text(stringResource(label)) },
//...
)
}
- Để biểu thị tham số
label
được kỳ vọng là tham chiếu tài nguyên chuỗi, hãy chú giải tham số hàm bằng chú thích@StringRes
:
@Composable
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
)
- Nhập các mục sau đây:
import androidx.annotation.StringRes
- Trong bảng thành phần kết hợp
TextField
của hàmEditNumberField()
, hãy truyền tham sốlabel
vào hàmstringResource()
.
@Composable
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
TextField(
//...
label = { Text(stringResource(label)) },
//...
)
}
- Trong lệnh gọi hàm
EditNumberField()
của hàmTipTimeScreen()
, hãy đặt tham sốlabel
thành tài nguyên chuỗiR.string.bill_amount
:
EditNumberField(
label = R.string.bill_amount,
value = amountInput,
onValueChange = { amountInput = it }
)
- Trong ngăn Design (Thiết kế), hãy nhấp vào Build & Refresh (Tạo và làm mới). Giao diện người dùng của ứng dụng sẽ trông giống như hình sau:
- Trong hàm
TipTimeScreen()
sau lệnh gọi hàmEditNumberField()
, hãy thêm một trường văn bản khác cho tỷ lệ phần trăm tiền boa tùy chỉnh. Gọi một hàm kết hợpEditNumberField()
với các tham số sau:
EditNumberField(
label = R.string.how_was_the_service,
value = "",
onValueChange = { }
)
Thao tác này sẽ thêm một hộp văn bản khác cho tỷ lệ phần trăm tiền boa tùy chỉnh.
- Trong ngăn Design (Thiết kế), hãy nhấp vào Build & Refresh (Tạo và làm mới). Bản xem trước ứng dụng hiện hiển thị trường văn bản Tiền boa (%) như trong hình sau:
- Ở đầu hàm
TipTimeScreen()
, hãy thêm một thuộc tínhvar
có tên làtipInput
cho biến trạng thái của trường văn bản đã thêm. Hãy dùngmutableStateOf("")
để khởi tạo biến và kết hợp với lệnh gọi bằng hàmremember
:
var tipInput by remember { mutableStateOf("") }
- Trong lệnh gọi hàm mới
EditNumberField
()
, hãy đặt tham số có tênvalue
thành biếntipInput
và sau đó cập nhật biếntipInput
trong biểu thức lambdaonValueChange
:
EditNumberField(
label = R.string.how_was_the_service,
value = tipInput,
onValueChange = { tipInput = it }
)
- Trong hàm
TipTimeScreen()
sau định nghĩa biếntipInput
, hãy xác định một biếnval
có tên làtipPercent
, biến này sẽ chuyển đổi biếntipInput
thành kiểuDouble
, hãy sử dụng toán tử elvis và trả về0.0
nếu giá trị lànull
:
val tipPercent = tipInput.toDoubleOrNull() ?: 0.0
- Trong hàm
TipTimeScreen()
, hãy cập nhật lệnh gọi hàmcalculateTip()
, truyền biếntipPercent
làm tham số thứ hai:
val tip = calculateTip(amount, tipPercent)
Đoạn mã cho hàm TipTimeScreen()
sẽ trông giống như sau:
@Composable
fun TipTimeScreen() {
var amountInput by remember { mutableStateOf("") }
var tipInput by remember { mutableStateOf("") }
val tipPercent = tipInput.toDoubleOrNull() ?: 0.0
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount, tipPercent)
Column(
modifier = Modifier.padding(32.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = stringResource(R.string.calculate_tip),
fontSize = 24.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(Modifier.height(16.dp))
EditNumberField(
label = R.string.bill_amount,
value = amountInput,
onValueChange = { amountInput = it }
)
EditNumberField(
label = R.string.how_was_the_service,
value = tipInput,
onValueChange = { tipInput = it }
)
Spacer(Modifier.height(24.dp))
Text(
text = stringResource(R.string.tip_amount, tip),
modifier = Modifier.align(Alignment.CenterHorizontally),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
}
}
- Chạy ứng dụng trên trình mô phỏng hoặc thiết bị, sau đó nhập số tiền trên hóa đơn và tỷ lệ phần trăm tiền boa. Ứng dụng có tính đúng số tiền boa không?
4. Đặt nút hành động
Trong lớp học lập trình trước, bạn đã tìm hiểu cách dùng lớp KeyboardOptions
để cài đặt loại bàn phím. Trong phần này, bạn sẽ tìm hiểu cách thiết lập nút hành động trên bàn phím cũng với KeyboardOptions
. Nút hành động trên bàn phím là nút nằm dưới cùng bàn phím. Bạn có thể thấy một số ví dụ trong bảng này:
Thuộc tính | Nút hành động trên bàn phím |
| |
| |
|
Trong tác vụ này, bạn cài đặt hai nút hành động khác nhau cho các hộp văn bản:
- Nút hành động Next (Tiếp theo) cho hộp văn bản Bill Amount (Số tiền hoá đơn), cho biết người dùng hiện đã hoàn thành thao tác nhập và muốn chuyển sang hộp văn bản tiếp theo.
- Nút hành động Done (Xong) cho hộp văn bản Tip % (% tiền boa) cho biết người dùng đã nhập xong thông tin đầu vào.
Bạn có thể xem ví dụ về bàn phím với các nút hành động trong các hình sau:
Thêm tuỳ chọn bàn phím:
- Trong lệnh gọi hàm
TextField()
của hàmEditNumberField()
, hãy truyền hàm khởi tạoKeyboardOptions
, mộtimeAction
được đặt đối số thành giá trịImeAction.Next
. Dùng hàmKeyboardOptions.Default.copy
để sử dụng các chế độ mặc định khác như viết hoa và tự động sửa lỗi.
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
)
)
}
- Chạy ứng dụng trên trình mô phỏng hoặc thiết bị. Giờ đây, bàn phím hiển thị nút hành động Tiếp theo như có thể thấy trong hình sau:
Tuy nhiên, bạn cần hai nút hành động khác nhau cho các trường văn bản. Bạn sẽ sớm khắc phục được vấn đề này.
- Kiểm tra hàm
EditNumberField()
. Tham sốkeyboardOptions
trong hàmTextField()
được mã hóa cứng. Để tạo những nút hành động khác cho các trường văn bản, bạn cần truyền đối tượngKeyboardOptions
dưới dạng một đối số. Đây là việc bạn sẽ thực hiện trong bước tiếp theo.
// No need to copy, just examine the code.
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit
) {
TextField(
//...
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
)
)
}
- Trong định nghĩa hàm
EditNumberField()
, hãy thêm một tham sốkeyboardOptions
thuộc loạiKeyboardOptions
. Trong phần nội dung hàm, hãy chỉ định hàm này vào tham số có tênkeyboardOptions
của hàmTextField()
:
@Composable
fun EditNumberField(
@StringRes label: Int,
keyboardOptions: KeyboardOptions,
value: String,
onValueChange: (String) -> Unit
){
TextField(
//...
keyboardOptions = keyboardOptions
)
}
- Trong hàm
TipTimeScreen()
, hãy cập nhật lệnh gọi hàmEditNumberField()
đầu tiên, truyền tham số có tênkeyboardOptions
vào trường văn bản Bill Amount (Số tiền trên hoá đơn).
EditNumberField(
label = R.string.bill_amount,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
value = amountInput,
onValueChange = { amountInput = it }
)
- Ở lệnh gọi hàm thứ hai
EditNumberField()
, thay đổi% tiền boa của trường văn bảnimeAction
thànhImeAction.Done
. Hàm của bạn phải trông giống như đoạn mã sau:
EditNumberField(
label = R.string.how_was_the_service,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
value = tipInput,
onValueChange = { tipInput = it }
)
- Chạy ứng dụng. Thành phần này hiển thị các nút hành động Tiếp theo và Xong như có thể thấy trong các hình sau:
- Nhập số tiền hóa đơn bất kỳ và nhấp vào nút hành động Tiếp theo, sau đó nhập tỷ lệ phần trăm tiền boa bất kỳ và nhấp vào nút hành động Xong. Không có gì xảy ra cả vì bạn chưa thêm chức năng nào vào các nút. Bạn có thể làm như vậy trong phần tiếp theo.
5. Đặt thao tác bằng bàn phím
Trong phần này, bạn triển khai chức năng để di chuyển tiêu điểm đến trường văn bản tiếp theo và đóng bàn phím để cải thiện trải nghiệm người dùng bằng lớp KeyboardActions
. Điều này cho phép nhà phát triển chỉ định những hành động được kích hoạt trong phản hồi hành động IME (trình chỉnh sửa Phương thức nhập) của người dùng trên bàn phím phần mềm. Một ví dụ về hành động IME là khi người dùng nhấp vào nút hành động Tiếp theo hoặc Xong.
Bạn hãy triển khai như sau:
- Trên hành động Tiếp theo: Di chuyển tiêu điểm đến trường văn bản tiếp theo (hộp văn bản % tiền boa ).
- Trên hành động Xong: Đóng bàn phím ảo.
- Trong hàm
EditNumberField()
, hãy thêm một biếnval
có tên làfocusManager
và chỉ định giá trị của thuộc tínhLocalFocusManager.current
:
val focusManager = LocalFocusManager.current
Giao diện LocalFocusManager
được dùng để kiểm soát tiêu điểm trong Compose. Bạn sử dụng biến này để di chuyển tiêu điểm đến và xóa tiêu điểm khỏi các hộp văn bản.
- Nhập
import androidx.compose.ui.platform.LocalFocusManager
. - Trong chữ ký hàm
EditNumberField()
, hãy thêm một tham sốkeyboardActions
khác thuộc loạiKeyboardActions
:
@Composable
fun EditNumberField(
@StringRes label: Int,
keyboardOptions: KeyboardOptions,
keyboardActions: KeyboardActions,
value: String,
onValueChange: (String) -> Unit
) {
//...
}
- Trong phần nội dung hàm
EditNumberField()
, hãy cập nhật lệnh gọi hàmTextField
()
, đặt tham sốkeyboardActions
thành tham sốkeyboardActions
được truyền vào.
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
keyboardActions = keyboardActions
)
}
Giờ đây, bạn có thể tuỳ chỉnh các trường văn bản bằng chức năng khác nhau cho từng nút hành động.
- Trong lệnh gọi hàm
TipTimeScreen()
, hãy cập nhật lệnh gọi hàmEditNumberField()
đầu tiên để bao gồm một tham số có tênkeyboardActions
làm đối số mới. Chỉ định nó một giá trị,KeyboardActions( onNext =
{ }
)
:
// Bill amount text field
EditNumberField(
//...
keyboardActions = KeyboardActions(
onNext = { }
),
//...
)
Biểu thức lambda của tham số có tên onNext
sẽ chạy khi người dùng nhấn nút hành động Tiếp theo trên bàn phím.
- Xác định hàm lambda, yêu cầu
FocusManager
di chuyển tiêu điểm xuống thành phần kết hợp tiếp theo là Tip % (% tiền boa). Trong biểu thức lambda, hãy gọi hàmmoveFocus()
trên đối tượngfocusManager
rồi truyền vào đối sốFocusDirection.Down
:
// Bill amount text field
EditNumberField(
label = R.string.bill_amount,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }
),
value = amountInput,
onValueChange = { amountInput = it }
)
Hàm moveFocus()
di chuyển tiêu điểm theo hướng đã chỉ định, khớp với trường văn bản Tip % (% tiền boa) trong trường hợp này.
- Nhập các mục sau:
import androidx.compose.ui.focus.FocusDirection
- Triển khai tương tự với trường văn bản Tip % (% tiền boa). Sự khác biệt là bạn cần xác định tham số có tên
onDone
thay vìonNext
.
// Tip% text field
EditNumberField(
//...
keyboardActions = KeyboardActions(
onDone = { }
),
//...
)
- Sau khi người dùng nhập tiền boa tuỳ chỉnh, thao tác Done (Xong) trên bàn phím sẽ xoá tiêu điểm và cũng sẽ đóng bàn phím. Hãy xác định hàm lambda, yêu cầu
FocusManager
để xóa tiêu điểm. Trong biểu thức lambda, hãy gọi hàmclearFocus()
trên đối tượngfocusManager
:
EditNumberField(
label = R.string.how_was_the_service,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = { focusManager.clearFocus() }),
value = tipInput,
onValueChange = { tipInput = it }
)
Hàm clearFocus()
xóa tiêu điểm khỏi thành phần được đặt tiêu điểm.
- Chạy ứng dụng. Giờ đây, các thao tác trên bàn phím sẽ thay đổi thành phần được đặt tiêu điểm như có thể thấy trong ảnh GIF này:
6. Thêm một nút chuyển
Nút chuyển có trạng thái bật hoặc tắt một mục. Nút bật/tắt có 2 trạng thái cho phép người dùng chọn giữa 2 tuỳ chọn. Một nút chuyển bao gồm hai phần thumb và track bạn có thể thấy trong những hình sau:
1. Thumb |
Nút chuyển là một lựa chọn kiểm soát có thể được dùng để nhập quyết định hoặc khai báo tùy chọn, chẳng hạn như các cài đặt bạn có thể thấy trong hình sau:
Người dùng có thể kéo nút thumb qua lại để đánh dấu lựa chọn hoặc chỉ cần nhấn vào nút chuyển để bật/tắt. Bạn có thể xem một ví dụ khác về nút bật/tắt trong ảnh GIF này, trong đó chế độ cài đặt Visual options (Tuỳ chọn hiển thị) được chuyển thành Dark mode (Chế độ tối):
Để tìm hiểu thêm về nút chuyển, hãy tìm đọc tài liệu về Nút chuyển.
Bạn sử dụng thành phần kết hợp Switch
để người dùng có thể chọn làm tròn số tiền boa lên số nguyên gần nhất như có thể thấy trong hình sau:
Thêm một hàng cho thành phần kết hợp Text
và Switch
:
- Sau hàm
EditNumberField()
, hãy thêm một hàm tổng hợpRoundTheTipRow()
rồi truyền mộtModifier
mặc định làm đối số tương tự như hàmEditNumberField()
:
@Composable
fun RoundTheTipRow(modifier: Modifier = Modifier) {
}
- Triển khai hàm
RoundTheTipRow()
, thêm một bố cục thành phần kết hợpRow
vớimodifier
sau để đặt chiều rộng của các thành phần con thành mức tối đa trên màn hình, căn giữa và đảm bảo nó có kích thước48
dp
:
Row(
modifier = Modifier
.fillMaxWidth()
.size(48.dp),
verticalAlignment = Alignment.CenterVertically
) {
}
- Nhập các mục sau:
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Size
- Trong khối lambda của bố cục thành phần kết hợp
Row
, hãy thêm một thành phần kết hợpText
sử dụng tài nguyên chuỗiR.string.round_up_tip
để hiển thị một chuỗiRound up tip?
:
Text(text = stringResource(R.string.round_up_tip))
- Sau thành phần kết hợp
Text
, hãy thêm một thành phần kết hợpSwitch
và truyền tham số có tênchecked
được đặt thànhroundUp
và một tham số có tênonCheckedChange
được đặt thànhonRoundUpChanged
.
Switch(
checked = roundUp,
onCheckedChange = onRoundUpChanged,
)
Bảng này chứa thông tin về các tham số bạn đã xác định cho hàm RoundTheTipRow()
:
Tham số | Mô tả |
| Liệu nút chuyển này có được kiểm tra hay không. Đây là trạng thái của thành phần kết hợp |
| Lệnh gọi lại sẽ được gọi khi nút chuyển được nhấp. |
- Nhập các mục sau đây:
import androidx.compose.material.Switch
- Trong hàm
RoundTipRow()
, hãy thêm tham sốroundUp
thuộc loạiBoolean
và hàm lambdaonRoundUpChanged
nhậnBoolean
và không trả về giá trị nào:
@Composable
fun RoundTheTipRow(
roundUp: Boolean,
onRoundUpChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier
)
Việc này sẽ chuyển trạng thái của nút chuyển.
- Trong thành phần kết hợp
Switch
, hãy thêmmodifier
này để căn chỉnh thành phần kết hợpSwitch
đến cuối màn hình:
Switch(
modifier = modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
//...
)
- Nhập các mục sau đây:
import androidx.compose.foundation.layout.wrapContentWidth
- Trong hàm
TipTimeScreen()
, hãy thêm một biến var cho trạng thái của thành phần kết hợpSwitch
. Tạo một biếnvar
có tên làroundUp
, đặt biến đó thànhmutableStateOf()
, trong đófalse
làm đối số mặc định. Bao quanh lệnh gọi bằngremember { }
.
fun TipTimeScreen() {
//...
var roundUp by remember { mutableStateOf(false) }
//...
Column(
...
) {
//...
}
}
Đây là biến cho trạng thái thành phần kết hợp Switch
và false sẽ là trạng thái mặc định.
- Trong khối
TipTimeScreen()
của hàmColumn
sau trường văn bản Tip % (% tiền boa), hãy gọi hàmRoundTheTipRow()
với các đối số sau: tham số có tênroundUp
được đặt thànhroundUp
và tham số có tênonRoundUpChanged
được đặt thành lệnh gọi lại lambda cập nhật giá trịroundUp
:
@Composable
fun TipTimeScreen() {
//...
Column(
...
) {
Text(
...
)
Spacer(...)
EditNumberField(
...
)
EditNumberField(
...
)
RoundTheTipRow(roundUp = roundUp, onRoundUpChanged = { roundUp = it })
Spacer(...)
Text(
...
)
}
}
Thao tác này sẽ hiển thị hàng Tiền boa được làm tròn.
- Chạy ứng dụng. Ứng dụng hiển thị nút bật/tắt Round up tip? (Làm tròn tiền boa?), nhưng nút thumb bật/tắt hầu như không hiển thị như trong hình này:
1. Thumb |
Bạn có thể cải thiện khả năng hiển thị nút thumb ở những bước tiếp theo bằng cách chuyển nó sang màu xám đậm.
- Trong thành phần kết hợp
Switch()
của hàmRoundTheTipRow()
, hãy thêm một tham số có tên làcolors
. - Đặt tham số có tên
colors
thành hàmSwitchDefaults.colors()
chấp nhận một tham số có tênuncheckedThumbColor
được đặt thành đối sốColor.DarkGray
.
Switch(
//...
colors = SwitchDefaults.colors(
uncheckedThumbColor = Color.DarkGray
)
)
- Nhập các mục sau:
import androidx.compose.material.SwitchDefaults
import androidx.compose.ui.graphics.Color
Hàm kết hợp RoundTheTipRow()
giờ đây sẽ có dạng như đoạn mã sau:
@Composable
fun RoundTheTipRow(roundUp: Boolean, onRoundUpChanged: (Boolean) -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.size(48.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(stringResource(R.string.round_up_tip))
Switch(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
checked = roundUp,
onCheckedChange = onRoundUpChanged,
colors = SwitchDefaults.colors(
uncheckedThumbColor = Color.DarkGray
)
)
}
}
- Chạy ứng dụng. Màu nút thumb của nút chuyển sẽ khác như trong hình sau:
- Nhập số tiền trên hoá đơn và tỷ lệ phần trăm của tiền boa, sau đó chọn nút bật/tắt Round up tip? (Làm tròn tiền boa?). Số tiền boa sẽ không được làm tròn vì bạn vẫn cần phải cập nhật hàm
calculateTip()
trong phần tiếp theo.
Cập nhật hàm calculateTip()
để làm tròn tiền boa
Sửa đổi hàm calculateTip()
để chấp nhận biến Boolean
nhằm làm tròn tiền boa lên số nguyên gần nhất:
- Để làm tròn tiền boa, hàm
calculateTip()
phải biết trạng thái của nút chuyển, đó làBoolean
. Trong hàmcalculateTip()
, hãy thêm tham sốroundUp
thuộc loạiBoolean
:
private fun calculateTip(
amount: Double,
tipPercent: Double = 15.0,
roundUp: Boolean
): String {
//...
}
- Trong hàm
calculateTip()
trước câu lệnhreturn
, hãy thêm một điều kiệnif()
kiểm tra giá trịroundUp
. NếuroundUp
làtrue
, xác định một biếntip
và đặt thànhkotlin.math.
ceil
()
rồi truyền hàmtip
làm đối số:
if (roundUp)
tip = kotlin.math.ceil(tip)
Hàm calculateTip()
hoàn chỉnh sẽ có dạng như đoạn mã này:
private fun calculateTip(amount: Double, tipPercent: Double = 15.0, roundUp: Boolean): String {
var tip = tipPercent / 100 * amount
if (roundUp)
tip = kotlin.math.ceil(tip)
return NumberFormat.getCurrencyInstance().format(tip)
}
- Trong hàm
TipTimeScreen()
, hãy cập nhật lệnh gọi hàmcalculateTip()
rồi truyền một tham sốroundUp
:
val tip = calculateTip(amount, tipPercent, roundUp)
- Chạy ứng dụng. Giờ đây, nó sẽ làm tròn số tiền boa như bạn thấy ở những hình sau:
7. Lấy mã giải pháp
Để tải xuống mã cho lớp học lập trình đã kết thúc, bạn có thể sử dụng lệnh git này:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator.git
Ngoài ra, bạn có thể tải kho lưu trữ xuống dưới dạng tệp zip, sau đó giải nén và mở tệp đó trong Android Studio.
Nếu bạn muốn xem mã giải pháp, hãy xem mã đó trên GitHub.
8. Kết luận
Xin chúc mừng! Bạn đã thêm chức năng tiền boa tùy chỉnh vào Ứng dụng Tip Time của mình. Giờ đây, ứng dụng cho phép người dùng nhập một tỷ lệ phần trăm tiền boa tùy chỉnh và làm tròn số tiền boa đó. Chia sẻ thành quả của bạn trên mạng xã hội với #AndroidBasics!