ConstraintSet
ConstraintSet允许我们动态的修改ConstraintLayout中某些控件的约束,并在约束改变的时候,为我们添加上动画效果
官方文档:https://developer.android.com/reference/androidx/constraintlayout/widget/ConstraintSet
个人理解
ConstraintSet对象中保存了ConstraintLayout里面所有控件的约束条件,通过constraintSet.applyTo()方法将修改以后的ConstraintSet应用到ConstraintLayout,可以实现批量更改约束条件,并通过TransitionManager.beginDelayedTransition()
方法,让对这种切换加上动画效果,可以实现一些非常复杂的交互效果。
创建ConstraintSet
val constraintSet = ConstraintSet() //创建对象
//用法1:从ConstraintLayout实例中获取约束集,最常用的用法
constraintSet.clone(constraintLayoutView) // constraintLayoutView必须是ConstraintLayout
//用法2:从布局中获取约束集
constraintSet.clone(context, R.layout.my_layout) // my_layout.xml的根布局必须是ConstraintLayout
//用法3:从其他约束集中获取约束集
constraintSet.clone(otherConstraintSet)
常见问题一:java.lang.RuntimeException: All children of ConstraintLayout must have ids to use ConstraintSet
当我们使用方法一和方法二的时候,ConstraintLayout下的所有直接子控件必须要设置id
使用方法一
最简单的一种使用方法则是写两个一模一样的ConstraintLayout布局,然后获取两个ConstraintSet进行互相切换。
原始布局
//activity_demo1.xml
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/demo1_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="左上角快乐的小文本"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
期望变化后的布局
//activity_demo1_constraint
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/demo1_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这个文件仅仅用来描述约束,加一个颜色,完全没影响呀"
android:textColor="@color/teal_200"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/demo1_tvvvv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="所以多一个控件,改个属性,完全没影响呀"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
代码切换ConstraintSet
//初始ConstraintSet
initConstraintSet = ConstraintSet().apply {
clone(bindingImpl.targetConstraintLayout)
}
//期望ConstraintSet
endConstraintSet = ConstraintSet().apply {
clone(this@Demo1Activity, R.layout.activity_demo1_constraint)
}
bindingImpl.switchLayout.setOnClickListener {
//加上动画切换
TransitionManager.beginDelayedTransition(bindingImpl.targetConstraintLayout)
endConstraintSet.applyTo(bindingImpl.targetConstraintLayout)
Handler(Looper.getMainLooper()).postDelayed({
//2秒以后还原位置
androidx.transition.TransitionManager.beginDelayedTransition(bindingImpl.targetConstraintLayout)
initConstraintSet.applyTo(bindingImpl.targetConstraintLayout)
}, 2000
)
}
ps:另一个布局文件,仅仅是用来描述约束的不同,只要期望更改的控件以及依赖的控件id都在就可以
使用方法二
比第一种方法使用复杂一点点,那就是使用ConstraintSet的Api来更改约束
比如更改大小、位置
//最开始的约束,从ConstraintLayout中获取
initConstraintSet = ConstraintSet().apply {
clone(binding.targetConstraintLayout)
}
//在最开始的约束上面新增、修改约束
endConstraintSet = ConstraintSet().apply {
clone(initConstraintSet)
}
binding.growUp.setOnClickListener {
//给R.id.demo2_tv添加约束app:layout_constraintWidth_percent="0.8"
endConstraintSet.constrainPercentWidth(R.id.demo2_tv, 0.8f)
TransitionManager.beginDelayedTransition(binding.targetConstraintLayout)
endConstraintSet.applyTo(binding.targetConstraintLayout)
}
binding.transition.setOnClickListener {
//给R.id.demo2_tv 添加约束app:layout_constraintBottom_toTopOf="@+id/anchor"
endConstraintSet.connect(
R.id.demo2_tv,
ConstraintSet.BOTTOM,
R.id.anchor,
ConstraintSet.TOP
)
//R.id.demo2_tv 去掉约束app:layout_constraintTop_toTopOf="parent"
endConstraintSet.clear(R.id.demo2_tv, ConstraintSet.TOP)
TransitionManager.beginDelayedTransition(binding.targetConstraintLayout)
endConstraintSet.applyTo(binding.targetConstraintLayout)
}
这种方法需要我们熟悉已有代码的约束,然后在代码中动态添加、修改、删除约束,很多时候会遇到代码中设置的没有效果,可以把这些约束写到布局中预览的看看,比如endConstraintSet.constrainPercentWidth(R.id.demo2_tv, 0.8f)
给控件R.id.demo2_tv设置宽度为父布局的80%,如果xml中没有设置如下三个约束,那么就不生效了
android:layout_width="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
常用API
设置宽高
ConstraintSet提供了一组方法用于设置控件的尺寸:
constrainWidth(int viewId, int width)
constrainHeight(int viewId, int height)
constrainPercentWidth(int viewId, float percent)
constrainPercentHeight(int viewId, float percent)
反过来,要重新设置回wrap_content
constrainWidth(R.id.button, ConstraintSet.WRAP_CONTENT)
constrainHeight(R.id.button, ConstraintSet.WRAP_CONTENT)
这里要注意,是ConstraintSet.WRAP_CONTENT,不是以前的LayoutParams.WRAP_CONTENT !!!
同样的还有:ConstraintSet.MATCH_CONSTRAINT,表示根据约束条件自动适应至最大,等于与android:layout_width="0dp"
其他的跟尺寸有关的API还有:
constrainDefaultWidth/constrainDefaultHeight
constrainMaxWidth/constrainMaxHeight
constrainMinWidth/constrainMinHeight
设置约束
1、connect()
:最基本的API,用于建立两个控件直接的约束关系:
public void connect(int startID, int startSide, int endID, int endSide, int margin)
public void connect(int startID, int startSide, int endID, int endSide)
简单的理解:控件a(startID)的右边(startSide),要相对于控件b(endID)的左边(endSide)对齐
其中startSide和endSide用以下几种锚点常量表示:
ConstraintSet.TOP
ConstraintSet.BOTTOM
ConstraintSet.LEFT
ConstraintSet.RIGHT
ConstraintSet.START
ConstraintSet.END
ConstraintSet.BASELINE //一般用于TextView
当想设置app:layout_constraintTop_toTopOf="parent"的时候,endID可以设置为ConstraintSet.PARENT_ID
2、center()
:基于connect封装,让某一控件(centerID)置于另外两个控件(firstID & secondID)的中间:
public void center(int centerID, int firstID, int firstSide, int firstMargin, int secondID, int secondSide, int secondMargin, float bias)
3、centerHorizontally()
、 centerHorizontallyRtl()
、centerVertically
基于center()的再次封装,让一个控件横向、垂直居中与某个控件,效果等同于添加了两个约束
//控件R.id.demo2_tv添加约束
//app:layout_constraintTop_toTopOf="@+id/anchor"
//app:layout_constraintBottom_toBottomOf="@+id/anchor"
endConstraintSet.centerVertically(R.id.demo2_tv, R.id.anchor)
其他API
以上是一些基本的约束,除此之外,还有很多其他的很约束有关的API比如:
//这些都没有动画效果,但是可以通过原始的ConstraintSet还原
setVisibility
setAlpha
//下面这些设置了,无法通过原始的ConstraintSet还原
rotation/rotationX/rotationY
scaleX/scaleY
transformPivotX/transformPivotY
translationX/translationY/translationZ
setElevation
一些类似于属性动画的API
setStringValue(String attributeName, String value)
setFloatValue(String attributeName, float value)
setIntValue(String attributeName, int value)
setColorValue(String attributeName, int value)
可以通过这些API来修改控件属性,attributeName为大写
//调用TextView的setTextColor,将TextView的文本颜色设置为红色
endConstraintSet.setColorValue(R.id.demo2_tv, "TextColor", Color.RED)
同样的,类似于setRotation方法,这些API调用以后,没有动画效果,并且不能通过原始的ConstraintSet还原
还有一些其他API
constrainCircle - 设置圆周约束
createHorizontalChain/createHorizontalChainRtl/createVerticalChain - 设置水平/垂直链
addToHorizontalChain/addToHorizontalChainRtl/addToVerticalChain - 加入水平/垂直链
removeFromHorizontalChain/removeFromHorizontalChainRtl/removeFromoVerticalChain - 从水平/垂直链移除
setHorizontalChainStyle/setVerticalChainStyle - 设置水平/垂直链的类型
setHorizontalBias/setVerticalBias - 设置水平/垂直偏移
setHorizontalWeight/setVerticalWeight - 设置水平/垂直权重
setMargin/setGoneMargin - 设置间隔和约束对象消失后的间隔
setGuidelineBegin/setGuidelineEnd/setGuidelinePercent - 设置参考线的相对位置
setDimensionRatio - 设置宽高比
clear - 清理约束