ConstraintLayout核心用法详解(五):Layer、ImageFilter*与MockView
整理旧电脑上的一些笔记
前言
- ConstraintLayout核心用法详解(一):相对定位、边距、偏移与环形定位
- ConstraintLayout核心用法详解(二):尺寸约束与链约束
- ConstraintLayout核心用法详解(三):引导线、屏障、组和占位
- ConstraintLayout核心用法详解(四):ConstraintSet、MotionLayout与Flow
今天继续整理 ConstraintLayout。
ConstraintLayout 的特点:
- 扁平布局,无嵌套,一个层级就可以绘制复杂布局;
- 渲染性能高;
- 集合了线性布局、相对布局、百分比布局的特点和大多数功能;
- 支持可视化环境下拖拽绘制约束布局;
- 轻松地为应用中使用的 UI 组件添加动画效果。
1. Layer
Layer 是 ConstraintLayout 2.0 加入的新的辅助类,它同 Guideline、Group 类似都是虚拟辅助。Layer 是用来控制一组视图的移动和旋转,它和 Group 的场景不一样,Group 是用来简单控制视图的可见性(VISIBILITY 或 INVISIBILITY)。
我们可以把 Layer 的效果看成是一个 ViewGroup,但是实际书写代码中并不需要把 view 包裹进去 Layer。
下面看具体 XML 代码示例:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".constraintlayout.LayerActivity">
<ImageView
android:id="@+id/iv_android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:src="@mipmap/ic_launcher"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="android"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_android" />
<androidx.constraintlayout.helper.widget.Layer
android:id="@+id/layer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="iv_android,tv_android"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/btn_rotation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="rotation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
上面 xml 代码中关键属性:
- constraint_referenced_ids:指定要控制的视图ID,如iv_android、tv_android,逗号分隔
Kotlin 代码示例:
class LayerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_layer)
val layer = findViewById<Layer>(R.id.layer)
val button = findViewById<Button>(R.id.btn_rotation)
button.setOnClickListener {
val animator = ValueAnimator.ofFloat(0f, 360f)
animator.duration = 2000
animator.addUpdateListener { animator ->
layer.rotation = animator.animatedValue as Float
layer.scaleX = 2.0f
layer.scaleY = 2.0f
layer.translationX = 100f
layer.alpha = 0.3f
}
animator.start()
}
}
}
上面代码因为比较简单,直接使用Kotlin原始方法findViewById,实际推荐使用viewBinding,减少模板代码。
运行效果图:

2.ImageFilterView和ImageFilterButton
ImageFilterView 和 ImageFilterButton 也都是 ConstraintLayout 2.0 新添加的控件。它是 Android 第一次提供原生支持图像圆角、滤镜(饱和度、亮度、对比度和色温)、交叉淡入(crossfade)等特性。
以往产品中遇到需要圆角图形,如头像、歌曲专辑封面等,往往采用自定义View或第三方控件实现,有了ImageFitlerView 就很好的解决了该场景问题。
2.1 ImageFilterView
首先介绍 ImageFilterView 支持的属性:
- altSrc:设置一个和src属性不同的图,用来实现交叉淡入淡出效果
- saturation:设置饱和度。其中0=灰度,1=原始图,2=超饱和
- brightness:设置亮度。0=全黑,1=原始图,2=亮度加倍
- warmth:设置色温。1=中性,2=暖色,0.5=冷色
- contrast:设置图像对比度。1=不变,0=灰色,2=高对比度
- crossfade:设置两张图像间的混合程度。0=完全显示src图,1=完全显示altSrc图像
- round:设置圆角半径
- roundPercent:设置圆角半径为短边长的一个比例值
- overlay:设置替代图(altSrc)是以叠加方式淡入原始图像上还是与原始图像进行交叉淡入淡出。默认=true
XML代码展示:
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/iv_avatar"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/img_avatar"
app:altSrc="@drawable/img_duck"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:round="20dp" />
Kotlin代码展示:
binding.sliderSaturation.addOnChangeListener { slider, value, fromUser ->
if (fromUser) {
val intValue = value.toInt()
Toast.makeText(this, "$intValue", Toast.LENGTH_SHORT).show()
when (intValue) {
0 -> binding.ivAvatar.saturation = 0f
1 -> binding.ivAvatar.saturation = 1f
2 -> binding.ivAvatar.saturation = 2f
}
}
}
binding.sliderBrightness.addOnChangeListener { slide, value, fromUser ->
if (fromUser) {
val intValue = value.toInt()
when (intValue) {
0 -> binding.ivAvatar.brightness = 0f
1 -> binding.ivAvatar.brightness = 1f
2 -> binding.ivAvatar.brightness = 2f
}
}
}
binding.sliderWarmth.addOnChangeListener { slide, value, fromUser ->
if (fromUser) {
val floatValue = value.toFloat()
when (floatValue) {
0.5f -> binding.ivAvatar.warmth = 0.5f
1.0f -> binding.ivAvatar.warmth = 1f
2.0f -> binding.ivAvatar.warmth = 2f
}
}
}
binding.sliderContrast.addOnChangeListener { slide, value, fromUser ->
if (fromUser) {
val intValue = value.toInt()
when (intValue) {
0 -> binding.ivAvatar.contrast = 0f
1 -> binding.ivAvatar.contrast = 1f
2 -> binding.ivAvatar.contrast = 2f
}
}
}
binding.seekbarCrossfade.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekbar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) {
val percentage = progress / 100.0f
binding.ivAvatar.crossfade = percentage
}
}
override fun onStartTrackingTouch(seekbar: SeekBar?) {
}
override fun onStopTrackingTouch(seekbar: SeekBar?) {
}
})
整体的代码示例还是很简单的,我们结合下面的效果图来加深理解即可。

2.2 ImageFilterButton
ImageFilterButton 是 AppCompatImageButton 的子类,可以用来显示、组合和过滤图像。它与上面提到的 ImageFilterView拥有相同的属性,所不一样的就是 ImageFilterButton 兼具 Button 的特性。
直接XML代码展示:
<androidx.constraintlayout.utils.widget.ImageFilterButton
android:id="@+id/btn_share"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginTop="20dp"
android:background="#E91E63"
android:src="@drawable/icon_share"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/seekbar_crossfade"
app:round="40dp" />
<androidx.constraintlayout.utils.widget.ImageFilterButton
android:id="@+id/btn_favorite"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginTop="20dp"
android:background="#E91E63"
android:src="@drawable/icon_favorite"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_share"
app:round="40dp" />
效果图:

ImageFilterButton 还是比较好理解的,它支持的饱和度、对比度等特性参考上面 ImageFilterView 示例,这里也就不再赘述了。
3. MockView
讲解 MockView 前先来看一张图。

一些产品稿中会使用上图中带对角线的矩形来占位,方便模拟一些视图,而 MockView 也正是这样的用途。
A view that is useful for prototyping layouts.
MockView 在布局中作为占位视图,模拟真实数据或复杂视图的布局效果。实际开发中,存在下面的场景:
- 真实数据还没有,比如用户头像;
- 某些View是动态加载的,比如RecyclerView加载的item;
- 某些View是有不同的状态的,比如loading、空数据提示
我们当然可以在 Java(Kotlin) 中用代码模拟假数据来实现UI效果,但当我们使用 ConstraintLayout 作为根布局时候,更推荐 MockView 来模拟这些数据存在。
下面是 MockView 的一些属性:
- mock_label:显示字体
- mock_labelColor:字体颜色
- mock_labelBackgroundColor:字体背景色
- mock_diagonalsColor:对角线颜色
- mock_showDiagonals:是否显示对角线
XML代码示例:
<androidx.constraintlayout.utils.widget.MockView
android:id="@+id/background"
android:layout_width="0dp"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:mock_label="LOGO或风景图"
app:mock_labelBackgroundColor="#9C27B0"
app:mock_labelColor="@color/white"
app:mock_showDiagonals="true" />
<androidx.constraintlayout.utils.widget.MockView
android:id="@+id/login"
android:layout_width="200dp"
android:layout_height="40dp"
android:layout_marginTop="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/background"
app:mock_diagonalsColor="#2196F3"
app:mock_label="登录"
app:mock_showDiagonals="true" />
<androidx.constraintlayout.utils.widget.MockView
android:id="@+id/register"
android:layout_width="200dp"
android:layout_height="40dp"
android:layout_marginTop="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/login"
app:mock_diagonalsColor="#2196F3"
app:mock_label="注册"
app:mock_showDiagonals="true" />
预览图:

MockView 作为辅助设计的控件,它不能设置事件而且正式发布项目前建议要移除 MockView,保持布局干净。
4.参考资料
- Developer-ConstraintLayout:https://developer.android.google.cn/reference/androidx/constraintlayout/widget/ConstraintLayout
- ConstraintLayout历史版本:https://developer.android.google.cn/jetpack/androidx/releases/constraintlayout?hl=zh_cn
- ImageFilterView:https://developer.android.google.cn/reference/kotlin/androidx/constraintlayout/utils/widget/ImageFilterView?hl=en
- ImageFilterButton:https://developer.android.google.cn/reference/androidx/constraintlayout/utils/widget/ImageFilterButton?hl=en
- MockView:https://developer.android.google.cn/reference/kotlin/androidx/constraintlayout/utils/widget/MockView?hl=en
浙公网安备 33010602011771号