Android 设备在运行,具有不同的尺寸和目标,如手机、平板电脑、电视、汽车、嵌入式硬件等,Android UI 已成为 Android 用户和开发人员非常有影响力的部分。随着构建复杂 UI 的新兴需求,google为了更好的解决适配问题,提供出来ConstraintLayout 约束布局。
为什么要约束布局?
在 Constraint Layout 之前,Android UI 是由线性布局、Frame Layout 和 RelativeLayout 等几个 View Group 创建的。在 Android 开发历史中,这些布局在创建 UI 时非常有用。嵌套是 Android 开发人员经常处理的主要问题之一——尤其是在使用上述布局时。这不仅会使布局文件变大而难以理解,而且还会影响 UI 渲染的性能。
ConstraintLayout
是 Android 团队新发布的 View Group,它解决了嵌套和性能等问题。这不仅可以帮助开发人员构建更复杂和更大的 UI,而且还带有扁平的层次结构。本系列文章将借助真实的 UI 示例逐步解释 ConstraintLayout 中的所有功能;
在项目中添加 ConstraintLayout
要在项目中使用 ConstraintLayout,请将库作为依赖项添加到应用级build.gradle
文件中。如果您的项目已经具有依赖项,则这不是必需的。
implementation "androidx.constraintlayout:constraintlayout:$constraintVersion"
添加约束
设置依赖项后,您可以ConstraintLayout
通过添加如下代码来添加:
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="@dimen/dp_60"> <androidx.constraintlayout.widget.Guideline android:id="@+id/on_car_guide" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_begin="@dimen/dp_130" /> <TextView android:id="@+id/textView" android:layout_width="@dimen/dp_70" android:layout_height="wrap_content" android:layout_marginStart="@dimen/dp_10" android:text="扫描区" android:textColor="@color/color_333" android:textSize="@dimen/sp_16" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.appcompat.widget.AppCompatEditText android:id="@+id/scan_area_view" style="@style/CommonEditStyle" android:layout_width="0dp" android:layout_height="@dimen/dp_40" android:hint="请扫描或输入" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/btn_scan_enter" app:layout_constraintLeft_toRightOf="@id/on_car_guide" app:layout_constraintRight_toLeftOf="@id/btn_scan_enter" app:layout_constraintStart_toEndOf="@+id/textView" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/btn_scan_enter" style="@style/CommonButtonStyle" android:layout_width="@dimen/dp_65" android:layout_height="@dimen/dp_40" android:layout_marginEnd="@dimen/dp_10" android:backgroundTint="@color/color_34c" android:text="确定" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
视图和小部件(例如按钮、文本等)被添加到 ConstraintLayout 标签内,然后成为所有嵌套元素的父级。在上面的代码中,设备的屏幕作为 ConstraintLayout 的父级。
在约束布局中,每个视图有四个边,称为锚点(看下图中的圆圈),这些锚点 作为约束的源或目标操作
要定义视图的位置,我们必须为其添加至少一个水平约束和一个垂直约束。要设置视图的水平或垂直位置,您需要使用 ConstraintLayout 中提供的一些约束。
这些约束的编写如下图所示。请注意,Source 和 Target 是一些视图,例如按钮、复选框等。您可以使用任何其他视图的 ID 或父视图来引用这些约束。根布局的父级将是设备本身,在嵌套布局的情况下,父级将是包含该特定视图的布局。
layout_constraintTop_toTopOf
这个约束告诉源视图的顶部应该与目标视图的顶部相同。如下图所示,绿色和蓝色视图的顶层位于相同的垂直位置。
layout_constraintBottom_toBottomOf
这个约束告诉源视图的底部应该与目标视图的底部相同。如下图所示,绿色和蓝色视图的底层处于相同的垂直位置。
layout_constraintBottom_toTopOf
这个约束告诉源视图的底部应该与目标视图的顶部相同。如下图所示,底层的绿色视图和顶层的蓝色视图处于相同的垂直位置。
layout_constraintTop_toBottomOf
这个约束告诉源视图的顶部应该与目标视图的底部相同。如下图所示,顶层的绿色视图和底层的蓝色视图处于垂直位置
layout_constraintStart_toStartOf
这个约束告诉源视图的开始应该与目标视图的开始相同。如下图所示,绿色和蓝色视图的起始层位于垂直位置。
layout_constraintEnd_toEndOf
这个约束告诉源视图的结束应该与目标视图的结束相同。如下图所示,绿色末端层和蓝色末端层视图处于垂直位置。
layout_constraintEnd_toStartOf
这个约束告诉源视图的结束应该与目标视图的开始对齐。如下图所示,绿色的结束层和蓝色视图的开始层位于垂直位置。
layout_constraintStart_toEndOf
这个约束告诉源视图的开始应该与目标视图的结束对齐。如下图所示,绿色的起始层和蓝色视图的结束层位于垂直位置。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/container" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/bg_login" android:layout_width="fill_parent" android:layout_height="fill_parent" android:src="@drawable/icon_bg_login" android:scaleType="centerCrop" 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/bg_login_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="76.0dip" android:scaleType="centerCrop" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/password" android:visibility="gone" android:layout_width="200.0dip" android:layout_height="wrap_content" android:hint="输入密码" android:selectAllOnFocus="true" android:inputType="textPassword" android:imeOptions="actionDone" android:imeActionLabel="t" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <FrameLayout android:id="@+id/status_bar" android:layout_width="fill_parent" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:textSize="12.0sp" android:textColor="@color/white" android:gravity="center" android:id="@+id/buttonXianGuang" android:background="@drawable/icon_xianguang" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="17.0dip" android:text="先逛逛" android:layout_marginEnd="9.0dip" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/status_bar" /> <LinearLayout android:orientation="vertical" android:id="@+id/edit_layout" android:layout_width="fill_parent" android:layout_height="wrap_content" app:layout_constraintBottom_toTopOf="@+id/reader"> <LinearLayout android:gravity="center_vertical" android:background="@drawable/icon_bg_edittext" android:layout_width="fill_parent" android:layout_height="54.0dip" android:layout_marginLeft="30.0dip" android:layout_marginRight="30.0dip" android:layout_marginBottom="10.0dip" app:layout_constraintBottom_toTopOf="@+id/login"> <TextView android:textSize="18.0sp" android:textStyle="bold" android:textColor="#ffffffff" android:gravity="center" android:id="@+id/city_select" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginLeft="25.0dip" android:text="+86" android:drawableRight="@drawable/ic_round_arrow_drop_down_24" /> <View android:background="#ffbbbbbb" android:layout_width="1.0dip" android:layout_height="30.0dip" android:layout_marginLeft="12.0dip" /> <EditText android:textSize="24dp" android:textColor="@color/white" android:gravity="center_vertical" android:id="@+id/username" android:background="@null" android:layout_width="0.0dip" android:layout_height="fill_parent" android:layout_marginLeft="20.0dip" android:hint="输入邮箱" android:maxLines="1" android:singleLine="true" android:selectAllOnFocus="true" android:layout_weight="1.0" android:inputType="phone" /> <ImageView android:id="@+id/button_clear" android:visibility="gone" android:layout_width="18.0dip" android:layout_height="18.0dip" android:layout_marginRight="20.0dip" android:src="@drawable/icon_clear_text" /> </LinearLayout> <com.google.android.material.button.MaterialButton android:enabled="false" android:textSize="16.0sp" android:textColor="@color/white" android:id="@+id/login" android:layout_width="fill_parent" android:layout_height="54.0dip" android:layout_marginLeft="30.0dip" android:layout_marginRight="30.0dip" android:layout_marginBottom="12.0dip" android:text="确定" android:insetTop="0.0dip" android:insetBottom="0.0dip" app:backgroundTint="@null" app:cornerRadius="27.0dip" app:layout_constraintBottom_toTopOf="@+id/reader" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:rippleColor="@null" style="@style/Widget.MaterialComponents.Button.UnelevatedButton" /> </LinearLayout> <LinearLayout android:gravity="center" android:orientation="horizontal" android:id="@+id/reader" android:paddingTop="10.0dip" android:paddingBottom="10.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="12.0dip" app:layout_constraintBottom_toTopOf="@+id/phone_one_login"> <com.google.android.material.checkbox.MaterialCheckBox android:id="@+id/imageCheck" android:background="@drawable/icon_unchecked_reader" android:layout_width="18.0dip" android:layout_height="18.0dip" android:checked="false" android:button="@null" /> <TextView android:textSize="10.0sp" android:textColor="@color/white" android:id="@+id/agreementText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="6.0dip" android:text="我已阅读并同意《用户协议》《隐私协议》" /> </LinearLayout> <LinearLayout android:gravity="center_horizontal" android:orientation="horizontal" android:id="@+id/phone_one_login" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="39.0dip" app:layout_constraintBottom_toTopOf="@+id/other_login_title"> <TextView android:textSize="14.0sp" android:textColor="#ff999999" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10.0dip" android:text="手机号码一键登录" android:drawableRight="@drawable/icon_arrow_right" android:drawablePadding="2.0dip" /> </LinearLayout> <LinearLayout android:gravity="center" android:orientation="horizontal" android:id="@+id/other_login_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="20.0dip" app:layout_constraintBottom_toTopOf="@+id/weixinLogin"> <View android:id="@+id/line1" android:background="@color/a_module_login_white30" android:layout_width="30.0dip" android:layout_height="1.0dip" /> <TextView android:textSize="10.0sp" android:textColor="@color/a_module_login_white30" android:id="@+id/text2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10.0dip" android:layout_marginRight="10.0dip" android:text="其他方式登录" /> <View android:id="@+id/line3" android:background="@color/a_module_login_white30" android:layout_width="30.0dip" android:layout_height="1.0dip" /> </LinearLayout> <FrameLayout android:id="@+id/loading" android:background="@drawable/a_module_login_loading_bg" android:visibility="gone" android:layout_width="80.0dip" android:layout_height="80.0dip" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <ProgressBar android:layout_gravity="center" android:id="@+id/progressBar" android:layout_width="35.0dip" android:layout_height="35.0dip" android:indeterminateDrawable="@drawable/a_module_login_progressbar_style" android:indeterminateBehavior="repeat" app:indicatorColor="@color/white" app:trackColor="#ff999999" /> </FrameLayout> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/weixinLogin" android:layout_width="36.0dip" android:layout_height="36.0dip" android:layout_marginBottom="39.0dip" android:src="@drawable/icon_weixin" app:layout_constraintBottom_toTopOf="@+id/navigationBar" app:layout_constraintEnd_toStartOf="@+id/qqLogin" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" /> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/qqLogin" android:layout_width="36.0dip" android:layout_height="36.0dip" android:layout_marginLeft="29.0dip" android:layout_marginBottom="39.0dip" android:src="@drawable/icon_qq" app:layout_constraintBottom_toTopOf="@+id/navigationBar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/weixinLogin" /> <FrameLayout android:id="@+id/navigationBar" android:layout_width="fill_parent" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
以上是具体实现的一个约束布局文件,主要是对几个属性的掌握,没什么难点之处,主要是多写多使用。