Chúng tôi giải thích bản chất của RelativeLayout — một ViewGroup trong Android SDK để định vị các phần tử con tương đối với nhau hoặc với vùng chứa cha. Không giống như LinearLayout, sắp xếp các phần tử theo trình tự nghiêm ngặt, RelativeLayout cho phép bạn đặt bất kỳ phần tử nào ở bên trái, bên phải, phía trên, phía dưới hoặc căn giữa so với bất kỳ phần tử nào khác theo ID của nó. Điều này làm cho RelativeLayout thuận tiện cho các bố cục phức tạp và chồng chéo mà không cần lồng ghép. Các quy tắc cơ bản được mô tả trong tài liệu Android Developers.
Các điểm chính
RelativeLayout — một ViewGroup được giới thiệu trong Android SDK từ API Level 1, định vị các phần tử con thông qua các quy tắc khai báo (layout rules) so với cha hoặc các phần tử con khác. Mỗi quy tắc được đặt bằng một thuộc tính XML như android:layout_above, android:layout_below, android:layout_toLeftOf với giá trị ID của phần tử mục tiêu.
Trước khi RelativeLayout ra đời, các nhà phát triển phải sử dụng LinearLayout lồng nhau cho mọi định vị không chuẩn — điều này dẫn đến lồng ghép sâu và kết xuất chậm. RelativeLayout đã giải quyết vấn đề này bằng cách cung cấp định vị tương đối ở một cấp phân cấp duy nhất. Tuy nhiên, tính linh hoạt của nó bị hạn chế so với ConstraintLayout hiện đại: RelativeLayout không hỗ trợ chuỗi, rào cản, đường hướng dẫn hoặc kích thước theo phần trăm.
RelativeLayout có hai đặc điểm chính: một lần onMeasure (không giống như LinearLayout với weight, yêu cầu hai lần) và phụ thuộc vào thứ tự khai báo — nếu phần tử A tham chiếu đến phần tử B và B được khai báo sau A, thì B phải có kích thước đã biết tại thời điểm đo A. Theo Hướng dẫn dành cho nhà phát triển Android (2024), RelativeLayout thực hiện đo trong một lần nếu tất cả các quy tắc tham chiếu đến các phần tử đã được đo. Nếu không, cần có lần thứ hai, điều này làm giảm hiệu suất.
RelativeLayout phù hợp cho các bố cục đơn giản với 3–7 phần tử khi cần định vị tương đối (nhãn phía trên nút, biểu tượng bên phải văn bản, bảng điều khiển ở cuối màn hình). Đối với các dự án mới, ConstraintLayout được khuyến nghị, nhưng RelativeLayout vẫn được sử dụng rộng rãi trong mã kế thừa và thư viện.
RelativeLayout sử dụng hai loại quy tắc: quy tắc relative (định vị tương đối so với phần tử khác) và quy tắc align (neo vào cha). Mỗi phần tử con có thể có số lượng quy tắc không giới hạn, nhưng các quy tắc xung đột (ví dụ: alignParentTop và below của phần tử khác) được giải quyết có lợi cho thuộc tính được khai báo cuối cùng.
Quy tắc relative:
- android:layout_above="@id/target" — cạnh dưới của phần tử được neo vào cạnh trên của target.
- android:layout_below="@id/target" — cạnh trên của phần tử được neo vào cạnh dưới của target.
- android:layout_toLeftOf="@id/target" — cạnh phải được neo vào cạnh trái của target.
- android:layout_toRightOf="@id/target" — cạnh trái được neo vào cạnh phải của target.
- android:layout_toStartOf="@id/target" — tương tự toLeftOf cho các ngôn ngữ RTL.
- android:layout_toEndOf="@id/target" — tương tự toRightOf cho RTL.
Quy tắc align (với cha):
- android:layout_alignParentTop="true" — cạnh trên của phần tử trùng với cạnh trên của cha.
- android:layout_alignParentBottom="true" — cạnh dưới trùng với cạnh dưới của cha.
- android:layout_alignParentLeft="true" — cạnh trái trùng với cạnh trái của cha.
- android:layout_alignParentRight="true" — cạnh phải trùng với cạnh phải của cha.
- android:layout_alignParentStart và android:layout_alignParentEnd — cho RTL.
Quy tắc align với các phần tử khác:
- android:layout_alignTop="@id/target" — đỉnh của phần tử trùng với đỉnh của target.
- android:layout_alignBottom, android:layout_alignLeft, android:layout_alignRight, android:layout_alignStart, android:layout_alignEnd — tương tự.
- android:layout_alignBaseline="@id/target" — căn chỉnh theo đường cơ sở văn bản (hữu ích cho các chuỗi có kích thước phông chữ khác nhau).
Căn giữa trong RelativeLayout được thiết lập bởi ba thuộc tính: android:layout_centerInParent="true" (trung tâm của cha trên cả hai trục), android:layout_centerHorizontal="true" (căn giữa theo chiều ngang) và android:layout_centerVertical="true" (căn giữa theo chiều dọc). Căn giữa tương thích với các quy tắc khác: một phần tử có thể được căn giữa theo chiều ngang trong khi vẫn được neo vào cạnh dưới của cha.
Thứ tự Z (thứ tự chồng lớp) trong RelativeLayout được xác định bởi thứ tự khai báo phần tử trong XML. Phần tử được khai báo đầu tiên được vẽ trước (sẽ ở dưới), phần tử cuối cùng — cuối cùng (sẽ ở trên). Nếu bạn cần thay đổi thứ tự chồng lớp mà không thay đổi XML, hãy sử dụng android:elevation (từ API 21) — các phần tử có elevation cao hơn được vẽ lên trên. Đối với các phiên bản dưới API 21, thứ tự chồng lớp chỉ được kiểm soát bởi thứ tự khai báo.
Quan trọng: RelativeLayout không hỗ trợ thuộc tính android:elevation cho cha — mỗi phần tử tự kiểm soát elevation của mình. Khi các quy tắc định vị và elevation xung đột (ví dụ: các phần tử chồng chéo), elevation được ưu tiên cho kết xuất, nhưng không phải cho định vị — một phần tử có elevation cao hơn có thể chồng lên phần tử khác về mặt trực quan, ngay cả khi các quy tắc bố cục của nó đặt nó "phía sau".
Ảnh đại diện được căn giữa ở trên cùng, tên bên dưới ảnh đại diện, trạng thái bên dưới tên, nút hành động ở cuối màn hình. Tất cả các phần tử sử dụng quy tắc relative để neo tuần tự.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/avatar"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="32dp"
android:src="@drawable/avatar_placeholder" />
<TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/avatar"
android:layout_centerHorizontal="true"
android:layout_marginTop="12dp"
android:text="Ivan Ivanov"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/user_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/user_name"
android:layout_centerHorizontal="true"
android:text="Trực tuyến"
android:textColor="@color/green" />
<Button
android:id="@+id/edit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp"
android:text="Chỉnh sửa hồ sơ" />
</RelativeLayout>
Ảnh đại diện được neo vào phía trên qua marginTop + centerHorizontal, tên ở dưới ảnh đại diện qua layout_below, trạng thái ở dưới tên và nút được neo vào cạnh dưới của cha qua alignParentBottom. Tất cả các phần tử độc lập với nhau — không có chuỗi hoặc lồng ghép.
Một biểu tượng với huy hiệu màu đỏ (hình tròn có số) ở góc trên bên phải. Huy hiệu được định vị tương đối so với biểu tượng thông qua căn chỉnh với các cạnh của nó với lề âm để chồng chéo.
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_notification" />
<TextView
android:id="@+id/badge"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignTop="@+id/icon"
android:layout_alignRight="@+id/icon"
android:layout_marginTop="-6dp"
android:layout_marginRight="-6dp"
android:gravity="center"
android:background="@drawable/badge_background"
android:text="3"
android:textColor="@android:color/white"
android:textSize="10sp" />
</RelativeLayout>
Huy hiệu được neo vào các cạnh trên và phải của biểu tượng. Lề âm (-6dp) dịch chuyển huy hiệu ra ngoài, tạo ra hiệu ứng chồng lên đường viền của biểu tượng. Đây là một mẫu phổ biến cho thông báo, mà nếu không có RelativeLayout sẽ yêu cầu FrameLayout hoặc vẽ tùy chỉnh.
Biểu mẫu đăng nhập: logo được căn giữa ở trên cùng, trường email bên dưới, trường mật khẩu bên dưới email, nút "Đăng nhập" bên dưới mật khẩu, liên kết "Đăng ký" được neo ở dưới cùng. Tất cả các phần tử được neo tuần tự qua layout_below.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="@+id/logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="48dp"
android:text="MyApp"
android:textSize="32sp"
android:textStyle="bold" />
<EditText
android:id="@+id/email_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/logo"
android:layout_marginTop="32dp"
android:hint="Email" />
<EditText
android:id="@+id/password_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/email_input"
android:layout_marginTop="16dp"
android:hint="Mật khẩu"
android:inputType="textPassword" />
<Button
android:id="@+id/login_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/password_input"
android:layout_marginTop="24dp"
android:text="Đăng nhập" />
<TextView
android:id="@+id/register_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:text="Chưa có tài khoản? Đăng ký"
android:textColor="@color/primary" />
</RelativeLayout>
Màn hình sử dụng 5 phần tử ở một cấp phân cấp duy nhất. Luồng từ trên xuống dưới (logo → email → mật khẩu → nút) được thực hiện qua layout_below và liên kết được đẩy xuống dưới qua alignParentBottom. Để có cùng chức năng, LinearLayout sẽ yêu cầu hai vùng chứa lồng nhau (một cho nhóm trên, một cho liên kết với weight).
So sánh hai ViewGroup cho định vị tương đối: RelativeLayout (cổ điển, API 1+) và ConstraintLayout (hiện đại, Jetpack). Tiêu chí: tính linh hoạt, hiệu suất, hỗ trợ RTL và khả năng thích ứng.
| Đặc điểm | RelativeLayout | ConstraintLayout |
|---|---|---|
| Kích thước phần trăm | Không (chỉ cố định và match_parent) | Có (width_percent, height_percent, guideline_percent) |
| Hỗ trợ RTL | Qua start/end (API 17+) | Tích hợp sẵn |
| Chain / chuỗi | Không | Có (spread, spread_inside, packed) |
| Barrier / Group / Flow | Không | Có (trình trợ giúp ảo) |
| Hoạt ảnh MotionLayout | Không | Có |
| Số lần onMeasure | 1 (không có chu kỳ phụ thuộc) | 2 (luôn luôn, nhưng được tối ưu hóa) |
| Căn giữa | centerInParent, centerHorizontal, centerVertical | Qua bias (0.0–1.0) với các ràng buộc đối lập |
| Khi nào sử dụng | Mã kế thừa, bố cục đơn giản với 3–7 phần tử | Dự án mới, màn hình phức tạp, bố cục thích ứng |
RelativeLayout thua kém ConstraintLayout ở hầu hết mọi khía cạnh ngoại trừ một điểm: nó được tích hợp trong Android SDK từ API 1 và không yêu cầu phụ thuộc bổ sung. Đối với các dự án vẫn sử dụng minSdk dưới API 14 (Android 4.0), RelativeLayout là tùy chọn tiêu chuẩn duy nhất cho định vị tương đối. Trong tất cả các trường hợp khác, Google khuyến nghị ConstraintLayout.
Câu hỏi thường gặp
Nguyên nhân có khả năng nhất là thiếu các quy tắc định vị đầy đủ. Mỗi phần tử trong RelativeLayout phải có ít nhất một quy tắc ngang (alignParentLeft, toRightOf, centerHorizontal) và một quy tắc dọc (alignParentTop, below, centerVertical). Nếu không có quy tắc, phần tử ở vị trí (0,0) và có thể bị ẩn dưới các phần tử khác hoặc ngoài màn hình. Nguyên nhân thứ hai là phụ thuộc vòng tròn (phần tử A được gắn với B, B được gắn với A), dẫn đến hành vi không thể đoán trước.
Sử dụng thuộc tính android:layout_centerInParent="true". Nó căn giữa phần tử trên cả hai trục cùng lúc. Nếu chỉ cần theo chiều ngang — android:layout_centerHorizontal="true", chỉ theo chiều dọc — android:layout_centerVertical="true". Căn giữa hoạt động kết hợp với lề — phần tử sẽ được dịch chuyển tương đối so với trung tâm theo khoảng cách đã chỉ định.
Có, nhưng không được khuyến nghị. RelativeLayout thực hiện hai lần onMeasure khi có phụ thuộc vòng tròn, chậm hơn 15–30% so với ConstraintLayout trong danh sách RecyclerView. Đối với bố cục mục RecyclerView, hãy sử dụng ConstraintLayout — nó cung cấp hệ thống phân cấp phẳng với thời gian đo tối thiểu. Nếu RelativeLayout đã được sử dụng trong dự án kế thừa, hãy đảm bảo các phần tử không tạo ra phụ thuộc vòng tròn và đo FPS cuộn qua Profile GPU Rendering.
android:layout_margin đặt lề cho tất cả các phía cùng lúc. android:layout_marginTop — chỉ phía trên. Thuộc tính cụ thể hơn được ưu tiên: nếu cả layout_margin (16dp) và layout_marginTop (8dp) được đặt, lề trên sẽ là 8dp. Trong RelativeLayout, lề hoạt động tương đối so với ràng buộc, không phải so với phần tử liền kề: layout_marginTop với layout_below="@id/target" là lề từ cạnh trên của phần tử đến cạnh dưới của target.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm