赞
踩
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/62896784
本文出自【赵彦军的博客】
Android ConstraintLayout 约束布局详解
Android ConstraintLayout ConstraintSet动态布局
在2016年的Google I/O
大会上 , Google
发布了Android Studio 2.2
预览版,同时也发布了Android 新的布局方案 ConstraintLayout
, 但是最近的一年也没有大规模的使用。2017年Google发布了 Android Studio 2.3
正式版,在 Android Studio 2.3
版本中新建的Module中默认的布局就是 ConstraintLayout
。如下所示:
<?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="com.constraintlayout.app.Main2Activity">
</android.support.constraint.ConstraintLayout>
在使用 ConstraintLayout
的布局方案,需要在 build.gradle
引入支持库:
dependencies {
compile 'com.android.support.constraint:constraint-layout:1.0.1'
}
传统的Android开发当中,界面基本都是靠编写XML代码完成的,虽然Android Studio也支持可视化的方式来编写界面,但是操作起来并不方便,我也一直都不推荐使用可视化的方式来编写Android应用程序的界面。
而ConstraintLayout就是为了解决这一现状而出现的。它和传统编写界面的方式恰恰相反,ConstraintLayout非常适合使用可视化的方式来编写界面,但并不太适合使用XML的方式来进行编写。当然,可视化操作的背后仍然还是使用的XML代码来实现的,只不过这些代码是由Android Studio根据我们的操作自动生成的。
另外,ConstraintLayout 还有一个优点,它可以有效地解决布局嵌套过多的问题。我们平时编写界面,复杂的布局总会伴随着多层的嵌套,而嵌套越多,程序的性能也就越差。ConstraintLayout则是使用约束的方式来指定各个控件的位置和关系的,它有点类似于 RelativeLayout,但远比RelativeLayout要更强大。
ConstraintLayout向下兼容 API 9
关于 ConstraintLayout 的基本使用方法请参照郭神的博客:
http://blog.csdn.net/guolin_blog/article/details/53122387
这篇文章说一些其他的特性。
layout_constraintTop_toTopOf // 将所需视图的顶部与另一个视图的顶部对齐。
layout_constraintTop_toBottomOf // 将所需视图的顶部与另一个视图的底部对齐。
layout_constraintBottom_toTopOf // 将所需视图的底部与另一个视图的顶部对齐。
layout_constraintBottom_toBottomOf // 将所需视图的底部与另一个视图的底部对齐。
layout_constraintLeft_toLeftOf // 将所需视图的左边与另一个视图的左边对齐。
layout_constraintLeft_toRightOf // 将所需视图的左边与另一个视图的右边对齐。
layout_constraintRight_toLeftOf // 将所需视图的右边与另一个视图的左边对齐。
layout_constraintRight_toRightOf // 将所需视图的右边与另一个视图的右边对齐。
我们要实现这个例子:最上面是一个蓝色背景,头像在蓝背景底部居中的位置,我们可以用:layout_constraintTop_toBottomOf
, layout_constraintBottom_toBottomOf
属性联合使用。
完整代码如下:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/bgView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#88f"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/head_bg"
app:layout_constraintBottom_toBottomOf="@id/bgView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bgView" />
</androidx.constraintlayout.widget.ConstraintLayout>
如何实现 3个view 3 等分,互相约束就行
代码如下:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/view1"
android:layout_width="0dp"
android:layout_height="100dp"
android:background="#88f"
app:layout_constraintEnd_toStartOf="@id/view2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view2"
android:layout_width="0dp"
android:layout_height="100dp"
android:background="#ff0"
app:layout_constraintEnd_toStartOf="@id/view3"
app:layout_constraintStart_toEndOf="@id/view1"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view3"
android:layout_width="0dp"
android:layout_height="100dp"
android:background="#567"
app:layout_constraintStart_toEndOf="@id/view2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
我们我们要使最后一个图占用2倍的宽度,如何呢?
使用 layout_constraintHorizontal_weight
就行,前两个 view 的权重都是1 ,最有一个权重是 2 ,权重越大,占据比例就越大。
代码如下:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/view1"
android:layout_width="0dp"
android:layout_height="100dp"
android:background="#88f"
app:layout_constraintEnd_toStartOf="@id/view2"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view2"
android:layout_width="0dp"
android:layout_height="100dp"
android:background="#ff0"
app:layout_constraintEnd_toStartOf="@id/view3"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/view1"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view3"
android:layout_width="0dp"
android:layout_height="100dp"
android:background="#567"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintStart_toEndOf="@id/view2"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
我们再显示一个产品的价格时基线对齐十分有用,我们先看看不用基线对齐的效果:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="100"
android:textSize="40sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="%"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="@id/view1"
app:layout_constraintStart_toEndOf="@id/view1" />
</androidx.constraintlayout.widget.ConstraintLayout>
可以看到 % 跟 100 的控件底部是对齐的,这样并不好看,我们增加一个基线对齐看看,app:layout_constraintBaseline_toBaselineOf
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="100"
android:textSize="40sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="%"
android:textSize="20sp"
app:layout_constraintBaseline_toBaselineOf="@id/view1"
app:layout_constraintStart_toEndOf="@id/view1" />
</androidx.constraintlayout.widget.ConstraintLayout>
已经美观多了。
顾名思义就是基于一个控件,用角度来约束。使用角度约束需要实现3个属性,分别是:
layout_constraintCircle
定义锚点控件layout_constraintCircleRadius
定义半径layout_constraintCircleAngle
定义角度<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/view1"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="@drawable/head_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/head_bg"
app:layout_constraintCircle="@id/view1"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="150dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
我们要实现一个头像下面有一个文字,是很好处理的。
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/view1"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="@drawable/head_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"
app:layout_constraintEnd_toEndOf="@id/view1"
app:layout_constraintStart_toStartOf="@id/view1"
app:layout_constraintTop_toBottomOf="@id/view1" />
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下:
可以看到有个问题,就是文字的长度已经超出了头像的长度,有什么办法能让文字控件的长度和头像一样长。用 layout_constrainedWidth
就能实现。
代码如下:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/view1"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="@drawable/head_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
app:layout_constrainedWidth="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"
app:layout_constraintEnd_toEndOf="@id/view1"
app:layout_constraintStart_toStartOf="@id/view1"
app:layout_constraintTop_toBottomOf="@id/view1" />
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下:
可以看到文字的长度已经和头像一样宽了,这在很多社交产品中头像设置很有用。
我们做一个左边头像,右边文字的布局,就像微信聊天界面一样。
代码如下:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/view1"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/head_bg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12345678901234567890123456789012345678901234567890"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/view1"
app:layout_constraintStart_toEndOf="@id/view1" />
</androidx.constraintlayout.widget.ConstraintLayout>
但是会有一个问题,当文字太长的时候,会超出父布局边界
这时我们用一个宽度约束 layout_constrainedWidth
就可以实现不超出父布局宽度。
代码如下:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/view1"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/head_bg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12345678901234567890123456789012345678901234567890"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/view1"
app:layout_constraintTop_toTopOf="@id/view1" />
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下:
当我们把文字缩短一点看看效果:
可以看到文字居中了,这并不是我们要的效果,我们想要的是跟微信一样靠左对齐,那怎么办呢? 加上 layout_constraintHorizontal_bias
试试 。
可以看到效果已经出来了,textview 左对齐了。
我们来看下一个例子,布局左边一个头像,右边一个文字,文字在头像右边。
此时,我们给图片增加 android:visibility="gone"
属性,隐藏图片。
可以看到图片隐藏后,文字左对齐屏幕。
现在有个需求:在图片显示的时候,文字紧贴着图片。当图片隐藏的时候,文字需要有个左边距。
我们可以使用 layout_goneMarginStart
属性来处理这个需求。
可以看到增加了 app:layout_goneMarginStart="20dp"
属性后,当图片隐藏后,文字有了左边距。
下面,我们把图片显示出来看看。
图片显示了,文字的左边距消失了,完美解决。
如果几个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"
tools:context="com.czy.constraintlayoutdemo.MainActivity">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tv2"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginTop="0dp"
android:background="#ff538c"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintLeft_toRightOf="@+id/tv1"
app:layout_constraintRight_toLeftOf="@+id/tv3"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="#41c0ff"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintLeft_toRightOf="@+id/tv2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
链条分为水平链条和竖直链条两种,分别用 layout_constraintHorizontal_chainStyle
和 layout_constraintVertical_chainStyle
两个属性来设置
属性值有以下三种:
默认值为 spread
可以通过下图来理解各个参数值的含义
当参数值为 spread 以及控件宽度非 0 时
android:layout_width="wrap_content"
app:layout_constraintHorizontal_chainStyle="spread"
当参数值为 spread 以及控件宽度为 0 时
android:layout_width="0dp"
app:layout_constraintHorizontal_chainStyle="spread"
当参数值为 packed 以及控件宽度非0时
android:layout_width="wrap_content"
app:layout_constraintHorizontal_chainStyle="packed"
当参数值为 spread_inside 以及控件宽度非0时
android:layout_width="wrap_content"
app:layout_constraintHorizontal_chainStyle="spread_inside"
当属性值为 packed 时,表示把链上的控件打包在一起。作为一个整体来响应约束。
通常会与 layout_constraintHorizontal_bias
, layout_constraintVertical_bias
联合起来使用,控制 ‘包’ 控件的偏移方向。
另外一点需要注意:
链上有多个控件时 packed 只能加在第一个控件上,其他控件加了也没用。
在其他布局中,如果想根据屏幕的宽度来为 View 设置固定的宽高比的话是比较麻烦的,但在 ConstraintLayout
中可以直接设置
例如:一个 imageView
宽度是固定长度 100dp
,要想设置宽高比是 2:1
,第一步把高度设置为 0dp, 然后设置 app:layout_constraintDimensionRatio="2:1"
。
在比例约束中 ,哪个边是通过计算得出的,就要把该边设置为 0dp
如果两个边都是 0dp
, 就需要指定那条边是通过计算得出来的,比如指定高是计算出来的,需要指定 app:layout_constraintDimensionRatio="H,2:1"
,表示宽度是充满约束,高度是计算出来的
指定宽是计算出来的,指定 app:layout_constraintDimensionRatio="W,2:1"
, 表示高度是充满约束,宽度是计算出来的。
app:layout_constraintWidth_percent="0.3" //宽度百分比约束
app:layout_constraintHeight_percent="0.3" //高度百分比约束
这两个都是相对于父布局的百分比约束。
创建一个辅助线
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
给辅助线指定方向
android:orientation="vertical" //垂直方法
android:orientation="horizontal" //水平方向
指定偏移量
app:layout_constraintGuide_percent="0.5" //50% 偏移量
app:layout_constraintGuide_begin="100dp" //距离开始偏移100
app:layout_constraintGuide_end="100dp" //距离接收偏移100
百分比还支持负值 ,app:layout_constraintGuide_percent="-0.2"
那么这个辅助线在父布局的外侧。
百分比也可以设置大于1的值 ,app:layout_constraintGuide_percent="1.2"
那么这个辅助线在父布局的外侧。
当一个布局中,有多个控件需要一起隐藏或者显示的时候,我们一个一个设置 android:visibility="gone"
是比较麻烦的,我们创建一个 Group 组件。
<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
然后设置 constraint_referenced_ids
属性,就能把多个view一起控制。
<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="view1,view2" />
然后控制 group
控件的 visibility
属性,就能一起控制 view1
,view2
的visibility
属性。
Layer
约束和 Group
写法上一样的,都是通过 constraint_referenced_ids
关联其他控件。
Group
控制view的隐藏和显示Layer
控制 view 的动画,也就是说如果多个view 要做相同的动画效果,那就用 Layer
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.helper.widget.Layer
android:id="@+id/layer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="view1,view2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/view1"
android:layout_width="0dp"
android:layout_height="100dp"
android:scaleType="fitXY"
android:src="@drawable/head_bg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.3" />
<ImageView
android:id="@+id/view2"
android:layout_width="0dp"
android:layout_height="100dp"
android:scaleType="fitXY"
android:src="@drawable/head_bg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.6" />
</androidx.constraintlayout.widget.ConstraintLayout>
执行动画
class MainActivity : AppCompatActivity() {
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val layer: Layer = findViewById(R.id.layer)
layer.rotation = 270f
layer.scaleX = 0.5f
layer.translationY = 300f
}
}
Barrier 中文意思:障碍、关卡、栅栏
如果我们遇到上面的几种情况,我们看到的控件A,控件B,控件C,我们的控件A,控件B中的内容的占位的控件不定,如果我们需要根据A,B来约束C则比较的麻烦,可能我们需要将A、B再嵌套一层D,使用D来约束C。
如果我们是使用A、B中的任何一个进行约束,都有可能造成C布局和A或者B重合了的问题。
这种情形下,如果我们使用Barrier控件,则可以很容易的解决问题。
<Button
android:id="@+id/btn01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="内容A"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="内容B内容B内容B内容B内容B内容B"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn01" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C内容C"
app:layout_constraintLeft_toRightOf="@+id/barrier"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="btn01,btn02" />
我们在前面学习了Group
Layer
Barrier
, 他们都有一个共同点,继承 ConstraintHelper
。 可以使用 constraint_referenced_ids
属性关联多个 view 。
假如我们有个需求,需要同时对多个 view
操作,比如执行一段动画。我们就可以自定义 ConstraintHelper
class ViewHelper(context: Context?, attrs: AttributeSet?) : ConstraintHelper(context, attrs) {
override fun updatePostLayout(container: ConstraintLayout) {
super.updatePostLayout(container)
//遍历view id ,然后对view做动画
referencedIds.forEach {
val view = container.getViewById(it)
//执行动画
val animator = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f)
animator.duration = 3000
animator.start()
}
}
}
updatePostLayout
这个方法回调的时候,表示所有控件的布局已经完成了。
在 xml
中关联其他 view
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.myapplication.ViewHelper
android:id="@+id/viewHelper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="view1,view2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="长文本长文本长文"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/view2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="长文本长文本长"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/view1" />
</androidx.constraintlayout.widget.ConstraintLayout>
效果:
这个叫做占位属性,先看代码:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/view1"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#f00"
android:gravity="center"
android:text="view1"
android:textColor="#fff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/view2"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#ff0"
android:gravity="center"
android:text="view2"
android:textColor="#000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/view1"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/view3"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#000"
android:gravity="center"
android:text="view3"
android:textColor="#fff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/view2"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
添加一个 Placeholder
然后使用 app:content
属性指定替换的控件
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Placeholder
android:id="@+id/placeholder"
android:layout_width="100dp"
android:layout_height="50dp"
app:content="@+id/view1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/view1"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#f00"
android:gravity="center"
android:text="view1"
android:textColor="#fff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/view2"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#ff0"
android:gravity="center"
android:text="view2"
android:textColor="#000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/view1"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/view3"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#000"
android:gravity="center"
android:text="view3"
android:textColor="#fff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/view2"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
当然也可以在代码中动态的替换
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val view1: TextView = findViewById(R.id.view1)
val view2: TextView = findViewById(R.id.view2)
val view3: TextView = findViewById(R.id.view3)
val placeholder: Placeholder = findViewById(R.id.placeholder)
view1.setOnClickListener {
placeholder.setContentId(R.id.view1)
}
view2.setOnClickListener {
placeholder.setContentId(R.id.view2)
}
view3.setOnClickListener {
placeholder.setContentId(R.id.view3)
}
}
}
Flow (VirtualLayout)
也叫虚拟布局
加入某个UI开发需求如下:
开发过程中,我们最碰到的可能是这种UI样式, 在个人中心或者哪儿, 要摆上这么多个icon,文字等, 传统的写法, 用Relative, 或者Linear都会嵌套, 复制很多无脑的代码, 在2.0中,我们使用一个简单的 view
就可以一步搞定, 再也不用想着怎么摆放的相互布局啦。
flow 布局方向
android:orientation="horizontal" //水平
android:orientation="vertical" //垂直
关联view
app:constraint_referenced_ids="view1,view2,view3,view4,view5"
流式布局模式
app:flow_wrapMode="none"
app:flow_wrapMode="aligned"
app:flow_wrapMode="chain"
app:flow_horizontalGap="20dp" //水平间距
app:flow_verticalGap="20dp" //垂直间距
没有间距
有间距
当 flow_wrapMode="chain"
时,默认为一条链,自动折行, 但是不会对齐 , 我们有几个特殊的属性来控制。
flow_firstHorizontalStyle 约束第一条水平链,当有多条链(多行)时,只约束第一条链(第一行),其他链(其他行)不约束;
flow_lastHorizontalStyle 约束最后一条水平链,当有多条链(多行)时,只约束最后一条链(最后一行),其他链(其他行)不约束;
flow_horizontalStyle 约束所有水平链;
flow_firstVerticalStyle 约束第一条垂直链;
flow_lastVerticalStyle 约束最后一条垂直链;
flow_verticalStyle 约束所有垂直链;
以上属性,取值有: spread
、spread_inside
、packed
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:constraint_referenced_ids="view1,view2,view3,view4,view5,view6"
app:flow_wrapMode="chain"
app:flow_lastHorizontalStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:constraint_referenced_ids="view1,view2,view3,view4,view5,view6"
app:flow_wrapMode="chain"
app:flow_lastHorizontalStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:constraint_referenced_ids="view1,view2,view3,view4,view5,view6"
app:flow_wrapMode="chain"
app:flow_lastHorizontalStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
一个完整的示例如下:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/view1"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#f00"
android:gravity="center"
android:text="view1"
android:textColor="#fff" />
<TextView
android:id="@+id/view2"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#ff0"
android:gravity="center"
android:text="view2"
android:textColor="#000" />
<TextView
android:id="@+id/view3"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#000"
android:gravity="center"
android:text="view3"
android:textColor="#fff" />
<TextView
android:id="@+id/view4"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#000"
android:gravity="center"
android:text="view4"
android:textColor="#fff" />
<TextView
android:id="@+id/view5"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#000"
android:gravity="center"
android:text="view5"
android:textColor="#fff" />
<TextView
android:id="@+id/view6"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#000"
android:gravity="center"
android:text="view6"
android:textColor="#fff" />
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:constraint_referenced_ids="view1,view2,view3,view4,view5,view6"
app:flow_wrapMode="chain"
app:flow_lastHorizontalStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下:
篇幅较长,我另写一篇
Android ConstraintLayout ConstraintSet动态布局
最近在做项目的时候,发现此前对于 bias 理解不够充分。
bais 有水平偏移、垂直偏移
layout_constraintHorizontal_bias //控件的水平偏移比例
layout_constraintVertical_bias //控件的垂直偏移比例
举个例子,一个 view 的宽度是 a , 增加 layout_constraintHorizontal_bias="0.5"
, 并不代表说控件会直接向右移动 50% 的距离,而是有一个换算公式。
公式: bias = a /a + b
总结:
1、bias值= View左相关的长度/( View左相关的长度+其右相关的长度)
2、 由公式可以看出,bias值与左相关的长度是成正比的,增大bias值,子View的左相关总是随之增长。至于控件具体往左往右移动,则视子View与关联控件的哪边相关。
3、当 bias 是0.5 时,也就是 a = 0.5 , b = 0.5 此时 view 是居中显示。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。