ConstraintLayout使用

常用属性

宽度或者高度设置为0dp的时候表示充满约束:match constraint

权重属性(水平权重,垂直权重)

使用权重时,宽度或者高度要充满约束。

  • layout_constraintHorizontal_weight
  • layout_constraintVertical_weight

文字基准线对齐

单位前面加上数字需要以文字的下面为基准:


baseline.png
app:layout_constraintBaseline_toBaselineOf="@id/money_tv"

这样让¥的符号和前面的数字的baseline在一条线。

约束链风格

如果几个View想要一起做一些操作

chains-styles.png

约束链layout_constraintVertical_chainStyle默认风格是spread格式,就是第一个风格。
如果想要使用约束链比如第一种格式,需要几个View相互联系,相互约束才能实现。比如:

<androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="标题"
        android:textSize="30dp"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/content"/>

<androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="这是内容"/>

这样两个TextView在一起实现了屏幕居中。

宽高比例

约束布局中可以使用宽高比例来设置View的大小

app:layout_constraintDimensionRatio="1:1"

这样宽和高的比例就是1:1。
如果在比例的前面加上W或者H表示宽和高是计算得出:

app:layout_constraintDimensionRatio="W,1:1"

文字和其他View宽度一致

比如上面是一个ImageView,下面是TextViewTextView的宽度不定,而TextView最大宽度需要和ImageView宽度保持一致:

<androidx.appcompat.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="萨拉的风景案例的风景阿萨拉的风景"
        app:layout_constrainedWidth="true"
        app:layout_constraintEnd_toEndOf="@id/iv"
        app:layout_constraintStart_toStartOf="@id/iv"
        app:layout_constraintTop_toBottomOf="@id/iv" />

上面布局中的TextView宽度受到iv的约束,最大宽度不会超过iv

如果两边同时约束,但是又想偏向某个方向可是使用

  • layout_constraintHorizontal_bias横向偏向

  • layout_constraintVertical_bias竖向偏向

当一个view根据另外一个view布局,另外一个view隐藏的时候,view需要间距

  • layout_goneMarginBottom
  • layout_goneMarginEnd
  • layout_goneMarginTop
  • layout_goneMarginStart

Guideline使用

guideline相当于在布局中加了一条隐藏的线,可以以这条线为基准进行布局:
可以指定guideline为竖线还是横线。

android:orientation="vertical"
android:orientation="horizontal"

guideline的位置可以用begin或者end还有百分比percent来指定。

app:layout_constraintGuide_begin="60dp"
app:layout_constraintGuide_percent="0.2"

比如下图中我使用了两条guideline,一条是60dp的竖线和一条百分比是20%的横线:

guide_blue.png

使用方法如下:

<androidx.constraintlayout.widget.Guideline
        android:id="@+id/v_guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="60dp" />

<androidx.constraintlayout.widget.Guideline
        android:id="@+id/h_guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.2" />

<androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/user_et"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@id/h_guideline"
        app:layout_constraintStart_toStartOf="@id/v_guideline" />

根据圆的位置设置布局

设置圆心

app:layout_constraintCircle

设置角度

app:layout_constraintCircleAngle

设置半径:

app:layout_constraintCircleRadius

如果半径设置为0则放在圆心的位置。所以如果半径小的话,有可能会和圆心的view重叠。

下图表示以circle_iv为圆心,在半径200dp角度是30度的地方放了一个out_iv:

circle_bule.png

使用方法如下:

<androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/circle_iv"
        android:layout_width="80dp"
        android:layout_height="80dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

<androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/out_iv"
        android:layout_width="80dp"
        android:layout_height="80dp"
        app:layout_constraintCircle="@id/circle_iv"
        app:layout_constraintCircleAngle="30"
        app:layout_constraintCircleRadius="200dp" />

Placeholder使用

placeholder可以替换成其他的view:

<androidx.constraintlayout.widget.Placeholder
        android:id="@+id/place_holder"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginTop="100dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

<androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

当点击下面的imageview的时候可以将placeholder替换成imageview

place_holder.setContentId(R.id.iv)

进入setContentId方法:

public void setContentId(int id) {
            this.mContentId = id;   
}

这个方法会将mContentId换成要替换的id,然后执行updatePreLayout方法:

public void updatePreLayout(ConstraintLayout container) {
        this.mContent = container.findViewById(this.mContentId);
    }

根据mContentId找到view并赋值给mContent,随后进行布局:

public void updatePostMeasure(ConstraintLayout container) {
        if (this.mContent != null) {
            LayoutParams layoutParams = (LayoutParams)this.getLayoutParams();
            LayoutParams layoutParamsContent = (LayoutParams)this.mContent.getLayoutParams();
            layoutParamsContent.widget.setVisibility(0);

 //将要替换view的宽高赋值给placeholder
            layoutParams.widget.setWidth(layoutParamsContent.widget.getWidth());
            layoutParams.widget.setHeight(layoutParamsContent.widget.getHeight());
//再将要替换的view隐藏
            layoutParamsContent.widget.setVisibility(8);
        }
    }

最后执行onDraw方法。

如果想要对一些View做整体操作比如隐藏或者显示可以使用Group

<androidx.constraintlayout.widget.Group
        android:id="@+id/group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="tv"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
group.visibility = View.GONE

constraint_referenced_ids里面的View就会全部隐藏。

Barrier使用

barrier表示屏障的意思,比如有多个view,总是以最长的view为最大宽度,然后barrier后面再添加其他view

barrier.png

tv1tv2的后面并且以这两个最宽的为屏障在后面添加iv控件。

<androidx.constraintlayout.widget.Barrier
        android:id="@+id/bi"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="tv1, tv2" />

constraint_referenced_ids表示作用宽度的控件。
barrierDirection表示添加到那些控件的位置。

Group和Barrier都是继承ConstraintHelper

我们可以根据需求来自定义ConstraintHelper
ConstraintHepler中设置的View个数默认是32个:

protected int[] mIds = new int[32];

ConstraintLayout的onMeasure中执行

private void updatePostMeasures() {
        helperCount = this.mConstraintHelpers.size();
        if (helperCount > 0) {
            for(int i = 0; i < helperCount; ++i) {
                ConstraintHelper helper = (ConstraintHelper)this.mConstraintHelpers.get(i);
                helper.updatePostMeasure(this);
            }
        }

    }

onLayout中执行:
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
helperCount = this.mConstraintHelpers.size();
if (helperCount > 0) {
for(int i = 0; i < helperCount; ++i) {
ConstraintHelper helper = (ConstraintHelper)this.mConstraintHelpers.get(i);
helper.updatePostLayout(this);
}
}

}

可以发现会执行ConstraintHelperupdatePostMeasureupdatePostLayout方法,可以在这两个方法中对View做一些操作。

使用ConstraintSet自定义布局或者替换Layout

val constraintLayout = view as ConstraintLayout
        val constraintSet = ConstraintSet().apply {
            clone(this@TestActivity, "要替换的布局")
        }
        //添加过渡动画
        TransitionManager.beginDelayedTransition(constraintLayout)
        constraintSet.applyTo(constraintLayout)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355