项目需求讨论 — ConstraintLayout 详细使用教程

题外话

关于ConstraintLayout的文章网上一抓一大把,并且ConstraintLayout在16年就已经出来了,可是我一直没有试着去使用(别问我为何不去使用,固然是由于懒啊)。毕竟前面的LinearLayout搭配RelativeLayout用习惯了,可是毕竟能减小布局的嵌套。仍是要抱着多学习的方式去接触。因此写下文章做为总结。html

前言

你们都知道AS在写相关布局的时候,有二种方式:android

1. 拖拽方式

就是在这里进行拖控件,各类操做,由于在之前RelativeLayout和LinearLayout的年代,本身拖会自动帮咱们添加各类属性值不说,并且还很不方便,可是对于ConstraintLayout来讲添加各类约束在这里操做反而很方便,并且这里的功能面板也增长了不少新功能,方便了不少。git

固然我也很少说,贴上郭霖大神写得在这里功能面板里面对ConstraintLayout 各类操做方式: 操做面板拖拽方式来使用ConstraintLayoutgithub

2.编写代码

这种更为你们使用,而我这里也更多的是直接写代码的方式。bash

正文

控件如何肯定本身的位置

1.直接肯定控件左上角的坐标

在约束布局中,一个控件如何来肯定本身的位置呢,有人可能说直接写死让它在界面的(XXX,XXX)位置不就行了么。app

好比在拖拽界面,咱们把一个TextView拖到了界面中间。less

咱们发现这个TextView的确在中间了,这时候咱们看下它的代码:ide

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我在哪里"
        tools:layout_editor_absoluteX="164dp"
        tools:layout_editor_absoluteY="263dp" />


</android.support.constraint.ConstraintLayout>
复制代码

咱们发现了:函数

tools:layout_editor_absoluteX="164dp"
tools:layout_editor_absoluteY="263dp"
复制代码

的确咱们告诉了TextView的左上角的坐标,这个TextView的确能够肯定了位置,可是这二个属性只是单纯的进行演示,在真机操做的时候是无效的,就像"tools:text"同样,能够在写布局的时候方便查看TextView显示的文字,可是实际运行app的时候不会有相应内容。布局

并且咱们也能够看到布局文件中有错误提示,也告诉咱们在真实运行时候会跳到(0,0)位置:

This view is not constrained, it only has designtime positions, so it will jump to (0,0) unless you add constraints less...

2.告诉控件相邻的二个边的位置状况

以下图所示:

咱们怎么来肯定它们的位置?好比咱们红色的矩形A,咱们是否是告诉它:你的左边靠着外面界面的左边,你的顶边靠着外面界面的顶边(而后是否是A就处在如今这个位置了)。绿色的矩形B咱们能够告诉它:你的右边靠着外面界面的右边,你的底边靠着外面界面的底边(而后B就处在了如今这个位置)。

因此基本操做就是:肯定某个控件二个边的位置(好比靠在哪一个控件旁边)。

咱们来看最简单的基本操做: layout_constraint[本身位置]_[目标位置]="[目标ID]"

layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
复制代码

举个例子:

好比咱们A按钮已经肯定好位置了。咱们如今要放B按钮,就像咱们上面说的,咱们B按钮的二个边的位置,咱们能够设置让B按钮的左边靠着A按钮的右边(至关于B按钮的左边与A按钮的右边处于同一位置)。

<Button android:id="@+id/buttonA" ... />

<Button android:id="@+id/buttonB" ...
    app:layout_constraintLeft_toRightOf="@+id/buttonA" />
复制代码

咱们能够看到app:layout_constraintLeft_toRightOf="@+id/buttonA",B的leftidbuttonA的控件的right相同位置。因此B的左侧就和A的右侧贴在了一块儿。

咱们发现上面还有一个layout_constraintBaseline_toBaselineOf,直接看下图就能够理解全部相关的属性:

若是是相对于父布局,咱们也能够不写入另一个控件的id值,直接填parent值就能够了

<android.support.constraint.ConstraintLayout ...>
    <Button android:id="@+id/button" ...
     app:layout_constraintLeft_toLeftOf="parent"
     />
     
<android.support.constraint.ConstraintLayout/>
复制代码

因此以上就是基本的操做。咱们接下来看下其余的特殊属性。


