一、传统界面布局
在安卓开发中,界面编程是一个很重要的部分,而很多时候为了实现好看的 ui 界面会用到很多层的布局来实现,当一个应用的界面含有多个控件并且布局嵌套的层级大于三层时可能界面的渲染速度就会变慢,导致界面丢帧,这时界面加载起来就会很不流畅。极端情况下也会发生 ANR (Application Not Responding) 。因此一个优秀的界面应该尽量的去满足以下两点。
- 布局嵌套的层级经可能的少
- 布局中的控件应该尽可能的使用相对位置。
使用相对位置可以在不用分辨率的设备中保持布局效果的一致性。
但是以上两点往往是冲突的,降低嵌套层级的方式是使用 RelativeLayout
, 而基于相对位置使用的是 LinearLayout
。当界面过于复杂时需要大量的 LinearLayout
保证控件的相对摆放,这样会导致嵌套层级过多,如果使用 RelativeLayout
控件之间的关系及其冗余,这时的界面可读性差也难与修改。
二、ConstraintLayout布局
ConstraintLayout
即约束布局,在2016年的 Google I/O 大会上推出,可一定程度的替代掉 RelativeLayout
和 LinearLayout
接下来就开始了解一些简单的用法。
- Hello World 的布局
<?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"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
这个熟悉的代码正是新版本的 Android Studio 新建项目时自动生成的布局文件,可以看到这个界面的根布局正是 ConstraintLayout
。这个效果是让 Hello World! 居中,其标准性模板如下
app:layout_constraint[ 本源位置 ]_to[ 目标位置 ]="[ 目标id ]"
- 指定约束
<?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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:text="取消"
app:layout_constraintBottom_toBottomOf="@id/constraintLayout"
app:layout_constraintStart_toStartOf="@id/constraintLayout" />
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:text="下一步"
app:layout_constraintBottom_toBottomOf="@id/constraintLayout"
app:layout_constraintStart_toEndOf="@id/cancel_button" />
</android.support.constraint.ConstraintLayout>
- 设置比例
<?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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="居中"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintEnd_toEndOf="@id/constraintLayout"
app:layout_constraintStart_toStartOf="@id/constraintLayout"
app:layout_constraintTop_toTopOf="@+id/constraintLayout" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按比例偏移"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintEnd_toEndOf="@id/constraintLayout"
app:layout_constraintHorizontal_bias="0.25"
app:layout_constraintStart_toStartOf="@id/constraintLayout"
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
app:layout_constraintVertical_bias="0.25" />
</android.support.constraint.ConstraintLayout>
- 引导线布局
<?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/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Guideline
android:id="@+id/guideLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="72dp"
tools:layout_editor_absoluteX="72dp"
tools:layout_editor_absoluteY="0dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="向上"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintStart_toStartOf="@id/guideLine"
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
app:layout_constraintVertical_bias="0.25"
tools:layout_editor_absoluteX="72dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="向下"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintStart_toStartOf="@id/guideLine"
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
app:layout_constraintVertical_bias="0.75"
tools:layout_editor_absoluteX="72dp" />
</android.support.constraint.ConstraintLayout>
- 布局填充
<?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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/small"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="自适应大小"
app:layout_constraintStart_toStartOf="@id/constraintLayout"
app:layout_constraintTop_toTopOf="@id/constraintLayout" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="取最宽的宽度"
app:layout_constraintBottom_toBottomOf="@id/constraintLayout"
app:layout_constraintEnd_toEndOf="@id/constraintLayout"
app:layout_constraintStart_toEndOf="@id/small"
app:layout_constraintTop_toTopOf="@id/constraintLayout" />
</android.support.constraint.ConstraintLayout>
- 控件宽高比
<?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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="0dp"
android:layout_height="200dp"
android:background="@color/colorAccent"
android:src="@mipmap/home_ban"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintLeft_toLeftOf="@+id/constraintLayout"
app:layout_constraintRight_toRightOf="@+id/constraintLayout"
app:layout_constraintTop_toTopOf="@+id/constraintLayout" />
<ImageView
android:layout_width="200dp"
android:layout_height="0dp"
android:background="@color/colorAccent"
android:contentDescription="@null"
android:src="@mipmap/home_ban"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintDimensionRatio="4:3"
app:layout_constraintLeft_toLeftOf="@+id/constraintLayout"
app:layout_constraintRight_toRightOf="@+id/constraintLayout" />
</android.support.constraint.ConstraintLayout>
- 圆形定位
这个用法简直是完美的解决了消息列表里使用圆形头像时右上角的未读消息的点。
<?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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/pic"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/login_sel_rel"
app:layout_constraintCircle="@+id/image"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="70dp" />
</android.support.constraint.ConstraintLayout>