ConstraintLayout – Phần 2: Các Chức Năng Nâng Cao
Bài đăng này đã không được cập nhật trong 3 năm
Chào các bạn.
Mời các bạn đến với phần 2 của bài viết về ConstraintLayout của mình.
Như mình có nói ở phần 1, ConstraintLayout
tuy dài (bị tách ra làm hai phần lận), nhưng nó không khó. Qua bài viết đó bạn đã nắm được ý nghĩa và cách sử dụng các công cụ cơ bản nhất mà Android Studio cung cấp để thiết kế kéo-thả cho layout mới mẻ này. Phần tiếp theo này bạn sẽ biết thêm các chức năng hay ho khác của ConstraintLayout
, giúp bạn có thể thiết kế được tất cả các layout mà bạn muốn.
Và chắc chắn chúng ta sẽ cùng nhau thử thực hành xây dựng một layout hoàn thiện ở cuối bài hôm nay.
Chức Năng Autoconnect (Tạo Constraint Tự Động)
Nếu như ở phần trước, bạn phải kéo kéo, neo neo thì mới có được các constraint ưng ý. Thì với một cú click chuột ở phần này, bạn đã có thể nói cho hệ thống biết nên tự tạo các constraint ngay khi bạn thả một view từ palette
vào design view
hoặc blueprint view
.
Đó là bạn hãy nhấn vào icon toolbar
được khoanh đỏ như hình trên để kích hoạt nó. Ở phần trước bạn đã thấy chức năng này bị tắt, khi đó bạn chú ý icon này sẽ có một đường gạch chéo. Khi được kích hoạt thì đường gạch chéo này sẽ biến mất.
Việc tự tạo các constraint một cách tự động như thế này theo mình thấy vẫn chưa hoàn hảo lắm, một số điểm neo chưa tạo được đúng theo ý mình. Nhưng nếu bật nó lên, thì mình không phủ nhận một điều rằng nó sẽ giúp thiết kế nhanh hơn trong một số trường hợp. Sử dụng nó hay không là tùy bạn nhé,
Bạn hãy xem sự so sánh giữa việc tắt và bật chức năng Autoconnect
này như sau.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Chức Năng Infer Constraints (Tự Suy Luận Các Constraint)
Nghĩa tiếng Việt là do tự mình đặt. Mà nghĩ cũng đúng thôi. Vì chức năng này khi được kích hoạt sẽ tự nó tính toán, suy luận ra các constraint để khớp với bố cục hiện thời của layout. Để sử dụng tính năng này, bạn hãy tìm đến icon toolbar được khoanh đỏ như trên.
Sự khác nhau giữa tính năng Infer Constraints
và Autoconnect
trên kia là, Infer Constraints
sẽ suy luận ra tất cả các constraint cho tất cả các view trong layout, để đạt được một bố cục như ý muốn của lập trình viên, còn Autoconnect
thì sẽ tạo các constraint cho bản thân view đang được tương tác.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Chức Năng Pack
Ý nghĩa của chức năng này là Đóng gói. Nhưng không hẳn như nghĩa của nó. Theo mình thấy thì chức năng này thực chất giúp bạn kéo dãn hết không gian của view theo chiều ngang hay dọc, sự kéo dãn này không đẩy các view khác đi khỏi vị trí của chúng. Bạn sẽ dễ dàng kiểm chứng chức năng này dựa theo minh họa sau. (Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Chức Năng Ratio (Điều Chỉnh Kích Cỡ View Theo Tỷ Lệ)
Theo lẽ mình đã có thể nói đến chức năng này ở mục Attribute
của phần trước. Nhưng ratio
quá xuất sắc khiến mình tách nó ra làm một phần riêng để bạn dễ nhớ.
Chức năng này cho phép bạn tạo các view bên trong ConstraintLayout
với kích thước theo tỉ lệ ngang-dọc. Chẳng hạn 16:9, hay 4:3. Khi bạn đã chỉ định ratio
cho view, thì bạn sẽ phải phó mặc sự tính toán về kích thước của view cho hệ thống.
Để có thể thiết lập ratio
cho view, bạn buộc phải chỉ định một trong hai giá trị (hoặc cả hai) layout_width
và layout_height
của view thành match_constraint
(hoặc là 0dp
), bạn có thể xem lại mục này ở phần trước để nhớ lại thiết lập match_constraint
cho view là như thế nào.
Sau khi bạn thiết lập một trong hai hướng (hoặc cả hai) của view là match_constraint
, thì ở view inspector
, chỗ hình vuông đại diện cho view trong cửa sổ này, sẽ xuất hiện một hình tam giác báo hiệu cho bạn biết bạn có thể click vào đó để thiết lập ratio
cho view.
Bạn có thể xem minh họa bên dưới (minh họa này có thực hiện việc kéo thả ImageView
vào blueprint view
, sau khi kéo các view liên quan đến image bạn phải chỉ định ngay cho nó một resource image ở cửa sổ xuất hiện sau đó, lát nữa vào phần thực hành bạn sẽ được kiểm chứng).
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Có một lưu ý cho tính năng này là. Nếu như ví dụ trên, bạn chỉ định match_constraint
theo một phương ngang, thì ratio
chỉ giúp bạn điều chỉnh chiều ngang của view, chiều cao của view vẫn được giữ cho cố định. Tức là nếu bạn chỉ định tỷ lệ 16:9, thì hệ thống sẽ lấy chiều cao hiện tại và xem giá trị ứng với tỷ lệ 9 là bao nhiêu, sau đó sẽ tính toán cho ra chiều ngang tương ứng với tỷ lệ 16, chính vì vậy, nếu nhìn kỹ bạn sẽ thấy chiều ngang của image ở trên dường như vượt ra khỏi chiều ngang của màn hình.
Nếu chưa rõ bạn hãy xem ví dụ dưới đây, khi mà layout_width
và layout_height
đều được thiết lập match_constraint
, thì nếu bạn nhấn nhiều lần lên hình tam giác ở góc, bạn có thể chọn được phương nào (ngang hay dọc) được hệ thống tinh chỉnh lại so với phương còn lại. Bạn chú ý phương được vẽ bằng đường thẳng đậm bên trong hình vuông của view inspector
chính là phương bị tinh chỉnh so với phương gốc nhé. Nếu không có đường thẳng đậm thì hoặc là tính năng ratio
đã tắt, hoặc là cả hai phương đều bị điều chỉnh. Chà nhức đầu nhỉ.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Chức Năng Guideline (Neo Constraint Vào Đường Biên)
Nếu bạn có nhiều view được canh chỉnh theo một trật tự thẳng hàng nào đó (thẳng hàng ngang hay thẳng hàng dọc), bạn có thể cân nhắc sử dụng guideline
. Các guideline
này là các đường thẳng không hề hiển thị lên giao diện khi bạn thực thi ứng dụng, nó chỉ được nhìn thấy khi thiết kế mà thôi, và nhờ vào các đường guideline
ẩn này, bạn có thể neo các view vào nó, để tạo ra một trật tự thẳng hàng nhất định.
Chi tiết guideline
hoạt động như thế nào thì bạn có thể nhìn vào ví dụ sau. Ở ví dụ dưới đây mình chỉ dùng các đường guideline
theo chiều dọc (Vetical Guideline), nhưng việc sử dụng với guideline
theo chiều ngang (Horizontal Guideline) cũng sẽ tương tự.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Ngoài việc canh chỉnh guideline
theo biên trái như trên, bạn còn có thể nhấn vào hình tròn ở đầu guideline
để thay đổi cách canh chỉnh theo biên phải, hay theo phần trăm.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Chức Năng Barrier (Neo Constraint Vào Đường Biên Động)
Nếu như guideline
giúp chúng ta tạo ra các đường biên, và canh chỉnh view dựa trên các đường biên đó. Mặc dù bạn có thể di chuyển được guideline
như ví dụ trên. Nhưng guideline
vẫn mang một công dụng “cứng”, tức là bạn chỉ có thể chỉnh sửa các guideline
khi nó còn đang ở dạng thiết kế, khi thực thi ứng dụng rồi thì guideline
ở đâu view sẽ ở đó, mặc cho có sự chồng lấp giữa các view với nhau, vì ai mà biết được các kích cỡ màn hình thực tế như thế nào để mà thiết lập các guideline
hợp lý.
Để giải quyết vấn đề cần phải có sự dịch chuyển động giữa các guideline
với nhau, bạn nên dùng chức năng hay hơn, đó là barrier
.
Bạn có thể thêm bất kỳ barrier
theo hướng ngang hay dọc vào màn hình bằng cách chọn trên toolbar
theo hình ảnh trên, rồi thực hiện như minh họa dưới đây.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Do ảnh động trên khá là dài nên có thể gây khó khăn cho bạn khi muốn xem lại quy trình sử dụng barrier
, nên mình viết lại các bước sau.
- Như đã nói, đầu tiên bạn thêm
barrier
(dọc hay ngang) bằng cách chọn trêntoolbar
như hình trên kia. Hoặc click chuột phải vàoblueprint view
thì bạn cũng sẽ thấy tùy chọn tương tự. - Sau đó bạn phải kéo các view muốn “chặn” lại một cách cơ động vào
barrier
vừa tạo ởcomponent tree
. - Bạn nhớ chỉ định thuộc tính
barrierDirection
làright
, đây chính là phía màbarrier
sẽ “chặn động” các view. - Cuối cùng, bạn có thể neo các view khác, như các
EditText
ở ví dụ trên, vàobarrier
này,. - Xong. Bạn có thể thử nghiệm tính hiệu quả của
barrier
bằng cách thay đổi text của cácTextView
. Bạn sẽ thấybarrier
chạy động theo biên phải của các thành phần con của nó như thế nào. Và các view khác khi neo vàobarrier
cũng sẽ tự dịch chuyển như thế nào.
Chức Năng Chain (Xâu Chuỗi Các View Lại, Hay Xích Các View Lại)
Chức năng này mình cũng khá thích. Nó giúp bạn canh chỉnh vị trí của nhiều view một cách tự động. Việc canh chỉnh này giống như hệ thống đã dùng dây xích để cột chúng lại vậy, và sau đó hệ thống sẽ dàn các view đó ra hoặc co chúng lại tùy thuộc vào kích cỡ màn hình và các thiết lập của bạn. Hệ thống gọi đây là chain
.
Để gom các view vào trong một chain
, bạn chỉ cần chọn hết các view muốn gom bằng cách nhấn giữ phím Shift trong lúc click chọn từng view. Rồi click phải lên bất kỳ view nào trong số chúng, và chọn Chain > Create Horizontal Chain (hay Create Vertical Chain). Bạn hãy xem minh họa sau.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Bạn cũng thấy đó, sau khi gia nhập vào một chain
, sẽ có một sợi xích ràng buộc các view lại với nhau, và khi đó vị trí các view sẽ dàn đều không gian của chúng.
Công dụng của chain
chưa dừng lại ở đó. Bạn có thể chỉ định nhiều thể loại chain
khác nhau. Các loại chain
đó là.
- Spread: Đây là kiểu dàn đều các view dựa vào không gian của chúng theo phương ngang hoặc dọc. Đây là kiểu sắp xếp mặc định khi bạn tạo mới một
chain
. Bạn có thể xem lại minh họa kiểuchain
này ở hình động trên kia. - Spread inside: Kiểu này cũng sẽ dàn đều các view, nhưng nó sẽ tôn trọng constraint của view đầu và cuối trong một
chain
. Như bạn thấy trên hình, nếu các view A và C đều setmargin
ở các biên là0dp
thì chúng sẽ dính chặt vào biên như vậy. - Weight: Cách này tương tự như bạn chỉ định trọng số
layout_weight
trong LinearLayout vậy. Để sử dụng đượcweight
trongConstraintLayout
thì bạn phải chỉ định các view trong chain vềmatch_constraint
, rồi tìm đến thuộc tínhhorizontal_weight
hoặcvertical_weight
để thiết lập trọng số này cho từng view. Bạn sẽ hiểu rõ hơn ở minh họa phía dưới đây. - Packed: Kiểu “đóng gói” các view lại thành một “cục” sát vào nhau. Sau khi đóng gói các view lại xong, bạn có thể sử dụng
bias
để thay đổi độ lệch theo chiều ngang hoặc dọc cho các gói này (bạn nhớ để ý xem minh họa cho việc thay đổibias
với kiểu packed này ở dưới đây).
Để thay đổi từng loại chain
đã nói đến trên đây, rất đơn giản, bạn hãy đưa trỏ chuột vào bất kỳ một view nào trong chain
, khi thấy xuất hiện một nút giống như hình mắt xích ở dưới view, bạn hãy nhấn vào nó để lần lượt thay đổi qua các loại chain
.
Minh họa dưới đây cho thấy chuyển đổi qua lại giữa các loại chain
thứ 1 (Spread), 2 (Spread inside) và 4 (Packed). Vì loại số 3 (Weight) cần phải thay đổi chút giá trị match_constraint
và weight
nên mình sẽ minh họa ở sau.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Còn đây là minh họa cho chain
ở loại thứ 3 (weight). Với chain
này mình sẽ set trọng số cho các view lần lượt là 1-2-1. Bạn xem.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Chức Năng Group (Gom Nhóm)
Chức năng cuối cùng mà mình nói đến hơi kém quan trọng xíu. Nó không phải ViewGroup
mà chúng ta từng biết. Bởi vì nó chẳng chứa đựng view nào bên trong cả. Group
ở ConstraintLayout
chỉ chứa địa chỉ các view, để bạn dễ dàng thiết lập một số tham số chung cho các view mà nó đang chứa đựng mà thôi, như tham số ẩn hiện như ví dụ này.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Đến đây thì các bạn đã nắm được tất cả các công cụ để tạo nên một giao diện hoàn chỉnh bằng ConstraintLayout
rồi đó. Giờ thì chúng ta cùng vận dụng những điều đã biết ở hai phần của bài viết để xây dựng một giao diện hoàn chỉnh nhé.
Thực Hành Xây Dựng Giao Diện Bằng ConstraintLayout
Bài thực hành này của mình sẽ xây dựng lại giao diện theo như hướng dẫn này.
Để có thể bắt tay vào xây dựng một giao diện hoàn chỉnh bằng ConstraintLayout
, thì tốt nhất bạn phải có đầy đủ các resource cho nó, đặc biệt là resource ảnh.
Bạn có thể làm theo hướng dẫn ở link thực hành mình để ở trên mà không cần phải đọc phần này của mình. Hoặc bạn cũng có thể vào link đó lấy resource về rồi đi qua các bước của mình bên dưới. Nếu không thích vào link trên thì bạn có thể click vào link này để download resource về.
Nào, sau khi chuẩn bị sẵn resource cho bài thực hành, để bắt đầu, bạn hãy tạo project với Empty Activity như mình có nói ở phần 1.
Đầu tiên, chúng ta sẽ kéo một ImageView
, đó chính là header ở trên cùng của màn hình. Header này sẽ có chiều dài chiếm trọn không gian dài của màn hình. Chiều cao của nó mình sẽ không set tỷ lệ, mà mình sẽ cho nó neo vào icon favorite sẽ xây dựng ở bước sau.
Bạn nhớ chỉnh margin
cho header về 0dp
hết. Định nghĩa text cho thuộc tính contentDescription
, thuộc tính này sẽ giúp hiển thị text cho ImageView
khi hệ thống không load được ảnh. Và nhớ chỉ định scaleTyle
là centerCrop nhé.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Tiếp theo bạn hãy kéo một ImageView
nữa, đây chính là icon favorite. Bạn hãy chỉ định kích thước cố định cho nó là 36dp
. Cũng định nghĩa text cho contentDescription
và thiết lập background
cho nó. Sau đó bạn nhớ neo đáy của header trên kia vào đáy của favorite. Khi này nếu bạn thay đổi bias
của icon favorite, bạn sẽ thấy chiều cao của header thay đổi theo. Thiệt là vi diệu!
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Chúng ta sẽ thêm một TextView
làm title. TextView
này không có gì đặc biệt cả. Chỉ có lưu ý là ở bước này bạn nên thay đổi giá trị default của margin
về 16dp
. Vì hầu như các view sau đều dùng đến giá trị margin
này.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Tiếp theo chúng ta sẽ thêm hai TextView
. Hai TextView
mà bạn sẽ thêm vào chỉ neo về bên trái thôi. Bạn chấp nhận báo lỗi từ Component Tree
đi, vì hai TextView
này chưa được neo theo chiều dọc nào. Chúng ta sẽ hoàn thiện constraint cho chúng khi có EdiText
ở bước dưới nữa. Có một lưu ý là chúng ta nên tạo barrier
cho hai TextView
ở bước này luôn.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Giờ thì chúng ta tạo EditText
rồi neo nó vào barrier
của TextView
, để nó có thể “lạc trôi” theo chiều rộng của TextView
.
Sau khi có EditText
rồi thì bạn tạo baseline constraint cho TextView
tương ứng với EditText
của nó. Minh họa sau chỉ thực hiện cho một EditText
, bạn hãy tạo một EditText
nữa bên dưới EditText
đã tạo nhé.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Bước tiếp theo sẽ tạo hai Button
rồi neo chúng về phía phải và dưới màn hình. Bước này không có gì khó khăn cả.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Bước cuối cùng bạn hãy đặt một TextView
vào khoảng không gian giữa màn hình. Đây chính là description của giao diện. Bạn có thể dùng chức năng pack
để mở rộng view hết cỡ trong khoảng không gian còn lại đó. Vậy là bạn đã xong một màn hình hoàn chỉnh bằng ConstraintLayout
rồi đó.
(Nếu bạn không xem được ảnh gif thì có thể xem bài viết ở link gốc này)
Kết Luận
Qua hai phần của ConstrainLayout
mà mình tự thiết kế lại (dĩ nhiên cũng có tham khảo khá nhiều nguồn). Hi vọng các bạn đủ tự tin để bắt đầu thiết kế các giao diện ứng dụng trên Android bằng ConstrainLayout
rồi nhé. Hoặc nếu các bạn có các project đang được xây dựng, hãy thử chuyển các layout kiểu cũ sang ConstraintLayout
xem sao.
Để biết nhiều hơn cách thức xây dựng một ứng dụng Android, bạn có thể đọc series bài viết này.
Bạn cũng có thể vào link gốc này để xem lại vài viết của ngày hôm nay: ConstraintLayout – Phần 2: Các Chức Năng Nâng Cao.
All rights reserved