ConstraintLayout的布局优越性已经不必再强调了,经过ConstraintLayout的束缚思想,能够很便利的处理一些之前需求写很复杂的动态代码才能完结的效果。

早在2016年,我就已经逐步将项目中的布局进行束缚化,采用ConstraintLayout来替换原有布局,同时对ConstraintLayout的根底运用,进行了总结,感兴趣的入门开发者能够参考下面的文章。

blog.csdn.net/eclipsexys/…

国际惯例,官网镇楼,这是入门ConstraintLayout最好的材料。

developer.android.com/training/co…

当然,ConstraintLayout并不是处理所有布局问题的银弹,鄙人面的这些场景下运用,能够算得上ConstraintLayout的最佳实践,能够达到事半功倍的效果。

固定份额视图

考虑下面这个场景,组件宽度撑满屏幕,高度按「宽度x固定份额」核算。

这样的布局,在以往的布局方法下,都需求经过动态核算后修改高度来完结,可是经过ConstraintLayout,则能够直接在XML中完结。

<ImageView
    android:layout_width="match_parent"
    android:layout_height="@dimen/length_0"
    app:layout_constraintDimensionRatio="1:0.34"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

经过DimensionRatio,能够很便利的完结份额视图的控制,同时,份额能够设置的很灵活,满足各种条件的需求。

N等分布局

常见的N等分布局,例如三等分布局,通常都需求进行动态核算,依据屏幕宽度,减去距离后得到每部分的宽度,再动态设置给每个元素,而经过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="wrap_content"
    android:layout_marginLeft="@dimen/length_16"
    android:layout_marginRight="@dimen/length_16">
    <ImageView
        android:id="@+id/bookCover1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="0.74:1"
        app:layout_constraintEnd_toStartOf="@+id/bookCover2"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_percent="0.30"
        tools:srcCompat="@tools:sample/avatars" />
    <ImageView
        android:id="@+id/bookCover2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:visibility="invisible"
        app:layout_constraintDimensionRatio="0.74:1"
        app:layout_constraintEnd_toStartOf="@+id/bookCover3"
        app:layout_constraintStart_toEndOf="@+id/bookCover1"
        app:layout_constraintTop_toTopOf="@+id/bookCover1"
        app:layout_constraintWidth_percent="0.30"
        tools:srcCompat="@tools:sample/avatars" />
    <ImageView
        android:id="@+id/bookCover3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="0.74:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/bookCover2"
        app:layout_constraintTop_toTopOf="@+id/bookCover2"
        app:layout_constraintWidth_percent="0.30"
        tools:srcCompat="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>

效果如下图所示。

ConstraintLayout使用场景必知必会

这其中的距离,主要是经过layout_constraintWidth_percent来设置在当时容器尺度下所占百分比来进一步束缚大小。

假如去掉这个特点,那么会直接等分父容器尺度。

ConstraintLayout使用场景必知必会

别的,还能够经过layout_constraintHorizontal_weight特点来控制相似LinearLayout的weight特点的效果,完结按权重进行分配。

ConstraintLayout使用场景必知必会

复杂的元素相对居中

在整个View中,针对某个固定元素,其它的元素环绕它做的各种对齐方法,在之前是很难直接完结的,即使是运用-margin的方法,也很难完结动态可变尺度的居中,而在ConstraintLayout中,这就变得很简单了。

ConstraintLayout使用场景必知必会

代码就不贴了,ConstraintLayout基操。

百分比对齐

在ConstraintLayout中,尽管不能运用-margin的方法来完结传统布局中的一些错位的效果,可是能够凭借Space来完结相似的功能,例如凭借Space来完结左边TextView在右边TextView某一百分比(或者是dp)对齐的场景。

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">
    <TextView
        android:id="@+id/textView2"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="32dp"
        android:background="#bebebe"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Space
        android:id="@+id/space"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="@+id/textView2"
        app:layout_constraintHorizontal_bias="0.2"
        app:layout_constraintStart_toStartOf="@+id/textView2"
        tools:layout_editor_absoluteY="68dp" />
    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        app:layout_constraintEnd_toStartOf="@+id/space"
        tools:layout_editor_absoluteY="92dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

由于ConstraintLayout不支持-Margin,所以许多场景下,咱们都能够凭借Space等辅助元从来完结中转,完结传统布局下经过-Margin完结的效果。

视点布局

经过视点的方法来对元素进行摆放,在传统布局中,只能经过FrameLayout,并经过动态核算的方法,将视点换算为边距的方法来布局,但经过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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="30"
        app:layout_constraintBottom_toTopOf="@+id/textView1"
        app:layout_constraintCircle="@id/textView1"
        app:layout_constraintCircleAngle="30"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintStart_toEndOf="@+id/textView1" />
    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="60"
        app:layout_constraintBottom_toTopOf="@+id/textView1"
        app:layout_constraintCircle="@id/textView1"
        app:layout_constraintCircleAngle="60"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintStart_toEndOf="@+id/textView1" />
    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="90"
        app:layout_constraintBottom_toTopOf="@+id/textView1"
        app:layout_constraintCircle="@id/textView1"
        app:layout_constraintCircleAngle="90"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintStart_toEndOf="@+id/textView1" />
</androidx.constraintlayout.widget.ConstraintLayout>

ConstraintLayout使用场景必知必会

这种布局的方法,触及的特点如下。

layout_constraintCircleAngle
layout_constraintCircleRadius
layout_constraintStart_toEndOf

经过这几个特点就能够很便利的依照视点坐标来进行布局。

全体居中

经过Chain能够完结多个元素在边际束缚的场景下居中的效果,如图所示。

ConstraintLayout使用场景必知必会