Margin值相关

好比咱们上面的A和B按钮经过了app:layout_constraintLeft_toRightOf拼接在一块儿了,可是我同时但愿A和B按钮中间能空一些距离,以下图所示:

咱们能够直接使用:

android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
复制代码

这时候就又会有一个问题,若是这时候A的visiblegone,这时候B的位置就会自动往左边了。由于A的所占的宽度没有了(可是A在里面对于其余控件的约束性都是仍是存在的)

可是若是个人需求就是A隐藏后,B仍是在这个位置(固然有些人可能会说你可让B根据其余控件来肯定位置),并且个人B的位置就是根据A来肯定的。那咱们怎么处理,咱们能够设置B的如下属性,就是当A处于 gone的时候,咱们可让B的margin值是根据如下的属性值:

layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom

复制代码

位置约束不止二个边

咱们上面提过,二个边的位置肯定好了(也能够说二个边的位置被约束了),咱们就能够肯定这个控件的相应位置,并且还能够经过margin的改变,来继续调节控件的位置。那若是我这时候是三个边约束或者四个边都约束了呢,好比:

<android.support.constraint.ConstraintLayout ...>
    <Button android:id="@+id/button" ...
     app:layout_constraintLeft_toLeftOf="parent"
     app:layout_constraintRight_toRightOf="parent/> <android.support.constraint.ConstraintLayout/> 复制代码

咱们让按钮的左边与父布局的左边对齐,让按钮的右边与父布局的右边对齐。这时候由于不是单纯的一边对齐,而是相同直线上的二个边都被约束了。因此按钮没法紧靠着左边的或者右边的其中一个边界,因此这时候,这个按钮就会居于二个约束边界的中间位置。以下图所示:

也许也有人问,我想在这二个约束条件下时候不是处于正中间,而是处于左边三分之一的位置,这时候你可使用:

layout_constraintHorizontal_bias
layout_constraintVertical_bias
复制代码

分别是水平和垂直方向上的所占比例。

<android.support.constraint.ConstraintLayout ...>
    <Button android:id="@+id/button" ...
     app:layout_constraintHorizontal_bias="0.3"
     app:layout_constraintLeft_toLeftOf="parent"
     app:layout_constraintRight_toRightOf="parent/> </android.support.constraint.ConstraintLayout> 复制代码

圆形布局

有些需求咱们可能须要让控件以某个控件为中心,绕着进行布局,以下图所示:

ConstarintLayout自带了这些功能,咱们可使用:

layout_constraintCircle : 引用另外一个控件的id
layout_constraintCircleRadius : 距离另一个控件中心的距离
layout_constraintCircleAngle : 应该在哪一个角度(从0到360度)
复制代码

例如:

<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
  app:layout_constraintCircle="@+id/buttonA"
  app:layout_constraintCircleRadius="100dp"
  app:layout_constraintCircleAngle="45" />
复制代码

尺寸限制(Dimensions constraints)

1.对ConstraintLayout进行限制:

您能够为ConstraintLayout自己定义最小和最大尺寸:

android:minWidth设置布局的最小宽度
android:minHeight设置布局的最小高度
android:maxWidth设置布局的最大宽度
android:maxHeight设置布局的最大高度
复制代码

这些最小和最大尺寸将在ConstraintLayout使用

2.对内部的控件进行限制:

能够经过以3种不一样方式设置android:layout_widthandroid:layout_height属性来指定控件的尺寸:

  • 用特定的值(如123dp等)
  • 使用WRAP_CONTENT,它会要求控件计算本身的大小
  • 使用0dp,至关于“MATCH_CONSTRAINT”

WRAP_CONTENT(在1.1中添加)

若是设置为WRAP_CONTENT,则在1.1以前的版本中, 约束不会限制生成的尺寸值。可是在某些状况下,您可能须要使用WRAP_CONTENT,但仍然执行约束来限制生成的尺寸值。在这种状况下,你能够添加一个相应的属性:

应用:layout_constrainedWidth =”真|假”
应用:layout_constrainedHeight =”真|假”
复制代码

MATCH_CONSTRAINT尺寸(也就是0dp)(在1.1中添加)

设置为MATCH_CONSTRAINT时,默认是大小是占用全部可用空间。有几个额外的修饰符可用:

