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, 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 dựng 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 không hiển thị xác định cấu trúc bố cục cho View và các đối tượng ViewGroup khác, như minh hoạ 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 một 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 một 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ư các lớp và lớp con 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 bản sao của 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
ViewvàViewGroup, đồng thời điều khiển 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. Vấn đề này sẽ được thảo luận thêm trong phần Hỗ trợ nhiều kích thước màn hình.
Khung Android giúp bạn sử dụng linh hoạt một trong hai hoặc cả hai phương thức này để tạo giao diện người dùng cho ứng dụng của mình. Ví dụ: bạn có thể khai báo các bố cục mặc định cho ứng dụng trong XML, sau đó chỉnh sửa bố cục trong thời gian chạy.
Ghi XML
Bằng cách 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 phần tử màn hình trong bố cục, giống như cách bạn tạo 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 phần tử gốc và 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 bổ sung làm các phần tử con để từng bước tạo một hệ phân cấp View xác định bố cục. Ví dụ: dưới đây là một bố cục XML sử dụng một LinearLayout dọc để giữ một 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ó phần mở rộng .xml trong thư mục res/layout/ của dự án Android để tệp này được 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 bài viết 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 đó bằng cách gọi setContentView(), chuyển tham chiếu đến tài nguyên bố cục ở dạng R.layout.layout_file_name. Ví dụ: nếu bố cục XML của bạn đượ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, hãy 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, mọi đối tượng View mở rộng lớp này cũng có thể kế thừa các thuộc tính này. Một số thuộc tính phổ biến với tất cả các đối tượng View vì những đối tượng này đượ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, là các thuộc tính mô tả một số hướng bố cục của đối tượng View, như được xác định bằng đối tượng ViewGroup gốc của đối tượng đó.
ID
Mọi đối tượng View đều có thể có một mã nhận dạng dưới dạng số nguyên liên kết với đối tượng đó để 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ã nhận dạng 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 đối với tất cả các đối tượng View và được xác định bằng lớp View. Bạn sử dụng ứng dụ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 sẽ 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. Ký hiệu 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 ký hiệu 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 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 thành phần hiển thị và tham chiếu chúng từ ứng dụng, bạn có thể sử dụng một mẫu chung như sau:
- Xác định một thành phần hiển thị trong tệp bố cục và chỉ định một mã nhận dạng duy nhất cho thành phần 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 phiên bản của đối tượng thành phần hiển thị và thu thập đối tượng đó 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 đố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 thành phần hiển thị đồng cấp có thể xác định bố cục của chúng so với một thành phần hiển thị đồng cấp khác, được tham chiếu theo mã nhận dạng duy nhất.
Mã nhận dạng không cần phải là duy nhất trong toàn bộ cây, nhưng phải là duy nhất trong phần của cây mà bạn tìm kiếm. Thường thì đây có thể là toàn bộ cây, vì vậy tốt nhất là bạn nên đặt mã nhận dạng duy nhất nếu có thể.
Tham số bố cục
Các thuộc tính bố cục XML có tên layout_something xác định các thông số bố cục cho View phù hợp với ViewGroup mà View nằm trong.
Mỗi lớp ViewGroup sẽ 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, tuỳ theo 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ị gốc xác định các thông 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ử gốc, mặc dù phần tử con này cũng có thể xác định một LayoutParams khác cho các phần tử con của nó.
Tất cả các nhóm thành phần hiển thị đều có chiều rộng và chiều cao, sử dụng layout_width và layout_height, đồng thời mỗi thành phần hiển thị đều phải xác định các thông số này. Nhiều LayoutParams có 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 có thể bạn không muốn thực hiện việc này thường xuyên. Thông thường, bạn sẽ 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 thành phần hiển thị điều chỉnh kích thước theo kích thước cần thiết cho nội dung.match_parent: yêu cầu thành phần hiển thị mở rộng bằng với kích thước mà nhóm thành phần hiển thị gốc cho phép.
Nhìn chung, bạn không nên chỉ định chiều rộng và chiều cao của bố cục bằng các đơn vị tuyệt đối như pixel. Cách tốt hơn là sử dụng các số đo 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ì cách này giúp ứng dụng của bạn 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 số đo được chấp nhận được xác định trong Tài nguyên bố cục.
Vị trí bố cục
Khung hiển thị có hình chữ nhật. Thành phần này có một vị trí, được biểu thị bằng một cặp toạ độ bên trái và trên cùng, cùng hai kích thước, được biểu thị bằ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 một 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 khung hiển thị. Phương thức thứ hai trả về toạ độ đỉnh (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ị so với thành phần hiển thị gốc. Ví dụ: khi getLeft() trả về 20, nghĩa là thành phần hiển thị này 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òn có các phương thức tiện lợi giúp 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 đại diện cho thành phần hiển thị. Ví dụ: lệnh gọi getRight() 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 một khung hiển thị được biểu thị bằng chiều rộng và chiều cao. Một thành phần 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 kích thước này xác định độ lớn mong muốn cho thành phần hiển thị trong phần tử mẹ. Bạn có thể lấy các kích thước đo được bằng cách gọi getMeasuredWidth() và getMeasuredHeight().
Cặp giá trị 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 phương diện này xác định kích thước thực tế của khung hiển thị trên màn hình, tại thời điểm vẽ và sau khi bố trí. Các giá trị này có thể khác với chiều rộng và chiều cao đo được, mặc dù không nhất thiết phải như vậy. 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 bằng 2 sẽ đẩy nội dung của thành phần hiển thị 2 pixel sang bên phải của cạnh trái. Bạn có thể đặt khoảng đệm bằng phương thức setPadding(int, int, int, int) và truy vấn khoảng đệm bằng cách gọi getPaddingLeft(), getPaddingTop(), getPaddingRight() và getPaddingBottom().
Mặc dù một thành phần hiển thị có thể xác định khoảng đệm, nhưng sẽ hoàn toàn 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ề phương diện, hãy xem phần Phương diện.
Ngoài việc đặt lề và khoảng đệm theo phương thức lập trình, bạn cũng có thể đặt các thuộc tính này trong bố cục XML, như minh hoạ 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ó khoảng lề và khoảng đệm đồng nhất được áp dụng ở mọi nơi, còn Button cho biết cách bạn có thể áp dụng chúng một cách độc lập 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 đó. ConstraintLayout là loại bố cục linh hoạt nhất và cung cấp các công cụ hiệu quả nhất để giữ cho hệ phân cấp bố cục của bạn ở mức thấp.
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 sổ vượt quá chiều dài màn hình.
Tạo danh sách linh hoạt
Khi nội dung cho bố cục của bạn là nội dung độ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 tốt 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ể có với RecyclerView và AdapterView:
RecyclerView cung cấp nhiều khả năng hơn và lựa chọn tạo một trình quản lý bố cục tuỳ chỉnh.
Điền dữ liệu vào thành phần hiển thị bộ chuyển đổi
Bạn có thể điền dữ liệu vào AdapterView như ListView hoặc GridView bằng cách liên kết bản sao AdapterView với Adapter. Bộ chuyển đổi này sẽ truy xuất dữ liệu từ một nguồn bên ngoài và tạo 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, cũng như 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,
ArrayAdaptertạo một thành phần hiển thị cho từng mục mảng bằng cách gọitoString()cho từng mục và đặt nội dung trong mộtTextView.Ví dụ: nếu bạn muốn hiển thị một mảng các chuỗi trong
ListView, hãy khởi chạy mộtArrayAdaptermới bằng một 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 là:
- Ứng dụng của bạn
Context - Bố cục chứa
TextViewcho mỗi chuỗi trong mảng - Mảng chuỗi
Sau đó, hãy gọi
setAdapter()trênListView: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 mỗi 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 một 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óImageViewcho từng mục mảng), hãy mở rộng lớpArrayAdaptervà ghi đègetView()để trả về loại thành phần hiển thị mà 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 trongCursorvà những cột trongCursormà bạn muốn chèn vào các 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 mọi người, bạn có thể thực hiện một truy vấn trả vềCursorchứa một hàng cho mỗi người và cột cho tên và số điện thoại. Sau đó, bạn tạo một mảng chuỗi xác định các cột trongCursormà bạn muốn có trong bố cục cho mỗi kết quả và một mảng số nguyên xác định các thành phần hiển thị tương ứng 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 bản sao của
SimpleCursorAdapter, hãy chuyển bố cục để sử dụng cho mỗi kết quả,Cursorchứa các kết quả và 2 mảng này: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 đó,
SimpleCursorAdaptersẽ tạo một thành phần hiển thị cho mỗi hàng trongCursorbằng bố cục đã cung cấp, bằng cách chèn mỗi mụcfromColumnsvào thành phần hiển thịtoViewstươ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 ra, hãy gọi notifyDataSetChanged().
Việ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à sẽ 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 của 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 bố cục được dùng trong ứng dụng minh hoạ Sunflower trên GitHub.


