其他
MotionLayout实现无限轮播图效果
ConstraintLayout自2.0起引入了MotionLayout
MotionLayout是ConstraintLayout的子类,它具有ConstraintLayout的所有能力,可以实现两个ConstraintSet之间的切换,并且可以通过Transition定义转场动画。
1. 模拟ViewPager效果
使用MotionLayout可以模拟实现一个无限Item的ViewPager切换效果:
2. 实现思路
定义三个子View,分别代表ViewPager当前显示中View以及前后两个View
因为无限的Item只能复用这三个子View,所以在切换结束后,要将motion恢复到初始状态,即左右都存在可以进一步切换的子View
3. 核心代码
要使用MotionLayout,必须将ConstraintLayout升级到2.0以上:
implementation 'androidx.constraintlayout:constraintlayout:$latest_version'
定义MotionScene
用三组ConstraintSet
定义三个布局状态:起始状态、左滑后、右滑后
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<ConstraintSet android:id="@+id/base_state">
<Constraint android:id="@id/centerView">
<Layout
android:layout_width="@dimen/center_size"
android:layout_height="@dimen/center_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
<Constraint android:id="@id/leftView">
<Layout
android:layout_width="@dimen/side_size"
android:layout_height="@dimen/side_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
<Constraint android:id="@id/rightView">
<Layout
android:layout_width="@dimen/side_size"
android:layout_height="@dimen/side_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/move_left_to_right">
<Constraint android:id="@id/centerView">
<Layout
android:layout_width="@dimen/side_size"
android:layout_height="@dimen/side_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
<Constraint android:id="@id/leftView">
<Layout
android:layout_width="@dimen/center_size"
android:layout_height="@dimen/center_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
<Constraint android:id="@id/rightView">
<Layout
android:layout_width="@dimen/side_size"
android:layout_height="@dimen/side_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/move_right_to_left">
<Constraint android:id="@id/centerView">
<Layout
android:layout_width="@dimen/side_size"
android:layout_height="@dimen/side_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
<Constraint android:id="@id/leftView">
<Layout
android:layout_width="@dimen/side_size"
android:layout_height="@dimen/side_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
<Constraint android:id="@id/rightView">
<Layout
android:layout_width="@dimen/center_size"
android:layout_height="@dimen/center_size"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
</ConstraintSet>
<Transition
motion:constraintSetEnd="@id/move_left_to_right"
motion:constraintSetStart="@id/base_state">
<OnSwipe
motion:dragDirection="dragRight"
motion:onTouchUp="autoCompleteToStart"
motion:touchAnchorId="@id/centerView"
motion:touchAnchorSide="right" />
</Transition>
<Transition
motion:constraintSetEnd="@id/move_right_to_left"
motion:constraintSetStart="@id/base_state">
<OnSwipe
motion:dragDirection="dragLeft"
motion:onTouchUp="autoCompleteToStart"
motion:touchAnchorId="@id/centerView"
motion:touchAnchorSide="left" />
</Transition>
</MotionScene>
<Transition/>
中定义了左滑、右滑的切换
Item切换
如前所述,左右滑动导致动画切换结束后,为了调整显示中的View到中间,需要回复视图到初始状态 motionLayout?.progress = 0F
motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
when (currentId) {
R.id.move_left_to_right -> {
if (currentPosition > 0) {
currentPosition--
} else {
currentPosition = itemList.lastIndex
}
motionLayout?.progress = 0F
updateView()
}
R.id.move_right_to_left -> {
if (currentPosition < itemList.lastIndex) {
currentPosition++
} else {
currentPosition = 0
}
motionLayout?.progress = 0F
updateView()
}
}
}
})
子View的位置虽然恢复初始状态,但是内容必须保持最新状态,所以在updateView
中,更新视图内容:
private fun updateView() {
centerTextView.text = "Item\n${itemList[currentPosition]}"
rightTextView.text = if (currentPosition == itemList.lastIndex) {
"Item\n${itemList.first()}"
} else {
"Item\n${itemList[currentPosition + 1]}"
}
leftTextView.text = if (currentPosition == 0) {
"Item\n${itemList.last()}"
} else {
"Item\n${itemList[currentPosition - 1]}"
}
}
4. 最后
MotionLayoutn能够方便实现场景切换的动画效果,AndroidStudio 4.0 以上还为MotionLayout提供了MotionEditor功能,无需通过Xml便可设置视图状态及转场动画。可以预见,未来MotionLayout必将作为Android开发中的一个重要角色被推广和使用。
代码地址:
https://github.com/vitaviva/MotionPager
↓关注公众号↓ | ↓添加微信交流↓ |
---|---|