12/08/2018, 14:50

Xây dựng layout linh hoạt với FlexBoxLayout

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 để ...

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_awidth="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.

Nguồn: Build flexible layouts with FlexboxLayout

0