Xây dựng layout linh hoạt với FlexBoxLayout
Bài đăng này đã không được cập nhật trong 3 năm
Tại Google I/O năm ngoái chúng tôi đã ra mắt ConstraintLayout
cho phép bạn xây dựng những layout phức tạp trong khi vẫn giữ được độ đơn giản của tầng view. Nó cũng đã được hỗ trợ hoàn toàn trong Visual Layout Editor của Android Studio.
Cùng lúc đó chúng tôi cũng đã open source FlexboxLayout
để mang những tính năng của Flexible Layout trong CSS đến với Android. Dưới đây là 1 số trường hợp mà sử dụng FlexboxLayout
tỏ ra rất hiệu quả.
FlexboxLayout
có thể được coi như 1 phiên bản cải tiến của LinearLayout
bởi vì cả hai đều xếp những view con 1 cách tuần tự. Điểm khác biệt chính yếu giữa LinearLayout
và FlexboxLayout
là FlexboxLayout
hỗ trợ việc gói (wrap).
Điều đó có nghĩa là nếu bạn sử dụng thuộc tính flexWrap="wrap"
, FlexboxLayout
sẽ đưa view con xuống dòng dưới nếu không đủ chỗ trống cho nó ở dòng hiện tại, như hình dưới đây:
Một layout cho nhiều kích cỡ màn hình
Với một đặc tính như vậy, hãy cùng xem xét một trường hợp khi bạn muốn sắp xếp view theo thứ tự nhưng sẽ đưa chúng xuống dòng tiếp theo nếu chỗ trống thay đổi (có thể là do yếu tố thiết bị, thay đổi định hướng hay thay đổi kích thước cửa sổ trong chế độ multi-window).
Nexus5X portrait
Nexus5X landscape
Pixel C bật chế độ multi window, thanh phân cách ở bên trái.
Pixel C bật chế độ multi window, thanh phân cách ở giữa.
Pixel C bật chế độ multi window, thanh phân cách ở bên phải.
Bạn sẽ cần phải định nghĩa những DP layout khác nhau (ví dụ như layout-600dp, layout-720dp, layout-1020dp) để xử lý các kích cỡ màn hình khác nhau đối với những layout truyền thống như LinearLayout
hay RelativeLayout
. Nhưng dialog ở trên được xây dựng đơn giản chỉ với 1 FlexboxLayout
.
Kĩ thuật được sử dụng ở đây là dùng thuộc tính flexWrap="wrap"
như đã giải thích ở trên,
<com .google.android.flexbox.flexboxlayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:flexwrap="wrap">
từ đó bạn sẽ có layout như dưới đây với những view con được xếp xuống dòng mới thay vì tràn ra ngoài view cha.
Một kĩ thuật khác mà tôi cũng muốn nhấn mạnh đó là sử dụng thuộc tính layout_flexGrow
cho 1 view riêng lẻ. Nó sẽ giúp cải thiện giao diện của layout khi chỗ trống bị bỏ thừa. Thuộc tính layout_flexGrow
hoạt động tương tự như layout_weight
trong LinearLayout
. Điều đó có nghĩa là FlexboxLayout
sẽ phân bổ những chỗ trống còn lại dựa trên giá trị được dùng cho mỗi view con trong cùng 1 dòng.
Bạn có thể xem layout XML đầy đủ trên Github
Tích hợp với RecyclerView
Một trong những điểm mạnh khác của FlexboxLayout
đó là nó có thể được tích hợp với RecyclerView. Với bản release mới nhất của version alpha, FlexboxLayoutManager
được extend từ RecyclerView.LayoutManager
, giờ đâyviệc sử dụng các chức năng của Flexbox trong 1 scrollable container tỏ ra hiệu quả hơn nhiều về mặt bộ nhớ.
Chú ý rằng bạn cũng có thể có 1 Flexbox container hỗ trợ cuộn bằng cách gói FlexboxLayout
trong 1 ScrollView
. Tuy nhiên, bạn sẽ gặp phải hiện tượng giật lag hay thậm chí là OutOfMemoryError
với số lượng lớn item nằm trong layout, bởi vì FlexboxLayout
không đảm nhiệm việc tái chế view khi nó ra khỏi màn hình.
Một ví dụ thực tế việc tích hợp RecyclerView
mang lại hiệu quả đó là cho những app như Google Photos hay app tin tức, vì cả 2 đều có số lượng lớn item trong khi cần quản lý chiều rộng khác nhau của những item đó.
Một ví dụ có thể được tìm thấy trong app demo của FlexboxLayout
. Như bạn có thể thấy ở trong repo, mỗi ảnh hiển thị trong RecyclerView
có 1 chiều rộng khác nhau. Nhưng bằng cách sử dụng thuộc tính flexWrap
,
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager();
layoutManager.setFlexWrap(FlexWrap.WRAP);
và thay đổi thuộc tính flexGrow (như bạn có thể thấy thì chúng ta có thể thay đổi thuộc tính này thông qua FlexboxLayoutManager
và FlexboxLayoutManager.LayoutParams
cho view con thay vì thay đổi nó trong xml) thành 1 giá trị dương cho mỗi view con,
void bindTo(Drawable drawable) {
mImageView.setImageDrawable(drawable);
ViewGroup.LayoutParams lp = mImageView.getLayoutParams();
if (lp instanceof FlexboxLayoutManager.LayoutParams) {
FlexboxLayoutManager.LayoutParams flexboxLp =
(FlexboxLayoutManager.LayoutParams) mImageView.getLayoutParams();
flexboxLp.setFlexGrow(1.0f);
}
}
bạn sẽ thấy rằng tất cả các hình ảnh được xếp một cách hợp lý vào layout bất kể ở định hướng nào của màn hình.
Nếu bạn cần ví dụ hoàn thiện của FlexboxLayout
, hãy xem:
-
Playground demo app - Sử dụng
FlexboxLayout
vàFlexboxLayoutManager
. -
Cat gallery demo app - Sử dụng
FlexboxLayoutManager
.
All rights reserved