layout_constraintWidth_min和layout_constraintHeight_min:将设置此维度的最小尺寸
layout_constraintWidth_max和layout_constraintHeight_max:将设置此维度的最大尺寸
layout_constraintWidth_percent和layout_constraintHeight_percent:将设置此维度的大小为父级的百分比
复制代码

百分比尺寸(Percent Dimensions)

说到Percent Dimensions就不得不说ConstraintLayout中的0dp问题,当控件设置为0dp的时候(0dp的称呼又叫match_constraint),默认的行为是撑开(spread),占满可用空间,可是这个行为是能够用layout_constraintWidth_default 属性来设置的。在 ConstraintLayout 1.0.x中,这个属性还能够把它设置为wrap。而到了1.1.x,它又有了一个新的值:percent,容许咱们设置控件占据可用空间的百分比。

(注意:这在1.1-beta1和1.1-beta2中layout_constraintWidth_default是必须的,可是若是percent属性被定义,则在如下版本中不须要,而后将layout_constraintWidth_percent或layout_constraintHeight_percent属性设置为介于0和1之间的值)

下面的TextView控件将占据剩余宽度的50%和剩余高度的50%:

<TextView
    android:id="@+id/textView6"
    android:layout_width="0dp"
    android:layout_height="0dp"
    
    app:layout_constraintHeight_default="percent"
    app:layout_constraintHeight_percent="0.5"
    
    app:layout_constraintWidth_default="percent"
    app:layout_constraintWidth_percent="0.5" />
复制代码

宽高比(Ratio)

您还能够控制控件的height或者width这二个值,让其中一个值与另一个值的成特定的比例。为此,须要至少将一个值设置为0dp(即,MATCH_CONSTRAINT),并将属性layout_constraintDimensionRatio设置为给定比率。例如:

<Button android:layout_width="wrap_content"
   android:layout_height="0dp"
   app:layout_constraintDimensionRatio="1:1" />
复制代码

这样这个按钮的宽和高是同样大小的。

Ratio能够设置为:

  • 浮点值,表示宽度和高度之间的比率
  • “宽度:高度”形式的比率

若是两个维都设置为MATCH_CONSTRAINT(0dp),则也可使用比率: 在这种状况下,系统设置知足全部约束条件的最大尺寸并保持指定的宽高比。

为了约束一个特定的边,能够根据另外一个边的大小来限定宽度或高度: 能够经过在比率前面添加字母W(用于限制宽度)或H(用于限制高度),用逗号分隔来指示哪一边应该受到约束:

<Button android:layout_width="0dp"
   android:layout_height="0dp"
   app:layout_constraintDimensionRatio="H,16:9"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintTop_toTopOf="parent"/>
复制代码

将按照16:9的比例设置按钮的高度,而按钮的宽度将匹配父布局的约束。


链(Chains)

链在单个轴(水平或垂直)中提供相似组的行为。

  • 建立一个链: 若是一组小部件经过双向链接连接在一块儿,则认为它们是一个链,以下图所示,是一个具备二个控件的最小的链:
  • 链头: 链由在链的第一个元素(链的“头”)上设置的属性控制:
    (头是水平链最左边的部件,也是垂直链最顶端的部件。)
  • 链样式: 在链的第一个元素上设置属性layout_constraintHorizontal_chainStylelayout_constraintVertical_chainStyle时,链的行为将根据指定的样式进行更改(默认为CHAIN_SPREAD)。
    例如:
<?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">
    

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="AAAA"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/textView3"
        />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="BBBB"
        app:layout_constraintEnd_toStartOf="@id/textView4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textView2" />

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CCCC"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textView3" />
</android.support.constraint.ConstraintLayout>
复制代码

效果以下:


屏障 (Barrier)

Barrier是一个虚拟的辅助控件,它能够阻止一个或者多个控件越过本身,就像一个屏障同样。当某个控件要越过本身的时候,Barrier会自动移动,避免本身被覆盖。

关于这个控件其余文章有详细的介绍,我直接附上地址: ConstraintLayout之Barrier


组(Group)

Group帮助你对一组控件进行设置。最多见的状况是控制一组控件的visibility。你只需把控件的id添加到Group,就能同时对里面的全部控件进行操做。

