1. Trước khi bắt đầu
Trong lớp học lập trình này, bạn sẽ xây dựng bố cục cho ứng dụng tính tiền boa cơ bản. Kết thúc lớp học này, bạn sẽ có một giao diện người dùng đang hoạt động, nhưng chức năng tính tiền boa vẫn chưa hoạt động. Làm cho ứng dụng bắt đầu hoạt động và trông chuyên nghiệp hơn sẽ là nội dung được đề cập trong các lớp học lập trình sau.
Điều kiện tiên quyết
- Có khả năng tạo và chạy một ứng dụng Android dựa trên mẫu trong Android Studio
Kiến thức bạn sẽ học được
- Biết cách đọc và ghi bố cục XML trong Android
- Biết cách tạo bố cục cho một biểu mẫu đơn giản để người dùng nhập văn bản và đưa ra các lựa chọn
Sản phẩm bạn sẽ tạo ra
- Giao diện người dùng cho ứng dụng tính toán tiền boa trên Android
Bạn cần có
- Máy tính đã cài đặt phiên bản Android Studio ổn định mới nhất
- Kết nối Internet để truy cập vào tài liệu dành cho nhà phát triển Android
2. Bắt đầu dự án
Hãy tham khảo máy tính tiền boa trên Google: https://www.google.com/search?q=tip+computer
Trong lộ trình này, bạn sẽ xây dựng một ứng dụng Android mô phỏng phiên bản đơn giản của máy tính tiền boa.
Các nhà phát triển thường đi theo cách tiếp cận này – trước hết sẽ xây dựng một phiên bản ứng dụng đơn giản và hoạt động một phần (thậm chí ứng dụng nhìn vẫn chưa đẹp lắm), sau đó sẽ hoàn chỉnh dần các chức năng và hình ảnh của ứng dụng.
Kết thúc lớp học lập trình này, ứng dụng tính tiền boa sẽ có dạng như sau:
Bạn sẽ sử dụng các thành phần sau đây trên giao diện người dùng do Android cung cấp:
EditText
- để nhập và chỉnh sửa văn bảnTextView
– để hiển thị văn bản như câu hỏi về chất lượng phục vụ và số tiền boaRadioButton
- nút chọn cho phép chọn các tuỳ chọn tiền boaRadioGroup
– để nhóm các tuỳ chọn nút chọnSwitch
– nút chuyển bật/tắt để xác định có làm tròn tiền boa hay không
Tạo dự án Hoạt động trống
- Để bắt đầu, hãy tạo một dự án Kotlin mới trong Android Studio bằng mẫu Empty Activity (Hoạt động trống).
- Đặt tên ứng dụng là "Tính tiền boa", với cấp độ API tối thiểu là 19 (KitKat). Tên gói là com.example.tiptime.
- Nhấp vào Finish (Hoàn tất) để tạo ứng dụng.
3. Đọc và hiểu XML
Thay vì sử dụng Layout Editor (Trình chỉnh sửa bố cục) quen thuộc, bạn sẽ xây dựng bố cục ứng dụng bằng cách chỉnh sửa tệp XML mô tả giao diện người dùng. Học cách hiểu rõ và chỉnh sửa bố cục giao diện người dùng bằng XML rất quan trọng đối với một nhà phát triển Android như bạn.
Bạn sẽ xem xét và chỉnh sửa tệp XML dùng để định nghĩa bố cục giao diện người dùng của ứng dụng này. XML là viết tắt của eXtensible Markup Language(Ngôn ngữ đánh dấu mở rộng), là một cách mô tả dữ liệu sử dụng tài liệu dựa trên văn bản. XML có thể mở rộng và rất linh hoạt nên được sử dụng cho nhiều mục đích khác nhau, bao gồm định nghĩa bố cục giao diện người dùng cho các ứng dụng Android. Bạn có thể nhớ lại các lớp học lập trình trước đây, các tài nguyên ứng dụng khác như chuỗi cũng được định nghĩa trong tệp XML có tên là strings.xml
.
Giao diện người dùng trên ứng dụng Android được xây dựng dưới dạng phân cấp vùng chứa của các thành phần (tiện ích) và bố cục trên màn hình của các thành phần đó. Lưu ý rằng các bố cục đó là chính các thành phần giao diện người dùng.
Bạn mô tả hệ phân cấp thành phần hiển thị (view hierarchy) của các thành phần giao diện người dùng trên màn hình. Ví dụ: một ConstraintLayout
(mẹ) có thể chứa Buttons
, TextViews
, ImageViews
hoặc các thành phần hiển thị (view) khác (con). Hãy nhớ rằng ConstraintLayout
là một lớp con của ViewGroup
. Hệ phân cấp này cho phép bạn định vị hoặc định kích thước khung nhìn con một cách linh hoạt.
Hệ phân cấp vùng chứa của một ứng dụng Android
Mỗi thành phần giao diện người dùng được biểu thị bằng một phần tử XML trong tệp XML. Mỗi phần tử bắt đầu và kết thúc bằng một thẻ, trong đó mỗi thẻ bắt đầu bằng một <
và kết thúc bằng >
. Tương tự như cách bạn thiết lập thuộc tính trên các thành phần giao diện người dùng bằng Layout Editor (design) (Trình chỉnh sửa bố cục (thiết kế)), các phần tử XML cũng có thuộc tính của mình. Một cách đơn giản, mã XML cho các thành phần trên giao diện người dùng nêu trên có thể có dạng như sau:
<ConstraintLayout>
<TextView
text="Hello World!">
</TextView>
</ConstraintLayout>
Hãy xem một ví dụ thực tế.
- Mở
activity_main.xml
(res > layout > activity_main.xml). - Bạn có thể thấy ứng dụng hiện một
TextView
với dòng chữ "Hello World!" (Xin chào mọi người) trong mộtConstraintLayout
, như đã thấy trong các dự án được tạo dựa trên mẫu này trước đây.
- Tìm tuỳ chọn cho các khung hiển thị Code (Mã), Split (Phân tách) và Design (Thiết kế) ở phía trên bên phải của Layout Editor.
- Chọn chế độ xem Code (Mã).
Mã XML trong activity_main.xml
sẽ hiển thị như sau:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Có rất nhiều thứ khác diễn ra bên ngoài ví dụ đơn giản này, nhưng Android Studio sẽ thực hiện một số bước để giúp mã XML dễ đọc hơn, tương tự như đã thực hiện với mã Kotlin của bạn.
- Hãy chú ý phần thụt đầu dòng. Android Studio sẽ tự động thực hiện việc này để hiển thị hệ phân cấp các phần tử.
TextView
được thụt đầu dòng vì nằm trongConstraintLayout
.ConstraintLayout
là phần tử mẹ vàTextView
là phần tử con. Các thuộc tính cho mỗi phần tử đều được thụt đầu dòng, cho biết những thuộc tính này thuộc về phần tử đó. - Hãy lưu ý đến việc lập trình màu sắc—một số đối tượng có màu xanh dương, một số có màu xanh lục, v.v. Các phần tương tự của tệp sẽ được vẽ cùng một màu để giúp bạn kết hợp các phần đó với nhau. Đặc biệt, hãy lưu ý rằng Android Studio sẽ vẽ phần đầu và phần cuối của các thẻ phần tử có cùng màu. (Lưu ý: màu sắc sử dụng trong lớp học lập trình có thể không khớp với màu bạn thấy trong Android Studio).
Thẻ, phần tử và thuộc tính XML
Dưới đây là phiên bản đơn giản của phần tử TextView
, bạn có thể xem một số phần quan trọng:
<TextView
android:text="Hello World!"
/>
Dòng chứa <TextView
là phần mở đầu thẻ, và dòng có />
là phần kết thúc thẻ. Dòng chứa android:text="Hello World!"
là thuộc tính của thẻ. Thuộc tính này thể hiện rằng văn bản sẽ được hiển thị trên TextView
. 3 dòng này là cách viết tắt thường dùng, được gọi là thẻ phần tử rỗng. Cách viết này cũng tương tự như khi bạn sử dụng một thẻ mở và một thẻ đóng riêng biệt, như sau:
<TextView
android:text="Hello World!"
></TextView>
Thông thường, thẻ phần tử rỗng sẽ được viết với số dòng càng ít càng tốt và phần thẻ đóng thường được ghép liền vào cuối dòng trước đó. Vì vậy, bạn có thể thấy thẻ phần tử rỗng được viết trên hai dòng (hoặc thậm chí một dòng nếu thẻ không có thuộc tính nào):
<!-- with attributes, two lines -->
<TextView
android:text="Hello World!" />
Phần tử ConstraintLayout
được viết với thẻ mở và thẻ đóng riêng biệt, vì phần tử này có thể chứa các phần tử khác bên trong. Dưới đây là phiên bản đơn giản của phần tử ConstraintLayout
có chứa phần tử TextView
bên trong:
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:text="Hello World!" />
</androidx.constraintlayout.widget.ConstraintLayout>
Nếu bạn muốn thêm một View
khác làm phần tử con của ConstraintLayout
, chẳng hạn như thêm một Button
bên dưới TextView
, thì phần tử này sẽ nằm sau thẻ đóng />
của TextView
và trước thẻ đóng của ConstraintLayout
, thể hiện như dưới đây:
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:text="Hello World!" />
<Button
android:text="Calculate" />
</androidx.constraintlayout.widget.ConstraintLayout>
Tìm hiểu thêm về sử dụng XML cho bố cục
- Có thể thấy thẻ của
ConstraintLayout
làandroidx.constraintlayout.widget.ConstraintLayout
thay vìConstraintLayout
nhưTextView
. Điều này là doConstraintLayout
là một phần của Android Jetpack, chứa các thư viện mã nhằm cung cấp các chức năng bổ sung cho nền tảng Android cốt lõi. Jetpack cung cấp các chức năng hữu ích, cho phép bạn xây dựng ứng dụng dễ dàng hơn. Bạn sẽ nhận ra thành phần giao diện người dùng này là một phần của Jetpack vì thành phần này bắt đầu bằng "androidx". - Bạn có thể đã thấy các dòng lệnh bắt đầu bằng
xmlns:
, theo sau làandroid
,app
vàtools
.
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns
là viết tắt của không gian tên XML, và mỗi dòng định nghĩa một lược đồ, hoặc từ vựng dành cho các thuộc tính liên quan đến các từ đó. Ví dụ: không gian tên android:
đánh dấu các thuộc tính do hệ thống Android định nghĩa. Tất cả thuộc tính trong XML bố cục đều bắt đầu bằng một trong các không gian tên đó.
- Khoảng trắng giữa các phần tử XML không mang nhiều ý nghĩa đối với một máy tính, nhưng có thể giúp XML trở nên dễ đọc hơn.
Android Studio sẽ tự động thêm một số khoảng trắng và thụt đầu dòng để XML dễ đọc hơn. Bạn sẽ tìm hiểu sau về cách thức để yêu cầu Android Studio đảm bảo XML của bạn tuân theo các quy ước lập trình.
- Bạn có thể thêm chú thích vào XML, tương tự như cách bạn thực hiện với mã Kotlin. Bắt đầu với
<!--
và kết thúc bằng-->
.
<!-- this is a comment in XML -->
<!-- this is a
multi-line
Comment.
And another
Multi-line comment -->
- Lưu ý dòng đầu tiên của tệp:
<?xml version="1.0" encoding="utf-8"?>
Dòng này cho biết đây là một tệp XML, nhưng không phải tệp XML nào cũng chứa dòng này.
4. Xây dựng bố cục trong XML
- Vẫn trong tệp
activity_main.xml
, hãy chuyển sang chế độ xem màn hình Split (Phân tách) để xem mã XML bên cạnh Design Editor (Trình chỉnh sửa thiết kế). Design Editor (Trình chỉnh sửa thiết kế) cho phép bạn xem trước bố cục giao diện người dùng.
- Bạn có thể chọn khung hiển thị theo sở thích cá nhân. Tuy nhiên, đối với lớp học lập trình này, hãy sử dụng khung hiển thị Split (Phân tách) để có thể thấy cả phần XML bạn chỉnh sửa và tác động của những chỉnh sửa đó trong Design Editor (Trình chỉnh sửa thiết kế).
- Hãy thử nhấp vào các dòng lệnh khác nhau, dòng bên dưới
ConstraintLayout
và tiếp đó là dòng bên dướiTextView
. Bạn sẽ thấy rằng thành phần hiển thị tương ứng sẽ được chọn trong chế độ xem Design Editor (Trình chỉnh sửa thiết kế). Kết quả sẽ tương tự khi làm điều ngược lại, ví dụ: nếu bạn nhấp vàoTextView
trong Design Editor (Trình chỉnh sửa thiết kế), đoạn mã XML tương ứng được làm nổi bật.
Xoá TextView
- Bây giờ bạn không cần đến
TextView
nữa nên có thể xoá thành phần hiển thị này. Hãy nhớ xoá toàn bộ nội dung từ thẻ mở<TextView
đến hết thẻ đóng/>
.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Phần nội dung còn lại trong tệp là về ConstraintLayout
:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
- Thêm khoảng đệm 16 dp vào
ConstraintLayout
để giao diện người dùng không bị tràn vào cạnh màn hình.
Khoảng đệm cũng tương tự như lề trang nhưng sẽ bổ sung thêm khoảng trống vào bên trong ConstraintLayout
, thay vì thêm khoảng trống ở bên ngoài.
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
Thêm trường văn bản phí dịch vụ
Ở bước này, bạn sẽ thêm thành phần giao diện người dùng để cho phép người dùng nhập phí dịch vụ vào ứng dụng. Bạn sẽ sử dụng phần tử EditText
để cho phép người dùng nhập hoặc chỉnh sửa văn bản trong ứng dụng.
- Xem tài liệu về
EditText
rồi kiểm tra XML của mẫu. - Tìm một khoảng trống giữa các thẻ mở và đóng của
ConstraintLayout
. - Sao chép và dán XML trong tài liệu này vào khoảng trống đó trong bố cục của bạn trên Android Studio.
Tệp bố cục của bạn sẽ có dạng như sau:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="text"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Có thể bạn chưa hiểu rõ toàn bộ nội dung này. Các bước sau sẽ giải thích chi tiết giúp bạn.
- Chú ý rằng
EditText
đang được gạch chân màu đỏ. - Di chuột qua đó bạn sẽ thấy lỗi "thành phần hiển thị chưa được ràng buộc". Các lỗi này bạn đã quen thuộc trong các lớp học lập trình trước đó. Hãy nhớ, cần thiết lập các điều kiện ràng buộc lên thành phần con của
ConstraintLayout
để bố cục biết cách sắp xếp các thành phần này.
- Thêm các điều kiện ràng buộc sau đây vào
EditText
để neo thành phần hiển thị này vào góc trên cùng bên trái của thành phần cha.
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
Nếu bạn đang viết bằng tiếng Anh hoặc một ngôn ngữ khác được viết từ trái sang phải (LTR), thì cạnh bắt đầu là phía bên trái. Nhưng một số ngôn ngữ như tiếng Ả Rập được viết từ phải sang trái (RTL). Vì thế, cạnh bắt đầu là phía bên phải. Đó là lý do tại sao điều kiện ràng buộc sử dụng từ "bắt đầu" để có thể phù hợp với các ngôn ngữ LTR hoặc RTL. Tương tự, các điều kiện ràng buộc sử dụng từ "kết thúc" thay vì bên phải.
Khi thêm vào các điều kiện ràng buộc mới, phần tử EditText
sẽ có dạng như sau:
<EditText
android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:inputType="text"/>
Xem lại các thuộc tính EditText
Kiểm tra cẩn thận tất cả thuộc tính EditText
bạn đã dán vào nhằm đảm bảo những thuộc tính này sẽ hoạt động đúng như dự kiến trong ứng dụng của bạn.
- Tìm thuộc tính
id
, đang được gán thành@+id/plain_text_input
. - Hãy thay đổi tên thuộc tính
id
thành tên phù hợp hơn,@+id/cost_of_service
.
- Xem thuộc tính
layout_height
. Thuộc tính này được thiết lập thànhwrap_content
, nghĩa là chiều cao sẽ cao bằng phần nội dung bên trong. Điều này sẽ không có vấn đề gì vì chỉ có 1 dòng văn bản. - Xem thuộc tính
layout_width
. Thuộc tính này được thiết lập thànhmatch_parent
, nhưng bạn không thể thiết lậpmatch_parent
trên thành phần con củaConstraintLayout
. Hơn nữa, trường văn bản không cần hiển thị quá rộng. Hãy thiết lập chiều rộng cố định cho trường văn bản là160dp
, như thế sẽ có nhiều không gian cho người dùng nhập phí dịch vụ.
- Hãy lưu ý thuộc tính mới
inputType
. Giá trị của thuộc tính này là"text"
, nghĩa là người dùng có thể nhập bất kỳ ký tự văn bản nào vào trường đó trên màn hình (ký tự bảng chữ cái, ký hiệu, v.v.)
android:inputType="text"
Tuy nhiên, bạn chỉ muốn người dùng nhập ký tự số vào EditText
vì trường này thể hiện giá trị tiền tệ.
- Xoá từ
text
nhưng để lại dấu ngoặc kép. - Bắt đầu nhập
number
vào đúng vị trí của nó. Sau khi nhập "n", Android Studio sẽ hiển thị một danh sách các nội dung có thể điền vào, trong đó có chứa ký tự "n".
- Chọn
numberDecimal
để yêu cầu người dùng nhập vào chữ số thập phân.
android:inputType="numberDecimal"
Để xem các tuỳ chọn khác cho các kiểu dữ liệu nhập vào, hãy tham khảo Chỉ định kiểu phương thức nhập trong tài liệu dành cho nhà phát triển.
Sẽ rất hữu ích khi hiển thị cho người dùng một số gợi ý về nội dung cần nhập vào trường này. Bạn sẽ cần thay đổi thêm một chút như bên dưới.
- Thêm thuộc tính
hint
vàoEditText
để mô tả những nội dung người dùng nên nhập cho trường này.
android:hint="Cost of Service"
Bạn cũng sẽ thấy phần thay đổi được cập nhật trong chế độ xem Design Editor (Trình chỉnh sửa thiết kế).
- Chạy ứng dụng của bạn trong trình mô phỏng. Ứng dụng sẽ hiển thị như sau:
Bạn làm tốt lắm! Mặc dù chưa làm gì nhiều nhưng bạn đã có một khởi đầu tốt đẹp và đã chỉnh sửa một số thành phần trong XML. Mã XML cho bố cục của bạn sẽ có dạng như sau.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/cost_of_service"
android:layout_width="160dp"
android:layout_height="wrap_content"
android:hint="Cost of Service"
android:inputType="numberDecimal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Thêm câu hỏi về chất lượng dịch vụ
Ở bước này, bạn sẽ thêm một TextView
cho phần câu hỏi "Bạn đánh giá thế nào về chất lượng dịch vụ?" Hãy thử nhập thành phần hiển thị này trong XML mà không cần sao chép/dán. Android Studio sẽ hiển thị các đề xuất giúp bạn.
- Sau khi đóng thẻ
EditText
,/>
, hãy thêm một dòng mới và bắt đầu nhập<TextView
- Chọn
TextView
trong số các đề xuất và Android Studio sẽ tự động thêm thuộc tínhlayout_width
vàlayout_height
choTextView
này. - Chọn
wrap_content
cho cả hai vì bạn chỉ cầnTextView
lớn bằng phần nội dung văn bản bên trong. - Thêm thuộc tính
text
bằng"How was the service?"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="How was the service?"
- Đóng thẻ bằng
/>
. - Lưu ý trong Design Editor (Trình chỉnh sửa thiết kế),
TextView
sẽ chồng chéo vớiEditText
.
Điều này có vẻ không ổn, vì vậy bạn sẽ thêm các điều kiện ràng buộc vào TextView
trong bước tiếp theo. Hãy suy nghĩ về những ràng buộc bạn cần thực hiện. TextView
nên được đặt theo chiều ngang và chiều dọc ở đâu? Bạn có thể kiểm tra ảnh chụp màn hình của ứng dụng để tham khảo.
Theo chiều dọc, bạn muốn TextView
đặt bên dưới trường văn bản về chi phí dịch vụ. Theo chiều ngang, bạn muốn TextView
được căn chỉnh theo cạnh bắt đầu của thành phần mẹ.
- Thêm một điều kiện ràng buộc theo chiều ngang vào
TextView
để ràng buộc cạnh bắt đầu của TextView này với cạnh bắt đầu của thành phần mẹ.
app:layout_constraintStart_toStartOf="parent"
- Thêm một điều kiện ràng theo chiều dọc vào
TextView
để ràng buộc cạnh trên củaTextView
với cạnh dưới cùng của thành phần hiển thị chi phí dịch vụView
.
app:layout_constraintTop_toBottomOf="@id/cost_of_service"
Lưu ý rằng không có dấu cộng trong @id/cost_of_service
vì mã nhận dạng đã được định nghĩa.
Tại thời điểm này, ứng dụng vẫn còn rất sơ khai nhưng bạn đừng lo lắng về điều đó. Chỉ cần đảm bảo tất cả thông tin cần thiết đều hiển thị trên màn hình và các chức năng đều hoạt động. Bạn sẽ khắc phục phần nhìn của ứng dụng trong các lớp học lập trình sau.
- Thêm mã nhận dạng tài nguyên vào
TextView
. Bạn sẽ cần tham chiếu đến thành phần hiển thị này về sau khi bổ sung các thành phần hiển thị khác cũng như các điều kiện ràng buộc trên các thành phần hiển thị này.
android:id="@+id/service_question"
Tại thời điểm này, tệp XML của bạn sẽ như bên dưới.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/cost_of_service"
android:hint="Cost of Service"
android:layout_height="wrap_content"
android:layout_width="160dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:inputType="numberDecimal"/>
<TextView
android:id="@+id/service_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="How was the service?"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cost_of_service"/>
</androidx.constraintlayout.widget.ConstraintLayout>
5. Thêm các tuỳ chọn tính tiền boa
Tiếp theo, bạn sẽ thêm các nút chọn cho phép người dùng lựa chọn tỷ lệ tính tiền boa khác nhau.
Bạn cần có 3 tuỳ chọn:
- Amazing (Tuyệt vời) (20%)
- Good (Tốt) (18%)
- Okay (Ổn) (15%)
Nếu không biết chắc phải thêm các nút chọn này như thế nào, bạn có thể tìm kiếm trên Google. Đây là một công cụ hỗ trợ tuyệt vời dành cho nhà phát triển nếu gặp bất kỳ khó khăn nào.
- Tìm kiếm chuỗi
radio button android
trên Google. Kết quả hiển thị hàng đầu là một hướng dẫn trong trang web dành cho nhà phát triển Android về cách sử dụng nút chọn — thật tuyệt vời!
- Đọc qua hướng dẫn về Nút chọn.
Trong nội dung mô tả, bạn có thể sử dụng thành phần giao diện người dùng RadioButton
trong bố cục của mình cho mỗi nút chọn bạn cần. Ngoài ra, người dùng chỉ được phép chọn một tuỳ chọn cho mỗi lần thực hiện, bạn cần nhóm các nút chọn trong một RadioGroup
.
Một số thành phần XML có thể giúp bạn đáp ứng nhu cầu này. Hãy đọc kỹ và xem cách thiết lập RadioGroup
là thành phần hiển thị mẹ và chứa thành phần hiển thị con là RadioButtons
.
- Quay lại với phần bố cục trong Android Studio để thêm
RadioGroup
vàRadioButton
vào ứng dụng. - Sau phần tử
TextView
nhưng vẫn ở trongConstraintLayout
, hãy bắt đầu nhập<RadioGroup
. Android Studio sẽ cung cấp các đề xuất hữu ích, giúp bạn hoàn thành mã XML của mình. - Đặt
layout_width
vàlayout_height
củaRadioGroup
thànhwrap_content
. - Thêm tập mã nhận dạng tài nguyên vào
@+id/tip_options
. - Đóng thẻ mở bằng
>
. - Android Studio sẽ thêm
</RadioGroup>
. Tương tự nhưConstraintLayout
, thành phầnRadioGroup
cũng chứa các thành phần khác bên trong. Vì vậy, bạn nên di chuyển thành phần này vào một dòng riêng. - Thiết lập ràng buộc để
RadioGroup
nằm bên dưới câu hỏi về chất lượng dịch vụ (theo chiều dọc) và đến cạnh bắt đầu của thành phần mẹ (theo chiều ngang). - Đặt thuộc tính
android:orientation
thànhvertical
. Nếu muốnRadioButtons
hiển thị trên một dòng, bạn nên thiết lập thuộc tính này thànhhorizontal
.
XML cho RadioGroup
sẽ có dạng như sau:
<RadioGroup
android:id="@+id/tip_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/service_question">
</RadioGroup>
Thêm nút chọn
- Sau thuộc tính cuối cùng của
RadioGroup
, nhưng trước thẻ đóng</RadioGroup>
, hãy thêm mộtRadioButton
.
<RadioGroup
android:id="@+id/tip_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/service_question">
<!-- add RadioButtons here -->
</RadioGroup>
- Đặt
layout_width
vàlayout_height
thànhwrap_content
. - Gán mã nhận dạng tài nguyên cho
RadioButton
là@+id/option_twenty_percent
. - Đặt phần văn bản thành
Amazing (20%)
. - Đóng thẻ bằng
/>
.
<RadioGroup
android:id="@+id/tip_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/service_question"
app:layout_constraintStart_toStartOf="parent"
android:orientation="vertical">
<RadioButton
android:id="@+id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Amazing (20%)" />
</RadioGroup>
Bây giờ khi đã thêm một RadioButton
, bạn có thể sửa đổi XML để thêm 2 nút chọn nữa cho các tuỳ chọn Good (18%)
và Okay (15%)
không?
XML của RadioGroup
và RadioButtons
sẽ hiển thị như sau:
<RadioGroup
android:id="@+id/tip_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/service_question"
app:layout_constraintStart_toStartOf="parent"
android:orientation="vertical">
<RadioButton
android:id="@+id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Amazing (20%)" />
<RadioButton
android:id="@+id/option_eighteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Good (18%)" />
<RadioButton
android:id="@+id/option_fifteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Okay (15%)" />
</RadioGroup>
Thêm lựa chọn mặc định
Hiện tại, chưa có tuỳ chọn tính tiền boa nào được chọn. Tốt nhất bạn nên thiết lập một trong các nút chọn làm lựa chọn mặc định.
RadioGroup
có một thuộc tính cho phép bạn chỉ định nút chọn nào sẽ được đánh dấu khi khởi tạo. Đó là thuộc tính checkedButton
và bạn sẽ thiết lập thuộc tính này thành mã nhận dạng tài nguyên cho nút chọn mặc định bạn muốn.
- Trên
RadioGroup
này, hãy đặt thuộc tínhandroid:checkedButton
thành@id/option_twenty_percent
.
<RadioGroup
android:id="@+id/tip_options"
android:checkedButton="@id/option_twenty_percent"
...
Chú ý, phần bố cục trong Design Editor (Trình chỉnh sửa thiết kế) đã được cập nhật. Tuỳ chọn tính tiền boa 20% được thiết lập thành lựa chọn mặc định — thật tuyệt! Bây giờ, ứng dụng đã bắt đầu giống một máy tính tiền boa!
Đây là giao diện của XML cho đến hiện tại:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/cost_of_service"
android:hint="Cost of Service"
android:layout_height="wrap_content"
android:layout_width="160dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:inputType="numberDecimal"/>
<TextView
android:id="@+id/service_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="How was the service?"
app:layout_constraintTop_toBottomOf="@id/cost_of_service"
app:layout_constraintStart_toStartOf="parent" />
<RadioGroup
android:id="@+id/tip_options"
android:checkedButton="@id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/service_question"
app:layout_constraintStart_toStartOf="parent"
android:orientation="vertical">
<RadioButton
android:id="@+id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Amazing (20%)" />
<RadioButton
android:id="@+id/option_eighteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Good (18%)" />
<RadioButton
android:id="@+id/option_fifteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Okay (15%)" />
</RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>
6. Hoàn thành những phần còn lại của bố cục
Hiện tại, bạn đang hoàn thiện phần cuối cùng của bố cục. Bạn sẽ thêm Switch
, Button
và TextView
để hiển thị số tiền boa.
Thêm thành phần Switch để làm tròn số tiền boa
Tiếp theo, bạn sẽ dùng tiện ích Switch
để cho phép người dùng chọn làm tròn hoặc không làm tròn số tiền boa.
Bạn muốn Switch
này rộng bằng với thành phần mẹ. Bạn có thể cho rằng nên thiết lập chiều rộng này thành match_parent
. Như đã lưu ý trước đó, bạn không thể thiết lập giá trị thuộc tính cho các thành phần giao diện người dùng trong ConstraintLayout
thành match_parent
. Thay vào đó, bạn cần ràng buộc cạnh bắt đầu và cạnh kết thúc của thành phần hiển thị này, đồng thời đặt chiều rộng thành 0dp
. Khi đặt chiều rộng thành 0dp
, hệ thống sẽ không tính chiều rộng này mà chỉ cần đáp ứng các điều kiện ràng buộc đang thiết lập trên thành phần hiển thị này.
- Thêm phần tử
Switch
sau phần XML choRadioGroup
. - Như đã lưu ý ở trên, hãy đặt
layout_width
thành0dp
- Đặt
layout_height
thànhwrap_content
. Điều này sẽ làm cho thành phần hiển thịSwitch
cao bằng phần nội dung bên trong. - Đặt thuộc tính
id
thành@+id/round_up_switch
. - Đặt thuộc tính
text
thànhRound up tip?
. Giá trị này sẽ được dùng làm nhãn choSwitch
. - Cố định cạnh bắt đầu của
Switch
với cạnh bắt đầu củatip_options
và cạnh kết thúc của nút chuyển này với cạnh kết thúc của thành phần mẹ. - Cố định phần đỉnh của
Switch
với phần đáy củatip_options
. - Đóng thẻ bằng
/>
.
Sẽ tốt hơn nếu nút chuyển này được bật theo mặc định. Bạn có thể sử dụng thuộc tính android:checked
để làm việc này, trong đó các giá trị có thể sẽ là true
(bật) hoặc false
(tắt).
- Đặt thuộc tính
android:checked
thànhtrue
.
Kết hợp tất cả lại với nhau, mã XML cho phần tử Switch
sẽ có dạng như sau:
<Switch
android:id="@+id/round_up_switch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:checked="true"
android:text="Round up tip?"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/tip_options"
app:layout_constraintTop_toBottomOf="@id/tip_options" />
Thêm nút Tính tiền
Tiếp theo, bạn sẽ thêm một Button
để người dùng có thể yêu cầu tính tiền boa. Bạn muốn nút này rộng bằng kích thước thành phần mẹ. Vì vậy, các điều kiện ràng buộc theo chiều ngang và chiều rộng sẽ giống như nút chuyển Switch
.
- Thêm một
Button
sauSwitch
. - Đặt chiều rộng thành
0dp
, như bạn đã làm choSwitch
. - Đặt chiều cao thành
wrap_content
. - Gán mã nhận dạng tài nguyên
@+id/calculate_button
cho nút này và thiết lập phần văn bản thành"Calculate"
. - Cố định cạnh trên của
Button
với cạnh dưới cùng của nút chuyển Round up tip? (Làm tròn tiền boa?)Switch
. - Cố định cạnh bắt đầu với cạnh bắt đầu của thành phần mẹ và cạnh kết thúc với cạnh kết thúc của thành phần mẹ.
- Đóng thẻ bằng
/>
.
Dưới đây là toàn bộ mã XML cho tính năng Button
Calculate (Tính tiền):
<Button
android:id="@+id/calculate_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Calculate"
app:layout_constraintTop_toBottomOf="@id/round_up_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
Thêm kết quả tính tiền boa
Bạn sắp hoàn thành phần bố cục ứng dụng! Trong bước này, bạn sẽ thêm một TextView
để hiển thị kết quả tính tiền boa. Đặt thành phần hiển thị này bên dưới nút Calculate (Tính tiền) và căn chỉnh dựa trên điểm kết thúc hay vì điểm bắt đầu như các thành phần giao diện người dùng khác.
- Thêm một
TextView
kèm theo mã nhận dạng tài nguyên có têntip_result
và phần văn bản tương ứng làTip Amount
. - Cố định cạnh kết thúc của
TextView
với cạnh kết thúc của thành phần mẹ. - Cố định cạnh trên cùng với cạnh đáy của nút Calculate (Tính tiền).
<TextView
android:id="@+id/tip_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/calculate_button"
android:text="Tip Amount" />
- Chạy ứng dụng. Ứng dụng này sẽ trông giống như ảnh chụp màn hình sau đây.
Thật tuyệt vời! Một trải nghiệm đặc biệt nếu đây là lần đầu tiên bạn làm việc với XML!
Lưu ý rằng ứng dụng có thể không giống hoàn toàn như ảnh chụp màn hình vì các mẫu thiết kế có thể đã thay đổi trong phiên bản Android Studio sau này. Nút Calculate (Tính tiền) hiện vẫn chưa có tác dụng gì. Tuy nhiên, bạn có thể nhập chi phí, chọn tỷ lệ phần trăm tiền boa và bật/tắt tuỳ chọn làm tròn tiền boa. Bạn sẽ bổ sung chức năng hoạt động cho nút Calculate (Tính tiền) trong lớp học lập trình tiếp theo. Chúng ta sẽ quay lại với nút này sau!
7. Áp dụng các phương pháp lập trình hay
Trích xuất các chuỗi
Bạn có thể nhìn thấy các cảnh báo về các chuỗi được mã hoá cứng. Hãy nhớ lại các lớp học lập trình trước đây khi bạn trích xuất các chuỗi sang một tệp tài nguyên. Việc này sẽ giúp bạn dễ dàng dịch ứng dụng sang các ngôn ngữ khác cũng như sử dụng lại chuỗi đó trong ứng dụng. Kiểm tra lại activity_main.xml
và trích xuất tất cả tài nguyên chuỗi.
- Nhấp vào một chuỗi; di chuột qua biểu tượng bóng đèn màu vàng xuất hiện, sau đó nhấp vào hình tam giác bên cạnh biểu tượng đó; chọn Extract String Resource (Trích xuất tài nguyên chuỗi). Có thể sử dụng tên mặc định cho các tài nguyên chuỗi. Nếu muốn, bạn có thể sử dụng các tên gợi tả hơn như
amazing_service
,good_service
vàok_service
cho các tuỳ chọn tiền boa.
Bây giờ, hãy kiểm tra các tài nguyên chuỗi bạn vừa thêm.
- Nếu cửa sổ Project (Dự án) không hiển thị, hãy nhấp vào thẻ Project (Dự án) ở phía bên trái cửa sổ.
- Mở app > res > values > strings.xml để xem tất cả tài nguyên chuỗi của giao diện người dùng.
<resources>
<string name="app_name">Tip Time</string>
<string name="cost_of_service">Cost of Service</string>
<string name="how_was_the_service">How was the service?</string>
<string name="amazing_service">Amazing (20%)</string>
<string name="good_service">Good (18%)</string>
<string name="ok_service">Okay (15%)</string>
<string name="round_up_tip">Round up tip?</string>
<string name="calculate">Calculate</string>
<string name="tip_amount">Tip Amount</string>
</resources>
Định dạng lại XML
Android Studio cung cấp nhiều công cụ giúp bạn dọn dẹp mã và đảm bảo mã của bạn tuân theo các quy ước lập trình được đề xuất.
- Trong
activity_main.xml
, chọn Edit > Select All (Chỉnh sửa > Chọn tất cả). - Chọn Code > Reformat Code (Mã > Định dạng lại mã).
Thao tác này sẽ đảm bảo căn lề nhất quán và có thể sắp xếp lại mã XML của các thành phần giao diện người dùng để gom nhóm mọi thứ với nhau. Chẳng hạn như đặt tất cả thuộc tính android:
của một phần tử lại với nhau.
8. Mã giải pháp
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/cost_of_service"
android:layout_width="160dp"
android:layout_height="wrap_content"
android:hint="@string/cost_of_service"
android:inputType="numberDecimal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/service_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/how_was_the_service"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cost_of_service" />
<RadioGroup
android:id="@+id/tip_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkedButton="@id/option_twenty_percent"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/service_question">
<RadioButton
android:id="@+id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/amazing_service" />
<RadioButton
android:id="@+id/option_eighteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/good_service" />
<RadioButton
android:id="@+id/option_fifteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok_service" />
</RadioGroup>
<Switch
android:id="@+id/round_up_switch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/round_up_tip"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/tip_options"
app:layout_constraintTop_toBottomOf="@id/tip_options" />
<Button
android:id="@+id/calculate_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/calculate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/round_up_switch" />
<TextView
android:id="@+id/tip_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tip_amount"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/calculate_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
res/values/strings.xml
<resources>
<string name="app_name">Tip Time</string>
<string name="cost_of_service">Cost of Service</string>
<string name="how_was_the_service">How was the service?</string>
<string name="amazing_service">Amazing (20%)</string>
<string name="good_service">Good (18%)</string>
<string name="ok_service">Okay (15%)</string>
<string name="round_up_tip">Round up tip?</string>
<string name="calculate">Calculate</string>
<string name="tip_amount">Tip Amount</string>
</resources>
9. Tóm tắt
- XML (Ngôn ngữ đánh dấu mở rộng) là cách sử dụng thẻ, phần tử và thuộc tính để sắp xếp văn bản trong một tệp.
- XML được dùng để định nghĩa bố cục cho ứng dụng Android.
EditText
cho phép người dùng nhập hoặc chỉnh sửa văn bản.EditText
dùng để gợi ý người dùng về thông tin có thể nhập cho trường đó.- Chỉ định thuộc tính
android:inputType
để giới hạn loại văn bản người dùng có thể nhập vào trườngEditText
. - Tạo một danh sách các tuỳ chọn loại trừ với
RadioButtons
, gom nhóm vớiRadioGroup
. RadioGroup
có thể bố trí theo chiều dọc hoặc chiều ngang và bạn có thể chỉ địnhRadioButton
nào làm tuỳ chọn mặc định.- Sử dụng
Switch
để cho phép người dùng chuyển đổi giữa hai tuỳ chọn. - Bạn có thể thêm nhãn vào
Switch
mà không cần sử dụngTextView
riêng. - Mỗi thành phần con của
ConstraintLayout
cần định nghĩa các điều kiện ràng buộc theo chiều dọc và chiều ngang. - Sử dụng các điều kiện ràng buộc "bắt đầu" và "kết thúc" để xử lý cả ngôn ngữ Trái sang Phải (LTR) và Phải sang Trái (RTL).
- Tên của các thuộc tính ràng buộc tuân theo mẫu định dạng
layout_constraint<Source>_to<Target>Of
. - Để tạo một
View
rộng bằng thành phần mẹConstraintLayout
, hãy thiết lập điều kiện ràng buộc điểm bắt đầu và kết thúc với điểm bắt đầu và kết thúc của thành phần mẹ, sau đó đặt chiều rộng thành 0dp.
10. Tìm hiểu thêm
Dưới đây là đường liên kết đến các tài liệu tham khảo thêm về những chủ đề đã đề cập. Bạn có thể tìm thấy tất cả tài liệu về Phát triển Android trên trang developer.android.com. Đừng quên rằng bạn có thể tìm kiếm thêm thông tin trên Google nếu gặp bất cứ sự cố gì.
11. Tự thực hành
Thực hiện những việc sau:
- Tạo một ứng dụng tính toán khác, chẳng hạn như chương trình chuyển đổi đơn vị nấu ăn, để chuyển đổi mililit sang ounce chất lỏng, gam sang cốc và ngược lại, v.v. Bạn sẽ cần những trường thông tin nào?