class CircularRevealHelper @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintHelper(context, attrs, defStyleAttr) { override fun updatePostLayout(container: ConstraintLayout) { super.updatePostLayout(container) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val views = getViews(container) for (view in views) { val anim = ViewAnimationUtils.createCircularReveal(view, view.width / 2, view.height / 2, 0f, Math.hypot((view.height / 2).toDouble(), (view.width / 2).toDouble()).toFloat()) anim.duration = 3000 anim.start() } } } } updatePostLayout会在 onLayout 之后调用,在这里做动画就可以。 有了CircularRevealHelper之后可以直接在 xml 里面使用,在CircularRevealHelper的constraint_referenced_ids里面指定需要做动画 view。 <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android” xmlns:app=“http://schemas.android.com/apk/res-auto” xmlns:tools=“http://schemas.android.com/tools” android:layout_width=“match_parent” android:layout_height=“match_parent”> <cn.feng.constraintLayout2.helps.CircularRevealHelper android:id=“@+id/helper” android:layout_width=“wrap_content” android:layout_height=“wrap_content” app:constraint_referenced_ids=“img_mario” tools:ignore=“MissingConstraints” /> </android.support.constraint.ConstraintLayout>
后面如果要对 view 做CircularReveal直接在 xml 里面指定就可以了,做到了很好的复用。
FlyinHelper
再来看看这个 Flyin 的飞入效果,view 从四周飞入到各自的位置。
这个动画的关键在于计算出每个 view 该从什么方向飞入。
红色边框的位置可以借助前面介绍的的Layer找到(当然也可以不借助Layer,自己算,稍显复杂),从而计算出红色框框部分的中间点位置, 再和图中每个 view 的中间点比较(图中每个白点的位置)从而得出每个 view 该从哪个方向飞入。
计算每个view 的初始位置代码如下,借助上面的图形应该很好理解。
for (view in views) { val viewCenterX = (view.left + view.right) / 2 val viewCenterY = (view.top + view.bottom) / 2 val startTranslationX = if (viewCenterX < centerPoint.x) -2000f else 2000f val startTranslationY = if (viewCenterY < centerPoint.y) -2000f else 2000f view.translationX = (1 - animatedFraction) * startTranslationX view.translationY = (1 - animatedFraction) * startTranslationY }
FlyinHelper 的完整代码参考这里
ComposeMultipleHelper
每个 view 不但可以接受一个ConstraintHelper,还可以同时接受多个ConstraintHelper。
左边的四个 ImageView 和右下的 FloatingActionButton 都有 Flyin 的效果,同时左边的四个ImageView还在绕 Y 轴做 3D 旋转。上方的 Seekbar的背景在做CircularReveal的效果。有了前面编写的CircularRevealHelper以及 FlyInHelper 我们可以很方便做到这样的效果。
Flow (VirtualLayout)
Flow 是 VirtualLayout,Flow 可以像 Chain 那样帮助快速横向/纵向布局constraint_referenced_ids里面的元素。 通过flow_wrapMode可以指定具体的排列方式,有三种模式
- wrap none : 简单地把constraint_referenced_ids里面的元素组成chain,即使空间不够
- wrap chain : 根据空间的大小和元素的大小组成一条或者多条 chain
- wrap aligned : wrap chain类似,但是会对齐
下面看下如何实现这个计算器布局:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android” xmlns:app=“http://schemas.android.com/apk/res-auto” xmlns:tools=“http://schemas.android.com/tools” android:layout_width=“match_parent” android:layout_height=“match_parent” tools:context=“.activity.MainActivity”> <android.support.constraint.helper.Flow android:id=“@+id/flow” android:layout_width=“match_parent” android:layout_height=“wrap_content” android:background=“#FFC107” android:padding=“20dp” app:constraint_referenced_ids=“tv_num_7,tv_num_8,tv_num_9,tv_num_4,tv_num_5,tv_num_6,tv_num_1,tv_num_2,tv_num_3,tv_num_0,tv_operator_div,tv_dot,tv_operator_times” app:flow_horizontalGap=“10dp” app:flow_maxElementsWrap=“3” app:flow_verticalGap=“10dp” app:flow_wrapMode=“aligned” app:layout_constraintBottom_toBottomOf=“parent” app:layout_constraintEnd_toEndOf=“parent” app:layout_constraintStart_toStartOf=“parent” /> <TextView