这也是ConstraintLayout基操,不细说了。

超长束缚强制束缚

考虑下面这个场景,最下面的TextView最大不会超过第一个TextView的宽度。

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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/textView2"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:ellipsize="end"
        android:singleLine="true"
        android:text="TextViewTextViewTextViewTextViewTextViewTextViewTextViewTextViewTextViewTextView"
        app:layout_constrainedWidth="true"
        app:layout_constraintEnd_toEndOf="@+id/textView2"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/textView2"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />
</androidx.constraintlayout.widget.ConstraintLayout>

效果如下所示。

ConstraintLayout使用场景必知必会

这时分就需求经过运用constrainedWidth来使其宽度束缚强制收效。

相似的,再考虑下面这个场景。

ConstraintLayout使用场景必知必会

当第二个TextView文字超长的时分,期望它切断,而不会影响左右的TextView。这个场景十分常用,在许多事务场景下都会运用到这样的功能,传统布局下,只能在布局时动态核算文字宽度来进行动态修改,但经过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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="32dp"
        android:text="TextView"
        app:layout_constraintEnd_toStartOf="@+id/textView5"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:ellipsize="end"
        android:singleLine="true"
        android:text="TextView"
        app:layout_constrainedWidth="true"
        app:layout_constraintEnd_toStartOf="@+id/textView6"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/textView4"
        app:layout_constraintTop_toTopOf="@+id/textView4" />
    <TextView
        android:id="@+id/textView6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="32dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/textView5"
        app:layout_constraintTop_toTopOf="@+id/textView5" />
</androidx.constraintlayout.widget.ConstraintLayout>

ConstraintLayout使用场景必知必会

多组件协同束缚

考虑下面这个场景,多个组件的宽度不定,需求取最大宽度的组件在布局中展现,例如下面这个比如。

Email和Password两个TextView的宽度或许由于文字的不一样而不同,需求他们全体取最大宽度后,与右边元素进行对齐,如下所示。

ConstraintLayout使用场景必知必会

这时分,就需求运用Barrier。Barrier能够理解为一个栅栏,Barrier和Group一样,经过constraint_referenced_ids来组合需求效果的组件,代码如下。

<?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="wrap_content">
    <TextView
        android:id="@+id/email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:textSize="24sp"
        app:layout_constraintBottom_toTopOf="@+id/password"
        app:layout_constraintStart_toStartOf="@+id/password"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed"
        tools:text="E-mail Address" />
    <EditText
        android:id="@+id/emailInput"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:ems="10"
        android:inputType="textEmailAddress"
        android:text="xys@gmail.com"
        app:layout_constraintBaseline_toBaselineOf="@+id/email"
        app:layout_constraintStart_toEndOf="@+id/barrier" />
    <TextView
        android:id="@+id/password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Password"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/email"
        tools:layout_editor_absoluteX="11dp" />
    <EditText
        android:id="@+id/passwordInput"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:ems="10"
        android:inputType="textPassword"
        android:text="666666"
        app:layout_constraintBaseline_toBaselineOf="@+id/password"
        app:layout_constraintStart_toEndOf="@+id/barrier" />
    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="right"
        app:constraint_referenced_ids="email,password" />
</androidx.constraintlayout.widget.ConstraintLayout>

其中barrierDirection设置为right,即右侧不超过Barrier,再让剩余组件与Barrier进行束缚即可。

容器束缚下的边界束缚

考虑下面这个场景,中心的TextView被束缚在两边的组件中,如下所示。

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">
    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_marginStart="32dp"
        android:layout_marginTop="32dp"
        android:background="#bebebe"
        android:text="TextView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="32dp"
        android:background="#bebebe"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/textView6"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:text="TextViewTextViewTextViewTextViewTextViewTextView"
        app:layout_constraintEnd_toStartOf="@+id/textView5"
        app:layout_constraintStart_toEndOf="@+id/textView4"
        tools:layout_editor_absoluteY="73dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

在个比如的重点是将layout_width设置为0dp,即MATCH_CONSTRAINT,即可完结这样的效果。

下面进一步思考下这个场景。

当TextView文字较少时,能够发现其尺度是默许占据了整个束缚空间,这时分,假如要求TextView只显示文字大小,相似设置wrap_content的效果,可是在文字长的时分,又必须被边际束缚,所以又不能设置wrap_content,这种场景下,能够经过layout_constraintWidth_default特点来处理,它提供了边际束缚下默许的尺度设置方法。

前面说的相似wrap_content的效果,就能够运用wrap来设置。

app:layout_constraintWidth_default="wrap"

ConstraintLayout使用场景必知必会

当然,不设置这个特点,将TextView的宽度设置为wrap_content,也是能够完结这个效果的,这就需求运用到前面讲的constrainedWidth特点了。

layout_constraintWidth_default的默许值为spread,即占据边际束缚下的所有空间。

总结

ConstraintLayout的学习曲线比较峻峭,入门很简单,想要写好,却是很难的,大部分的开发者在经过一段时间的学习后,都能够上手进行布局,可是遇到一些比较复杂的事务场景时,就很难将ConstraintLayout的这些特性融会贯通了,所以,运用ConstraintLayout,有下面这些原则。

  • 找准布局基准元素,一般是界面的固定不变的事务元素,其它组件,依据其束缚来进行布局
  • 运用Group等虚拟布局组件来简化布局代码
  • 对ConstraintLayout的特性需求掌握熟练,特别是上面这些场景,需求手到擒来
  • 修改ConstraintLayout时,先理清束缚联系再下手,避免上手就拖组件,导致剪不断理还乱

再次重申,ConstraintLayout并不是Android布局的银弹,适宜的场景挑选适宜的布局方法,才是最重要的。