Trong bản phát hành ConstraintLayout
2.1, một số tính năng đã được thêm vào để giúp quản lý các thiết bị có thể gập, trong đó có SharedValues
, ReactiveGuide
và hỗ trợ nâng cao ảnh động với MotionLayout
.
Giá trị được chia sẻ
Chúng tôi thêm một cơ chế mới để chèn giá trị thời gian chạy vào ConstraintLayout
– mục đích của cơ chế này là sử dụng cho các giá trị trên toàn hệ thống, vì mọi thực thể của ConstraintLayout
đều có thể truy cập vào giá trị đó.
Trong bối cảnh của các thiết bị có thể gập lại, chúng ta có thể sử dụng cơ chế này để chèn vị trí của màn hình đầu tiên trong thời gian chạy:
Kotlin
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)
Java
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);
Trong một trình trợ giúp tuỳ chỉnh, bạn có thể truy cập vào các giá trị dùng chung bằng cách thêm trình nghe đối với mọi thay đổi:
Kotlin
val sharedValues: SharedValues = ConstraintLayout.getSharedValues() sharedValues.addListener(mAttributeId, this)
Java
SharedValues sharedValues = ConstraintLayout.getSharedValues(); sharedValues.addListener(mAttributeId, this);
Bạn có thể xem ví dụ về Thử nghiệm có thể gập lại để xem cách chúng tôi nắm bắt vị trí màn hình đầu tiên bằng cách sử dụng thư viện Jetpack WindowManager và chèn vị trí vào ConstraintLayout
.
Kotlin
inner class StateContainer : Consumer<WindowLayoutInfo> { override fun accept(newLayoutInfo: WindowLayoutInfo) { // Add views that represent display features for (displayFeature in newLayoutInfo.displayFeatures) { val foldFeature = displayFeature as? FoldingFeature if (foldFeature != null) { if (foldFeature.isSeparating && foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL ) { // The foldable device is in tabletop mode val fold = foldPosition(motionLayout, foldFeature) ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold) } else { ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0); } } } } }
Java
class StateContainer implements Consumer<WindowLayoutInfo> { @Override public void accept(WindowLayoutInfo newLayoutInfo) { // Add views that represent display features for (DisplayFeature displayFeature : newLayoutInfo.getDisplayFeatures()) { if (displayFeature instanceof FoldingFeature) { FoldingFeature foldFeature = (FoldingFeature)displayFeature; if (foldFeature.isSeparating() && foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL ) { // The foldable device is in tabletop mode int fold = foldPosition(motionLayout, foldFeature); ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold); } else { ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0); } } } } }
fireNewValue()
lấy một mã nhận dạng đại diện cho giá trị làm tham số đầu tiên và giá trị cần chèn làm tham số thứ hai.
ReactiveGuide
Một cách để tận dụng SharedValue
trong bố cục mà không phải viết bất kỳ mã nào là sử dụng trình trợ giúp ReactiveGuide
. Thao tác này sẽ định vị một hướng dẫn theo chiều ngang hoặc chiều dọc theo SharedValue
được liên kết.
<androidx.constraintlayout.widget.ReactiveGuide
android:id="@+id/fold"
app:reactiveGuide_valueId="@id/fold"
android:orientation="horizontal" />
Sau đó, bạn có thể sử dụng đoạn mã này như với hướng dẫn thông thường.
MotionLayout
cho thiết bị có thể gập lại
Chúng tôi đã thêm một số tính năng trong MotionLayout
ở phiên bản 2.1 để giúp biến đổi trạng thái – một tính năng đặc biệt hữu ích cho thiết bị có thể gập lại, vì chúng ta thường phải xử lý ảnh động giữa nhiều bố cục có thể có.
Có 2 phương pháp dành cho thiết bị có thể gập lại:
- Trong thời gian chạy, hãy cập nhật bố cục hiện tại (
ConstraintSet
) để hiện hoặc ẩn đường ranh giới phần hiển thị. - Sử dụng một
ConstraintSet
riêng cho từng trạng thái có thể gập lại mà bạn muốn hỗ trợ (closed
,folded
hoặcfully open
).
Tạo ảnh động cho ConstraintSet
Hàm updateStateAnimate()
trong MotionLayout
đã được thêm vào bản phát hành 2.1:
Kotlin
fun updateStateAnimate(stateId: Int, set: ConstraintSet, duration: Int)
Java
void updateStateAnimate(int stateId, ConstraintSet set, int duration);
Hàm này sẽ tự động tạo ảnh động cho các thay đổi khi cập nhật một ConstraintSet
nhất định thay vì cập nhật ngay (bạn có thể thực hiện với updateState(stateId, constraintset)
). Tính năng này cho phép bạn cập nhật giao diện người dùng một cách nhanh chóng, tuỳ thuộc vào các thay đổi, chẳng hạn như trạng thái gập của thiết bị có thể gập lại.
ReactiveGuide
bên trong một MotionLayout
ReactiveGuide
cũng hỗ trợ 2 thuộc tính hữu ích khi được dùng bên trong một MotionLayout
:
app:reactiveGuide_animateChange="true|false"
app:reactiveGuide_applyToAllConstraintSets="true|false"
Phần đầu tiên sẽ sửa đổi ConstraintSet
hiện tại và tự động tạo ảnh động cho thay đổi. Phần tử thứ hai sẽ áp dụng giá trị mới của vị trí ReactiveGuide
cho tất cả ConstraintSet
trong MotionLayout
. Một phương pháp điển hình cho thiết bị có thể gập lại là sử dụng ReactiveGuide
đại diện cho vị trí gập, thiết lập các phần tử bố cục tương ứng với ReactiveGuide
.
Sử dụng nhiều ConstraintSet
để biểu thị trạng thái có thể gập lại
Thay vì cập nhật trạng thái MotionLayout
hiện tại, một cách khác để thiết kế giao diện người dùng nhằm hỗ trợ thiết bị có thể gập lại là tạo các trạng thái riêng biệt cụ thể (bao gồm closed
, folded
và fully open
).
Trong trường hợp này, bạn có thể vẫn muốn sử dụng ReactiveGuide
để thể hiện màn hình đầu tiên, nhưng bạn sẽ có nhiều quyền kiểm soát hơn (so với ảnh động tự động khi cập nhật ConstraintSet
hiện tại) về cách mỗi trạng thái sẽ chuyển đổi thành một trạng thái khác.
Với cách tiếp cận này, trong trình nghe DeviceState
, bạn chỉ cần lệnh MotionLayout
chuyển đổi sang các trạng thái cụ thể thông qua phương thức MotionLayout.transitionToState(stateId)
.