Bố cục trong khung hiển thị
Bố cục xác định cấu trúc cho giao diện người dùng trong ứng dụng của bạn, chẳng hạn như trong một hoạt động. Tất cả các phần tử trong bố cục đều được tạo bằng một hệ phân cấp gồm các đối tượng View
và ViewGroup
. View
thường vẽ một nội dung mà người dùng có thể nhìn thấy và tương tác cùng. ViewGroup
là một vùng chứa ẩn giúp xác định cấu trúc bố cục cho View
và các đối tượng ViewGroup
khác, như trong hình 1.
Các đối tượng View
thường được gọi là tiện ích và có thể là một trong nhiều lớp con, chẳng hạn như Button
hoặc TextView
. Các đối tượng ViewGroup
thường được gọi là bố cục và có thể là một trong nhiều loại cung cấp cấu trúc bố cục khác, chẳng hạn như LinearLayout
hoặc ConstraintLayout
.
Bạn có thể khai báo một bố cục theo 2 cách:
- Khai báo các thành phần trên giao diện người dùng ở định dạng XML. Android cung cấp từ vựng XML đơn giản, tương ứng với các lớp và lớp con
View
, chẳng hạn như từ vựng cho tiện ích và bố cục. Bạn cũng có thể sử dụng Layout Editor của Android Studio để tạo bố cục XML bằng giao diện kéo và thả. - Tạo thực thể cho các phần tử bố cục trong thời gian chạy. Ứng dụng của bạn có thể tạo các đối tượng
View
vàViewGroup
cũng như chỉnh sửa các thuộc tính của các đối tượng đó theo phương thức lập trình.
Việc khai báo giao diện người dùng trong XML cho phép bạn tách riêng bản trình bày của ứng dụng khỏi mã kiểm soát hành vi của ứng dụng. Việc sử dụng tệp XML cũng giúp bạn dễ dàng cung cấp nhiều bố cục cho nhiều kích thước và hướng màn hình. Nội dung này sẽ được thảo luận kỹ hơn trong phần Hỗ trợ nhiều kích thước màn hình.
Khung Android cho phép bạn linh hoạt sử dụng một hoặc cả hai phương thức này để tạo giao diện người dùng cho ứng dụng. Ví dụ: bạn có thể khai báo bố cục mặc định của ứng dụng trong XML, sau đó sửa đổi bố cục trong thời gian chạy.
Ghi XML
Khi sử dụng từ vựng XML của Android, bạn có thể nhanh chóng thiết kế bố cục giao diện người dùng và các thành phần trên màn hình trong đó, giống như cách bạn tạo các trang web trong HTML với một loạt các phần tử lồng nhau.
Mỗi tệp bố cục phải chứa đúng một thành phần gốc, phải là đối tượng View
hoặc ViewGroup
. Sau khi xác định phần tử gốc, bạn có thể thêm các đối tượng hoặc tiện ích bố cục khác làm phần tử con để xây dựng dần hệ phân cấp View
xác định bố cục của bạn. Ví dụ: dưới đây là bố cục XML sử dụng LinearLayout
dọc để giữ TextView
và Button
:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, I am a TextView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, I am a Button" /> </LinearLayout>
Sau khi bạn khai báo bố cục trong XML, hãy lưu tệp có đuôi .xml
trong thư mục res/layout/
của dự án Android để tệp biên dịch đúng cách.
Để biết thêm thông tin về cú pháp cho tệp XML bố cục, hãy xem Tài nguyên bố cục.
Tải tài nguyên XML
Khi bạn biên dịch ứng dụng, mỗi tệp bố cục XML sẽ được biên dịch thành
tài nguyên View
. Tải tài nguyên bố cục trong quá trình triển khai lệnh gọi lại Activity.onCreate()
của ứng dụng. Hãy thực hiện việc này bằng cách gọi
setContentView()
,
chuyển tham chiếu đến tài nguyên bố cục của bạn ở dạng:
R.layout.layout_file_name
. Ví dụ: nếu bố cục XML
được lưu dưới dạng main_layout.xml
, hãy tải bố cục đó cho
Activity
như sau:
Kotlin
fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.main_layout) }
Java
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_layout); }
Khung Android gọi phương thức gọi lại onCreate()
trong Activity
khi Activity
khởi chạy. Để biết thêm thông tin về vòng đời hoạt động, vui lòng xem phần Giới thiệu về các hoạt động.
Thuộc tính
Mỗi đối tượng View
và ViewGroup
đều hỗ trợ nhiều thuộc tính XML riêng. Một số thuộc tính dành riêng cho đối tượng View
. Ví dụ: TextView
hỗ trợ thuộc tính textSize
. Tuy nhiên, các thuộc tính này cũng được đối tượng View
mở rộng của lớp này kế thừa. Có một số thuộc tính phổ biến cho tất cả các đối tượng View
vì chúng được kế thừa từ lớp View
gốc, chẳng hạn như thuộc tính id
. Các thuộc tính khác được coi là tham số bố cục. Đây là những thuộc tính mô tả một số hướng bố cục nhất định của đối tượng View
, như được đối tượng ViewGroup
mẹ của đối tượng đó xác định.
ID
Mọi đối tượng View
đều có thể liên kết với một mã nhận dạng số nguyên để
xác định duy nhất View
trong cây. Khi ứng dụng được biên dịch, mã nhận dạng này được tham chiếu dưới dạng số nguyên, nhưng mã này thường được chỉ định trong tệp XML bố cục dưới dạng một chuỗi trong thuộc tính id
. Đây là một thuộc tính XML phổ biến cho tất cả các đối tượng View
và thuộc tính này được xác định bằng lớp View
. Bạn sử dụng ngôn ngữ này rất thường xuyên. Cú pháp cho mã nhận dạng bên trong thẻ XML như sau:
android:id="@+id/my_button"
Ký hiệu at (@) ở đầu chuỗi cho biết rằng trình phân tích cú pháp XML có thể phân tích cú pháp và mở rộng phần còn lại của chuỗi mã nhận dạng, đồng thời xác định chuỗi đó là một tài nguyên mã nhận dạng. Biểu tượng dấu cộng (+) có nghĩa là đây là tên tài nguyên mới phải được tạo và thêm vào tài nguyên của bạn trong tệp R.java
.
Khung Android cung cấp nhiều tài nguyên mã nhận dạng khác. Khi tham chiếu mã nhận dạng tài nguyên Android, bạn không cần biểu tượng dấu cộng, nhưng bạn phải thêm không gian tên gói android
như sau:
android:id="@android:id/empty"
Không gian tên gói android
cho biết rằng bạn đang tham chiếu đến một mã nhận dạng từ lớp tài nguyên android.R
, thay vì lớp tài nguyên cục bộ.
Để tạo các khung hiển thị và tham chiếu các khung hiển thị đó từ ứng dụng, bạn có thể sử dụng một mẫu phổ biến như sau:
- Xác định một khung hiển thị trong tệp bố cục và chỉ định một mã nhận dạng duy nhất cho khung hiển thị đó, như trong ví dụ sau:
<Button android:id="@+id/my_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/my_button_text"/>
- Tạo một thực thể của đối tượng khung hiển thị và chụp thực thể đó từ bố cục, thường là trong phương thức
onCreate()
, như minh hoạ trong ví dụ sau:Kotlin
val myButton: Button = findViewById(R.id.my_button)
Java
Button myButton = (Button) findViewById(R.id.my_button);
Bạn cần xác định mã nhận dạng cho các đối tượng thành phần hiển thị khi tạo RelativeLayout
.
Trong một bố cục tương đối, các khung hiển thị đồng cấp có thể xác định bố cục so với một khung hiển thị đồng cấp khác được tham chiếu bằng mã nhận dạng duy nhất.
Mã nhận dạng không nhất thiết phải là giá trị duy nhất trong toàn bộ cây, nhưng phải là mã duy nhất trong phần cây mà bạn tìm kiếm. Thông thường, biến này có thể là toàn bộ cây, vì vậy, tốt nhất bạn nên tạo đặc điểm riêng biệt khi có thể.
Tham số bố cục
Các thuộc tính bố cục XML có tên là layout_something
sẽ xác định
các tham số bố cục cho View
phù hợp với
ViewGroup
chứa nó.
Mỗi lớp ViewGroup
triển khai một lớp lồng ghép mở rộng ViewGroup.LayoutParams
.
Lớp con này chứa các loại thuộc tính xác định kích thước và vị trí của mỗi
thành phần hiển thị con, phù hợp với nhóm thành phần hiển thị. Như minh hoạ trong hình 2, nhóm thành phần hiển thị mẹ xác định các tham số bố cục cho mỗi thành phần hiển thị con, bao gồm cả nhóm thành phần hiển thị con.
Mỗi lớp con LayoutParams
đều có cú pháp riêng để đặt
giá trị. Mỗi phần tử con phải xác định một LayoutParams
phù hợp với phần tử mẹ, mặc dù phần tử này cũng có thể xác định một LayoutParams
khác cho các phần tử con riêng.
Tất cả các nhóm khung hiển thị đều có chiều rộng và chiều cao, sử dụng layout_width
và layout_height
. Ngoài ra, mỗi thành phần hiển thị bắt buộc phải xác định các thông số này. Nhiều LayoutParams
bao gồm lề và đường viền không bắt buộc.
Bạn có thể chỉ định chiều rộng và chiều cao bằng các số đo chính xác, nhưng bạn có thể không muốn thực hiện việc này thường xuyên. Thường xuyên hơn, bạn sử dụng một trong các hằng số sau để đặt chiều rộng hoặc chiều cao:
wrap_content
: yêu cầu khung hiển thị tự định kích thước theo kích thước mà nội dung của khung hiển thị yêu cầu.match_parent
: yêu cầu thành phần hiển thị của bạn mở rộng bằng kích thước mà nhóm thành phần hiển thị mẹ cho phép.
Nói chung, bạn không nên chỉ định chiều rộng và chiều cao bố cục bằng cách sử dụng các đơn vị tuyệt đối như pixel. Một phương pháp hay hơn là sử dụng các thông tin đo lường tương đối, chẳng hạn như đơn vị pixel không phụ thuộc vào mật độ (dp), wrap_content
hoặc match_parent
, vì những thông tin này giúp ứng dụng hiển thị chính xác trên nhiều kích thước màn hình thiết bị. Các loại đo lường được chấp nhận được xác định trong mục Tài nguyên bố cục.
Vị trí bố cục
Một khung hiển thị có hình chữ nhật. Nó có vị trí, được biểu thị bằng một cặp toạ độ bên trái và trên cùng, và hai kích thước, được biểu thị dưới dạng chiều rộng và chiều cao. Đơn vị cho vị trí và kích thước là pixel.
Bạn có thể truy xuất vị trí của khung hiển thị bằng cách gọi các phương thức getLeft()
và getTop()
.
Phương thức thứ nhất trả về toạ độ bên trái (x) của hình chữ nhật đại diện cho thành phần hiển thị. Phần sau trả về toạ độ trên cùng (y) của hình chữ nhật đại diện cho khung hiển thị. Các phương thức này trả về vị trí của thành phần hiển thị tương ứng với thành phần hiển thị mẹ. Ví dụ: khi getLeft()
trả về 20, điều này có nghĩa là thành phần hiển thị có vị trí cách cạnh trái của thành phần hiển thị gốc 20 pixel về bên phải.
Ngoài ra, có các phương thức tiện lợi để tránh các phép tính không cần thiết: cụ thể là getRight()
và getBottom()
.
Các phương thức này trả về toạ độ của các cạnh bên phải và cạnh dưới cùng của hình chữ nhật biểu thị thành phần hiển thị. Ví dụ: việc gọi getRight()
sẽ tương tự như phép tính sau: getLeft() + getWidth()
.
Kích thước, khoảng đệm và lề
Kích thước của thành phần hiển thị được biểu thị bằng chiều rộng và chiều cao. Một khung hiển thị có hai cặp giá trị chiều rộng và chiều cao.
Cặp giá trị đầu tiên được gọi là chiều rộng đo được và chiều cao đo được. Các tham số này xác định kích thước mong muốn của thành phần hiển thị trong thành phần mẹ. Bạn có thể lấy các kích thước được đo lường bằng cách gọi
getMeasuredWidth()
và
getMeasuredHeight()
.
Cặp thứ hai được gọi là chiều rộng và chiều cao, hoặc đôi khi là chiều rộng được vẽ và chiều cao được vẽ. Các kích thước này xác định kích thước thực tế của thành phần hiển thị trên màn hình, tại thời điểm vẽ và sau khi tạo bố cục. Các giá trị này có thể khác nhưng không nhất thiết phải khác với chiều rộng và chiều cao đo được. Bạn có thể lấy chiều rộng và chiều cao bằng cách gọi getWidth()
và getHeight()
.
Để đo các kích thước, thành phần hiển thị sẽ tính đến khoảng đệm. Khoảng đệm được biểu thị bằng pixel cho các phần bên trái, trên cùng, bên phải và dưới cùng của khung hiển thị.
Bạn có thể sử dụng khoảng đệm để bù trừ nội dung của thành phần hiển thị theo một số lượng pixel cụ thể. Ví dụ: khoảng đệm bên trái gồm 2 pixel sẽ đẩy nội dung của khung hiển thị sang bên phải của cạnh trái 2 pixel. Bạn có thể đặt khoảng đệm bằng phương thức setPadding(int, int, int, int)
và truy vấn phương thức này bằng cách gọi getPaddingLeft()
, getPaddingTop()
, getPaddingRight()
và getPaddingBottom()
.
Mặc dù khung hiển thị có thể xác định khoảng đệm, nhưng khung hiển thị này sẽ không hỗ trợ lề. Tuy nhiên, nhóm thành phần hiển thị có hỗ trợ lề. Hãy xem ViewGroup
và ViewGroup.MarginLayoutParams
để biết thêm thông tin.
Để biết thêm thông tin về kích thước, hãy xem bài viết Kích thước.
Bên cạnh việc đặt lề và khoảng đệm theo phương thức lập trình, bạn cũng có thể thiết lập lề trong bố cục XML, như trong ví dụ sau:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:padding="8dp" android:text="Hello, I am a TextView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:paddingBottom="4dp" android:paddingEnd="8dp" android:paddingStart="8dp" android:paddingTop="4dp" android:text="Hello, I am a Button" /> </LinearLayout>
Ví dụ trước cho thấy lề và khoảng đệm đang được áp dụng. TextView
có lề và khoảng đệm đồng nhất được áp dụng xung quanh, còn Button
cho biết cách bạn có thể áp dụng độc lập các lề và khoảng đệm này cho các cạnh khác nhau.
Bố cục phổ biến
Mỗi lớp con của lớp ViewGroup
cung cấp một cách duy nhất để hiển thị các thành phần hiển thị mà bạn lồng trong đó. Loại bố cục linh hoạt nhất và cung cấp các công cụ tốt nhất để duy trì hệ phân cấp bố cục ở mức nông, là ConstraintLayout
.
Sau đây là một số loại bố cục phổ biến được tích hợp vào nền tảng Android.
Sắp xếp các phần tử con thành một hàng ngang hoặc dọc và tạo một thanh cuộn nếu chiều dài của cửa sổ vượt quá chiều dài màn hình.
Tạo danh sách động
Khi nội dung cho bố cục là động hoặc không được xác định trước, bạn có thể sử dụng RecyclerView
hoặc một lớp con của AdapterView
.
RecyclerView
thường là lựa chọn phù hợp hơn vì sử dụng bộ nhớ hiệu quả hơn AdapterView
.
Sau đây là các bố cục phổ biến có thể dùng với RecyclerView
và AdapterView
:
RecyclerView
cung cấp nhiều khả năng hơn và
lựa chọn tạo trình quản lý bố cục
tuỳ chỉnh.
Điền dữ liệu vào khung hiển thị bộ chuyển đổi
Bạn có thể điền sẵn AdapterView
chẳng hạn như ListView
hoặc GridView
bằng cách liên kết thực thể AdapterView
với một Adapter
. Tính năng này sẽ truy xuất dữ liệu từ một nguồn bên ngoài và tạo một View
đại diện cho từng mục dữ liệu.
Android cung cấp một số lớp con của Adapter
rất hữu ích trong việc truy xuất nhiều loại dữ liệu và tạo thành phần hiển thị cho AdapterView
. Hai bộ chuyển đổi phổ biến nhất là:
ArrayAdapter
- Hãy dùng bộ chuyển đổi này khi nguồn dữ liệu của bạn là một mảng. Theo mặc định,
ArrayAdapter
tạo một khung hiển thị cho từng mục mảng bằng cách gọitoString()
trên từng mục và đặt nội dung trongTextView
.Ví dụ: nếu bạn muốn hiển thị một mảng chuỗi trong
ListView
, hãy khởi chạy mộtArrayAdapter
mới bằng cách sử dụng hàm khởi tạo để chỉ định bố cục cho từng chuỗi và mảng chuỗi:Kotlin
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
Java
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray);
Các đối số cho hàm khởi tạo này như sau:
- Ứng dụng của bạn
Context
- Bố cục chứa
TextView
cho mỗi chuỗi trong mảng - Mảng chuỗi
Sau đó, hãy gọi
setAdapter()
trênListView
của bạn:Kotlin
val listView: ListView = findViewById(R.id.listview) listView.adapter = adapter
Java
ListView listView = (ListView) findViewById(R.id.listview); listView.setAdapter(adapter);
Để tuỳ chỉnh giao diện của từng mục, bạn có thể ghi đè phương thức
toString()
cho các đối tượng trong mảng. Hoặc để tạo thành phần hiển thị cho từng mục không phải làTextView
(ví dụ: nếu bạn muốn cóImageView
cho từng mục mảng), hãy mở rộng lớpArrayAdapter
và ghi đègetView()
để trả về loại thành phần hiển thị bạn muốn cho từng mục. - Ứng dụng của bạn
SimpleCursorAdapter
- Sử dụng bộ chuyển đổi này khi dữ liệu của bạn được lấy từ
Cursor
. Khi sử dụngSimpleCursorAdapter
, hãy chỉ định một bố cục để sử dụng cho mỗi hàng trongCursor
và cột nào trongCursor
mà bạn muốn chèn vào thành phần hiển thị của bố cục mà bạn muốn. Ví dụ: nếu muốn tạo danh sách tên và số điện thoại của người dùng, bạn có thể thực hiện truy vấn trả vềCursor
chứa một hàng cho từng người và cột cho tên và số điện thoại. Sau đó, bạn sẽ tạo một mảng chuỗi chỉ định những cột từCursor
bạn muốn có trong bố cục cho từng kết quả và một mảng số nguyên chỉ định các thành phần hiển thị tương ứng mà bạn cần đặt cho mỗi cột:Kotlin
val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER) val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
Java
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; int[] toViews = {R.id.display_name, R.id.phone_number};
Khi bạn tạo thực thể cho
SimpleCursorAdapter
, hãy truyền bố cục để sử dụng cho từng kết quả,Cursor
chứa kết quả và 2 mảng sau:Kotlin
val adapter = SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0) val listView = getListView() listView.adapter = adapter
Java
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0); ListView listView = getListView(); listView.setAdapter(adapter);
Sau đó,
SimpleCursorAdapter
sẽ tạo một khung hiển thị cho từng hàng trongCursor
bằng bố cục đã cung cấp bằng cách chèn từng mụcfromColumns
vào khung hiển thịtoViews
tương ứng.
Nếu trong quá trình hoạt động của ứng dụng, bạn thay đổi dữ liệu cơ bản mà bộ chuyển đổi đọc, hãy gọi notifyDataSetChanged()
.
Thao tác này sẽ thông báo cho thành phần hiển thị đính kèm rằng dữ liệu đã được thay đổi và tự làm mới.
Xử lý các sự kiện nhấp chuột
Bạn có thể phản hồi các sự kiện nhấp chuột trên từng mục trong AdapterView
bằng cách triển khai giao diện
AdapterView.OnItemClickListener
. Ví dụ:
Kotlin
listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> // Do something in response to the click. }
Java
// Create a message handling object as an anonymous class. private OnItemClickListener messageClickedHandler = new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { // Do something in response to the click. } }; listView.setOnItemClickListener(messageClickedHandler);
Tài nguyên khác
Xem cách sử dụng bố cục trong ứng dụng minh hoạ Sunflower trên GitHub.