<android.support.constraint.ConstraintLayout ...>
  <TextView
    android:id=”@+id/text1" ... /> <TextView android:id=”@+id/text2" ... />
  <android.support.constraint.Group
    android:id=”@+id/group”
    ...
    app:constraint_referenced_ids=”text1,text2" /> </android.support.constraint.ConstraintLayout> 复制代码

此时若是咱们调用group.setVisibility(View.GONE);那么text1 和 text2 都将不可见。


Guideline

ConstraintLayout的辅助对象的实用程序类。Guideline不会显示在设备上(它们被标记为View.GONE),仅用于布局。他们只能在ConstraintLayout中工做。

指引能够是水平的也能够是垂直的: 垂直指南的宽度为零,它们的ConstraintLayout父项的高度为零 水平指南的高度为零,其ConstraintLayout父项的宽度为零 定位准则有三种不一样的方式:

  • 指定布局左侧或顶部的固定距离(layout_constraintGuide_begin)
  • 从布局的右侧或底部指定固定距离(layout_constraintGuide_end)
  • 指定布局的宽度或高度的百分比(layout_constraintGuide_percent)

相应的代码为setGuidelineBegin(int,int),setGuidelineEnd(int,int)和setGuidelinePercent(int,float)函数。

而后控件就能够被Guideline来约束。(换句话就是说弄了一个隐藏的View,来约束咱们的控件,咱们的控件相对的就更容易进行位置定位)。

限制于垂直Guideline的按钮示例:

<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">

    <android.support.constraint.Guideline
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/guideline"
            app:layout_constraintGuide_begin="100dp"
            android:orientation="vertical"/>

    <Button
            android:text="Button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button"
            app:layout_constraintLeft_toLeftOf="@+id/guideline"
            android:layout_marginTop="16dp"
            app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

复制代码

Placeholder

你们具体使用能够看这篇文章: New features in ConstraintLayout 1.1.x。 我如下Placeholder内容也就转载这个文章里面的例子:

Placeholder顾名思义,就是用来一个占位的东西,它能够把本身的内容设置为ConstraintLayout内的其它view。所以它用来写布局的模版,也能够用来动态修改UI的内容。

用做模版: 咱们用Placeholder建立一个名为template.xml的模版:

模版写好了咱们来填充真正的东西。

咱们把刚才定义的模版include到真正的布局文件中,而且在这个布局文件中添加真实的控件,注意这里的控件无需添加任何约束,由于它们的位置是由Placeholder决定的。

还有一点就是模版要放在被引用的全部控件以前:

<?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:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.app.androidkt.constraintlayoutb.MainActivity"
    tools:showIn="@layout/activity_main">
    <include layout="@layout/template" />
    <ImageView
        android:id="@+id/top_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="fitXY"
        android:src="@drawable/place_holder_demo" />
    
    <ImageButton
        android:id="@+id/save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:srcCompat="@drawable/ic_save_black_24dp" />
 
    <ImageButton
        android:id="@+id/edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/ic_edit_black_24dp" />
 
    <ImageButton
        android:id="@+id/cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
 
        app:srcCompat="@drawable/ic_cancel_black_24dp" />
 
    <ImageButton
        android:id="@+id/delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
 
        app:srcCompat="@drawable/ic_delete_black_24dp" />
 
 
</android.support.constraint.ConstraintLayout>
复制代码

以上就是PlaceHolder的使用场景之一模版功能。

动态替换: PlaceHolder还能够在Java代码中动态替换本身的内容:

public class MainActivity extends AppCompatActivity {
  private Placeholder placeholder;
  private ConstraintLayout root;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ...
  }
  public void onClick(View view) {
    placeholder.setContentId(view.getId());
  }
}
复制代码

若是结合过渡动画的话,就能够实现一些比较有趣的效果:

public class MainActivity extends AppCompatActivity {
  private Placeholder placeholder;
  private ConstraintLayout root;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ...
  }
  public void onClick(View view) {
    TransitionManager.beginDelayedTransition(root);
    placeholder.setContentId(view.getId());
  }
}
复制代码

下面是使用PlaceHolder结合过渡动画实现的效果:

而这个Demo也是上面那篇文章做者附上的,Demo地址是 PlaceHolder动态替换


结语:

仍是老话,哪里不对。能够在留言处写出来。我会进行更正,哈哈。

相关文章
相关标签/搜索