Android ConstraintLayout 使用与适配(使用篇)

本文主要涉及以下几篇文章

Android 屏幕适配总结

Android ConstraintLayout 使用与适配(使用篇)

Android ConstraintLayout 使用与适配(适配篇)

前言:官方把 ConstraintLayout  扶正(取代以前五种布局方式,如项目创建即使用 ConstraintLayout  做根布局)好久了,但是一直没有当回事。最近重新关注了一下 Android 屏幕适配,现在官方推荐使用 ConstraintLayout 来解决适配的问题,因此好好看看了相关的文章。开始看的时候有些不习惯。原因是总以之前的五种布局思维去理解 ConstraintLayout,因此会很别扭。网上的相关文章大多也是这样对比讲解的。但我觉得应该完全抛弃以前的布局思维,以一种全新的思维方式去理解 ConstraintLayout 更合适,以下是我的理解方式:

一、使用

1.约束

    为控件添加一个约束很简单,只要选中控件,然后拖拽四个圆圈到想约束的控件即可,如下

图1

    约束是 ConstraintLayout 的核心概念,约束在 ConstraintLayout 中可以理解为一个控件(约束控件)与另一个控件(被约束控件)的位置关系。要决定 控件B 的位置,就要给 控件B 添加相对 控件A 的约束。控件A(约束控件),控件B(被约束控件)。

    图1的操作中,ConstraintLayout 就是约束控件,Button 是被约束控件。当约束 Button 到 ConstraintLayout 右边时,Button 就被固定在了 ConstraintLayout  的右边。此时再约束 Button 到 ConstraintLayout 左边,就会产生水平方向的冲突(一个控件不能同时被固定在左右两边的位置,除非 Button 的大小与 ConstraintLayout 完全一样,否则无法同时满足这两个约束)。当发生这种冲突的时候,约束会自动进行处理,约束会自动把 Button控件 移动至两个约束的中间(即出现 Button 被居中的现象),此时 Button控件 便可设置 Bias 属性,并在编辑区右边出现水平偏移轴,都是用来设置 Button控件 在水平方向位置的。如下图

2.Bias 属性

    在两个冲突约束出现时,可以设置 Bias 属性来控制控件在两个约束间的位置,取范围是 0 ~ 1 之间,可以使用小数,最终按设置值的百分比来进行位置定位。如 0.2 就是两个冲突约束的 20% 的位置,0.375 就是两个冲突约束的 37.5% 的位置。

    Bias 属性有两个:

        layout_constraintHorizontal_bias (水平轴)

        layout_constraintVertical_bias (垂直轴)

    当冲突约束发生,如果不设置 Bias 属性,会按默认值 50% 的方式进行摆放,即默认值为 0.5

    对图1设置 layout_constraintHorizontal_bias (水平轴) 为 0.275 的效果如下

    <Button
        ...
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.275"
        app:layout_constraintStart_toStartOf="parent"
        ... />

注:Bias 属性在实际使用中有一个小小的注意点,在 Android ConstraintLayout 使用与适配(适配篇) 说明这个问题。

3.圆形定位

    圆形定位比较好理解,即以一个控件为圆心,让其他控件以此控件为中心点进行圆形摆放,相关属性有三个

        layout_constraintCircle :引用圆心部件的ID
        layout_constraintCircleRadius :到圆心部件中心的距离
        layout_constraintCircleAngle :当前部件相对圆心部件的角度(以度为单位,从0到360)

    下面是 6 个 ImageView 以 Button 为圆心半径是 100dp 分别以 60 度为倍数摆放的代码与样式

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

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintCircle="@+id/button"
        app:layout_constraintCircleAngle="0"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintEnd_toEndOf="@+id/button"
        app:srcCompat="@android:drawable/btn_star_big_on" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintCircle="@+id/button"
        app:layout_constraintCircleAngle="60"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintEnd_toEndOf="@+id/button"
        app:srcCompat="@android:drawable/btn_star_big_on" />

    <ImageView
        android:id="@+id/imageView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintCircle="@+id/button"
        app:layout_constraintCircleAngle="120"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintEnd_toEndOf="@+id/button"
        app:srcCompat="@android:drawable/btn_star_big_on" />

    <ImageView
        android:id="@+id/imageView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintCircle="@+id/button"
        app:layout_constraintCircleAngle="180"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintEnd_toEndOf="@+id/button"
        app:srcCompat="@android:drawable/btn_star_big_on" />

    <ImageView
        android:id="@+id/imageView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintCircle="@+id/button"
        app:layout_constraintCircleAngle="240"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintEnd_toEndOf="@+id/button"
        app:srcCompat="@android:drawable/btn_star_big_on" />

    <ImageView
        android:id="@+id/imageView6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintCircle="@+id/button"
        app:layout_constraintCircleAngle="300"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintEnd_toEndOf="@+id/button"
        app:srcCompat="@android:drawable/btn_star_big_on" />

</androidx.constraintlayout.widget.ConstraintLayout>

4.链式布局

    如果两个以上的控件相互约束(例如:控件A 的右边约束在 控件B 的左边,同时 控件B 的左边又约束在 控件A 的右边),即为一个链式布局(注:无法在操作页面使用鼠标拖动添加两个控件为相互约束,只有在代码中编写)。链式布局的第一个控件被称为:链头,针对链头可以设置 chainStyle 属性,来控制链式布局的样式。chainStyle 属性也有两个,分别对应:水平链 与 垂直链

        layout_constraintHorizontal_chainStyle (水平链样式)

        layout_constraintVertical_chainStyle (垂直链样式)

    chainStyle 属性只有设置在首链中才起作用,他有三个值分别是:

        spread:展开样式(首尾控件不固定,所有控件平均展开),也是默认值。

        spread_inside:内部展开样式(首尾控件固定,首尾中间的控件平均展开)。

        packed:打包样式(所有控件将连接在一起,并可设置 Bias 属性,来确定链式布局的位置)

