ConstraintLayout之Barrier

ConstraintLayout 的Barrier是1.1版本引入的一个非常实用的功能,但是官网没有对它做任何介绍,只提了一下名字:https://androidstudio.googleblog.com/2017/05/constraintlayout-110-beta-1-release.html 。

更正:官网还对Barrier这个类做了一些介绍:https://developer.android.com/reference/android/support/constraint/Barrier.html

它跟 Guideline  一样属于Virtual Helper objects,在运行时的界面上看不到,但是要比Guideline实用多了。

constraintlayout网站(非官方)对它的介绍已经非常清晰了:https://constraintlayout.github.io/basics/barriers.html 

下面是翻译:

当我们创建布局的时候,有时会遇到布局会随着本地化变化的情况,下面是一个简单的例子:

barriers_alignment_en.png

我们有三个TextViews: 左边 textView1 和 textView2 ,右边 textView3。textView3 约束在 textView1 的右边,效果也符合我们的预期。

但是当需要支持多语言的时候事情就变得复杂了。如果我们添加德语就出现了问题,因为在英语里面textView1的文字是长于textView2的,但是在德语中却是textView2的文字比textView1长:

barriers_alignment_de.png

这里的问题在于textView3仍然是相对于textView1的,所以textView2直接插入了textView3中。在设计视图里看起来更明显(白色背景的那个)。

比较直接的解决办法是使用TableLayout,或者把 textView1 & textView2 包裹在一个垂直的,android:layout_width="wrap_content" 的 LinearLayout中。然后让textView3约束在这个LinearLayout的后面。但是我们有更好的办法:Barriers。

Barrier 是一个虚拟视图,类似于 Guideline,用来约束对象。Barrier 和 Guideline 的区别在于它是由多个 view 的大小决定的。在这个例子中,我们不知道 textView1 和 textView2 哪个长些,因此我们可以 基于这两个 view 的宽度创建一个Barrier。我们可以让 textView3 约束在 Barrier 后面。

在编辑器中创建Barriers

首先选择上下文菜单的create a vertical barrier,创建一个垂直的barrier:

注:Android Studio2.3貌似没有这个菜单,Android Studio3.0有,但是在help菜单组的下级菜单,跟下面的演示图有些区别。

barrier_create.gif

你可以在组建树(component tree)中看到Barrier(左边靠近底部的面板)。

我们可以通过拖动改变它的位置(可选项):

barrier_order.gif

接下来我们需要设置Barrier 方向(direction)。这里我们是想让Barrier根据textView1 和 textView2 的大小确定是在谁的后面,因此我们需要把 direction 设置为 end:

barrier_direction.gif

最后一步是告诉Barrier它是相对于哪些view。我不用约束来形容是因为约束一般指一对一的,而这里是多个view。我们需要为 Barrier 指定引用的view的 ID , 可以通过在 component tree 中把view拖动到 Barrier 来完成:

barrier_references.gif

一旦定义好之后,这些引用将被列为 Barrier 的 children。而且你还会在蓝色面板中看到Barrier跳到了新的位置(垂直的虚线)。

现在 Barrier 就已经定义好了,只剩下把textView3的约束从相对于 textView1 改为 相对于 Barrier 了:

barrier_constrain.gif

完了之后 textView3 就到了 textView2 的后面了。

为了看到整体的效果,可以切换语言,此时你会看到 Barrier 会自动位于较宽的那个 textView 后面,也就间接让 textView3 也位于了正确的位置:

barrier_example.gif

在XML中创建Barriers

XML代码其实也非常简单:

<?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"
  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:layout_marginStart="16dp"
    android:layout_marginTop="16dp"
    android:text="@string/warehouse"
    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:layout_marginStart="16dp"
    android:layout_marginTop="8dp"
    android:text="@string/hospital"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView1" />
  <android.support.constraint.Barrier
    android:id="@+id/barrier7"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:barrierDirection="end"
    app:constraint_referenced_ids="textView2,textView1" />
  <TextView
    android:id="@+id/textView3"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:text="@string/lorem_ipsum"
    app:layout_constraintStart_toEndOf="@+id/barrier7"
    app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

Barrier特别的地方就在于Barrier元素自身。app:barrierDirection 属性决定 Barrier 的方向 - 这里把它放在被引用view的后面。被引用的view 是布局中的view的id列表,用逗号隔开。

来自:ConstraintLayout