各样式效果与代码如下

展开样式
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button3"
        app:layout_constraintStart_toEndOf="@+id/button1"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintStart_toEndOf="@+id/button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
内部展开样式
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button3"
        app:layout_constraintStart_toEndOf="@+id/button1"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintStart_toEndOf="@+id/button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
打包样式+设置Bias属性(不设置 Bias 属性默认居中显示)
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintHorizontal_bias="0.2"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button3"
        app:layout_constraintStart_toEndOf="@+id/button1"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintStart_toEndOf="@+id/button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

除了可以设置链式布局显示的样式外,还可以通过设置权重来控制链式布局中每个控件的大小,使用权重需要设置控件宽度或高度为0dp(或 match_constraint),再设置 layout_constraintHorizontal_bias 或 layout_constraintVertical_bias 属性,如下

权重链
    <Button
        android:id="@+id/button1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button3"
        app:layout_constraintStart_toEndOf="@+id/button1"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/button2"
        app:layout_constraintTop_toTopOf="parent" />

二、辅助工具

辅助工具用于帮助控件在特殊情况下的约束处理(让控件显示在什么位置相应规则),辅助工具只是起辅助的作用,在程序运行期间不会显示。

1.Guideline(指导线)

指导线用于指定 X 或 Y 坐标,然后让控件约束在以 X 或 Y 坐标上。

添加指导线的方式如下

在此可添加水平或垂直,两种指导线,也可直接使用代码添加

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" />

android:orientation 属性指定 Guideline 的样式,vertical  是垂直线,horizontal 是水平线。

此外它还有三个属性用来设置定位方式,定位的方式是与父布局 ConstraintLayout 边距。在同一个 Guideline 中三个属性只有

    app:layout_constraintGuide_begin="20dp" 依据开始位置定位。水平线是上边距,垂直线是左边距。

    app:layout_constraintGuide_end="20dp" 依据结束位置定位。水平线是下边距,垂直线是右边距。

代码与效果如下

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_end="20dp"/>
水平线 end 属性
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="100dp"/>
垂直线 begin 属性

上边两种是 dp 的方式,还有一种按百分比设置的方式:

    app:layout_constraintGuide_percent="0.2" 依据父布局百分比定位。水平线依据屏幕高度的百分比,垂直线是屏幕宽度的百分比。取值范围是 0~1,0.2 即  20%。

代码与效果如下

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

    

水平线 20%

2.Barrier(屏障)

屏障一般用在下面的场景:控件A,控件B的宽度都是自适应的,在程序运行期间有可能 控件A 的宽度比 控件B 的宽度大,也有可能比 控件B 宽度小,此时 控件C 需要一直显示在 控件A 和 控件B 的右边。这时可以为 控件A 和 控件B 添加屏障,让 控件C 约束在屏障的右边,即可实现效果。

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="控件A"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.1"
        app:layout_constraintWidth_default="percent"
        app:layout_constraintWidth_percent="0.5" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="控件B:自动增长,又增长"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.2"
        app:layout_constraintWidth_percent="0.2" />

    <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="textView1,textView2"/>

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="控件C"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/barrier"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.149" />

Barrier 有主要的两个属性

    app:constraint_referenced_ids(设置屏障与哪些控件建立约束,填写控件id,多个 id 使用逗号 "," 分割)

    app:barrierDirection (设置 Barrier 显示在约束控件的方向:top,left,bottom,right 以及 start【同left】,end【同right】)

3.Group(组)

组的作用是可将多个控件一起隐藏或显示 。如下有三个 TextView 

<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="TextView1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.07" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="608dp"
        android:text="TextView2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.399"
        app:layout_constraintStart_toEndOf="@+id/textView2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.109" />

    <androidx.constraintlayout.widget.Group
        android:id="@+id/group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="visible"
        app:constraint_referenced_ids="textView1,textView2,textView3" />

</androidx.constraintlayout.widget.ConstraintLayout>

在 Group 中还是使用 app:constraint_referenced_ids 属性将三个 TextView 归为一组,visibility 控制整组的显示与隐藏。

4. Placeholder(占位符)

占位符是一个可把其他控件移动到本身位置的控件,把它约束在屏幕的任何位置,然后在代码中通过设置其 setContent(id) 属性,可以将其他控件移动到当前占位符的位置。如下是带动画效果的占位符

布局文件,占位符被放在屏幕中间

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/constraint"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@android:drawable/sym_def_app_icon" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="66dp"
        android:layout_height="66dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@android:drawable/star_big_on" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="Button1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.85" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:onClick="click2"
        android:text="Button2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/button1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.85" />

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

</androidx.constraintlayout.widget.ConstraintLayout>

Activity,如果添加动画需要在 setContent(id) 前为根布局 ConstraintLayout 设置动画即可。 

class PlaceHolderActivity : AppCompatActivity() {

    private var placeholder: Placeholder? = null
    private var constraintLayout: ConstraintLayout? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(R.layout.placeholder)
        supportActionBar?.hide()
        placeholder = this.findViewById(R.id.placeholder)
        constraintLayout = this.findViewById(R.id.constraint)
    }

    fun click(view: View) {
        //设置动画
        TransitionManager.beginDelayedTransition(constraintLayout)
        placeholder?.setContentId(R.id.imageView1)
    }

    fun click2(view: View) {
        //设置动画
        TransitionManager.beginDelayedTransition(constraintLayout)
        placeholder?.setContentId(R.id.imageView2)
    }
}
发布了17 篇原创文章 · 获赞 46 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/xiaojinlai123/article/details/103331504