通用布局属性
属性名称 | 功能 |
---|---|
android:id | 设置布局的标识 |
android:layout_width | 设置布局的宽度 |
android:layout_height | 设置布局的高度 |
android:layout_margin | 设置当前布局与屏幕边界或与周围控件的距离 |
android:padding | 设置当前布局与该布局中控件的距离 |
android:background | 设置布局的背景 |
android:visibility | visible:显示;invisible:不显示但是占用空间;gone:不显示也不占用空间; |
android:enabled | 是否启用该控件 |
android: focusable | 是否可以获取焦点 |
其中 padding 和 margin 可以分别控制单独上下左右,比如 paddingTop, paddingLeft, marginRight 等。
LinearLayout(线性布局):
-
用途:
LinearLayout是最简单的布局,它按照水平或垂直方向排列子视图。有点类似 css 中的 flex 布局。可以 LinearLayout 中嵌套 LinearLayout/RelativeLayout 等布局使用。 -
属性:
属性名称 功能 android:orientation 设置布局内控件的排列顺序(horizontal 或 vertical) android:weightSum 定义了weight 总和的最大值(单独设置没效果)。
可以不设置,如果不设置,会根据子组件所设置的 layout_weight 计算出 weightSum 的值。android:layout_weight 在布局内设置子控件权重,属性值可直接写int,控件的大小剩余空间将按比例来分配。
horizontal 配合 android:layout_width="xxxdp" 使用
vertical 配合 android:layout_height="xxxdp" 使用。android:gravity 控制该组件所包含的子元素的对齐方式,可以多个组合,如(left|bottom)。
写在父组件里,控制子组件。android:layout_gravity 控制子组件在父组件中的位置。
写在子组件上,控制自己在父组件中的位置。会覆盖父组件的 android:gravity 设置
剩余空间,他指的是在布局方向上,除去所有组件自身所占空间和 margin 所占空间之后,剩余的值。以水平方向布局为例:水平方向总宽度为 700dp,并且有组件A和B,组件A设置 layout_width=200dp;layout_margin=50dp; 组件B设置 layout_width=100dp;那么水平方向剩余空间为:700-50-200-50-100=300dp。该属性的主要作用可以用来做适配 。
这时,我们将剩余空间(300dp)分为3份(weightSum=3),组件A占2份(layout_weight=2),组件B占1份(layout_weight=1)。
此时,我们不难发现,该水平方向空间占比依次为:marginLeft:50dp ---- 组件A:400dp(200dp+200dp) ---- marginRight:50dp ---- 组件B:200dp(100dp+100dp)。
-
样例:
竖向分布的3个按钮按照 1:1:2 均分父视图高度。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:text="按钮1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<Button
android:text="按钮2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<Button
android:text="按钮3"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
/>
</LinearLayout>
layout_weight 属性
给其中的按钮 1 添加点 margin 和 padding 效果图如下:
<Button
android:text="按钮1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="200dp"
android:paddingTop="50dp"
android:layout_weight="1"
/>
layout_margin 与 padding
这里可以看到 view 的 layout_width、layout_height、layout_left、layout_right、layout_start、layout_end、layout_top、layout_bottom 均包含 padding 但是不包含 layout_margin。
这个和 css 布局中的 IE 盒子模型(box-sizing: border-box)比较像。区别在于,Android中没有 border 这个东西,而且在 Android 中,margin 并不是控件的一部分。
Android 可以通过设置背景图元素达到设置边框的效果,下面两种设置方式都可以:
border_bg_01.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<!-- 边框颜色 -->
<solid android:color="#FF0000" />
</shape>
</item>
<!--给View的上 左 下 右 设置2dp的边框 -->
<item android:top="2dp" android:left="2dp"
android:right="2dp" android:bottom="2dp">
<shape>
<!-- View填充颜色 -->
<solid android:color="#FFFFFF" />
</shape>
</item>
</layer-list>
border_bg_02.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<stroke android:width="2dp" android:color="#f00"></stroke>
<padding android:left="2dp" android:top="2dp" android:right="2dp" android:bottom="2dp" />
</shape>
修改上面的 LinearLayout 容器的 background 属性如下:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/border_bg_01"
tools:context=".LayoutStudyActivity">
background 设置边框样式
另一个展示 layout_gravity、layout_weight 的例子。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="3"
android:gravity="bottom"
tools:context=".LayoutStudyActivity">
<View
android:layout_width="100dp"
android:layout_margin="50dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:layout_weight="2"
android:background="@color/teal_700"
/>
<View
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/sky"
/>
</LinearLayout>
layout_gravity、layout_weight
RelativeLayout(相对布局)、PercentRelativeLayout(百分比相对布局):
-
用途:
RelativeLayout 允许子视图相对于父视图或其他子视图定位。通过指定视图之间的相对关系,可以实现灵活的布局效果。
PercentRelativeLayout 允许子视图设置相对于父视图的百分比。 -
属性:
属性名称 | 功能 |
---|---|
android:layout_centerInParent | 设置当前控件位于父布局的中央位置 |
android:layout_centerVertical | 设置当前控件位于父布局的垂直居中位置 |
android:layout_centerHorizontal | 设置当前控件位于父控件的水平居中位置 |
android:layout_alignParentTop | 设置当前控件是否与父控件顶端对齐 |
android:layout_alignParentBottom | 设置当前控件是否与父控件底端对齐 |
android:layout_alignParentLeft | 设置当前控件是否与父控件左对齐 |
android:layout_alignParentRight | 设置当前控件是否与父控件右对齐 |
android:layout_above | 设置当前控件位于某控件上方 |
android:layout_below | 设置当前控件位于某控件下方 |
android:layout_toLeftOf | 设置当前控件位于某控件左侧 |
android:layout_toRightOf | 设置当前控件位于某控件右侧 |
android:layout_alignTop | 设置当前控件是否与父控件上边界对齐 |
android:layout_alignBottom | 设置当前控件是否与父控件下边界对齐 |
android:layout_alignLeft | 设置当前控件是否与父控件左边界对齐 |
android:layout_alignRight | 设置当前控件是否与父控件右边界对齐 |
注意:
上述居中对齐于父视图相关(当前视图的 border-box 中不含 padding 的 content 区域与父视图中含 padding 的 border-box 盒子居中,非常特殊)。
上述与父视图对齐相关(当前视图的包含 margin 的 content-box 盒子与父视图的 border-box 里面除去 padding 的 content 区域对齐)。
上述与其它子视图的对齐相关(当前视图的包含 margin 的 content-box 盒子区域与其它视图的包含 margin 的 content-box 盒子区域对齐)。
PercentRelativeLayout 另外支持的属性:
属性名称 | 功能 |
---|---|
app:layout_widthPercent | 设置子视图宽度和父布局宽度的百分比(例如:50%) |
app:layout_heightPercent | 设置子视图高度和父布局高度的百分比(例如:50%) |
app:layout_marginPercent | 略 |
app:layout_marginLeftPercent | 略 |
app:layout_marginTopPercent | 略 |
app:layout_marginRightPercent | 略 |
app:layout_marginBottomPercent | 略 |
app:layout_marginStartPercent | 略 |
app:layout_marginEndPercent | 略 |
app:layout_aspectRatio | 确定宽度或者高度的情况下,设置宽高比 |
- 样例:
固定宽高的 6 个按钮分别放在父视图的左上、右上、中间、左下、右下角、中间上面的位置。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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"
android:background="@color/teal_200"
android:layout_marginLeft="20dp"
android:paddingLeft="50dp">
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_marginLeft="50dp"
android:text="左上角"
android:textSize="30dp" />
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:text="左下角"
android:textSize="30dp" />
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:text="右上角"
android:textSize="30dp" />
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="右下角"
android:textSize="30dp" />
<Button
android:id="@+id/center_button"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:layout_marginTop="20dp"
android:text="中间"
android:textSize="30dp" />
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_above="@id/center_button"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dp"
android:text="中间上方"
android:textSize="30dp" />
</RelativeLayout>
RelativeLayout
上述例子中的 layout_width/height、layout_margin相关 改成百分比如下:
<androidx.percentlayout.widget.PercentRelativeLayout
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"
android:background="@color/teal_200"
android:layout_marginLeft="20dp"
android:paddingLeft="50dp"
android:background="@color/cyan">
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_alignParentLeft="true"
app:layout_marginLeftPercent="10%"
android:text="左上角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_alignParentBottom="true"
android:text="左下角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_alignParentRight="true"
android:text="右上角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="右下角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/center_button"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_centerInParent="true"
app:layout_marginTopPercent="5%"
android:text="中间"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_above="@id/center_button"
android:layout_centerHorizontal="true"
app:layout_marginBottomPercent="5%"
android:text="中间上方"
android:textSize="15dp" />
</androidx.percentlayout.widget.PercentRelativeLayout>
PercentRelativeLayout
设置图片的宽高比为 16: 9
<androidx.percentlayout.widget.PercentRelativeLayout
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">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/teal_700"
android:layout_margin="10dp"
app:layout_widthPercent="100%"
app:layout_aspectRatio="178%"/>
</androidx.percentlayout.widget.PercentRelativeLayout>
宽高比设置
FrameLayout(帧布局)、PercentFrameLayout(百分比帧布局):
-
用途:
FrameLayout 将子视图堆叠在一起,每个子视图位于最顶层的位置。常用于覆盖显示或切换视图。
PercentFrameLayout 允许子视图设置相对于父视图的百分比。 -
属性:
属性名称 | 功能 |
---|---|
android:foreground | 设置帧布局容器的前景图像 |
android:foregroundGravity | 设置前景图像显示的位置 |
android:layout_gravity | 用于子视图设置自己在父视图中的对齐方式 |
PercentFrameLayout 另外支持的属性
属性名称 | 功能 |
---|---|
app:layout_widthPercent | 设置当前控件宽度和父布局宽度的百分比(例如:50%) |
app:layout_heightPercent | 设置当前控件高度和父布局高度的百分比(例如:50%) |
app:layout_marginPercent | 略 |
app:layout_marginLeftPercent | 略 |
app:layout_marginTopPercent | 略 |
app:layout_marginRightPercent | 略 |
app:layout_marginBottomPercent | 略 |
app:layout_marginStartPercent | 略 |
app:layout_marginEndPercent | 略 |
app:layout_aspectRatio | 确定宽度或者高度的情况下,设置宽高比 |
- 样例:
图片放在几个重叠视图的最上方
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foreground="@mipmap/ic_launcher"
android:foregroundGravity="center">
<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/teal_200"
tools:ignore="SpeakableTextPresentCheck" />
<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="100dp"
android:background="@color/cyan"
tools:ignore="SpeakableTextPresentCheck" />
<TextView
android:layout_width="200dp"
android:layout_height="100dp"
android:layout_marginTop="100dp"
android:background="@color/teal_700"/>
</FrameLayout>
FrameLayout
PercentRelativeLayout 中的例子去掉中间上方的按钮样式如下:
<androidx.percentlayout.widget.PercentFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/teal_200"
android:layout_marginLeft="20dp"
android:paddingLeft="50dp">
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
app:layout_marginLeftPercent="10%"
android:layout_gravity="left|top"
android:text="左上角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_gravity="left|bottom"
android:text="左下角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_gravity="right|top"
android:text="右上角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_gravity="right|bottom"
android:text="右下角"
android:textSize="15dp" />
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_widthPercent="30%"
app:layout_heightPercent="30%"
android:layout_gravity="center"
app:layout_marginTopPercent="5%"
android:text="中间"
android:textSize="15dp" />
</androidx.percentlayout.widget.PercentFrameLayout>
PercentFrameLayout、layout_gravity
仔细对比会看到中间的按钮位置往右往下移动了。
上述各种 layout_gravity 控制的与父视图对齐(当前视图的包含 margin 的 content-box 盒子与父视图的 border-box 里面除去 padding 的 content 区域对齐)。注意居中与上面的 RelativeLayout 的不同。
ConstraintLayout(约束布局):
- 用途:
ConstraintLayout 是一个灵活强大的布局,可以实现复杂的界面布局。它使用约束将子视图相对于父视图或其他子视图进行定位。
功能强大,ConstraintLayout 几乎能实现其他布局所有的功能。
能减少布局层次的嵌套,有性能的优势。
可视化操作的增强,大部分界面用 ConstraintLayout 都能通过可视化编辑区域完成。
它的灵活性和性能使得它成为 Android 开发中常用的布局方式之一。
- 相对定位:可以通过约束条件将视图相对于父容器或其他视图进行定位,而不需要使用嵌套布局。
- 完全由约束决定宽高:android:layout_width="0dp" 或者 android:layout_height="0dp":代表完全由约束决定宽高,
另外配合 app:layout_constraintWidth_percent="0.3" 和 app:layout_constraintHeight_percent="0.5" 属性可以实现百分比布局。 - 按照比例分配:子视图按比例分配父视图的宽或高的剩余空间。
- 上下左右空余按比例设置间距(bias):默认值为 0.5,表示两边等分剩余空间。可用于设置子视图相对于父视图的上下左右居中等对齐。
- 百分比布局:控件的尺寸按照父布局尺寸的百分比来设置。(只有上面的宽度和高度尺寸为0dp百分比属性才会生效,见上一条描述)
- 宽高比:如果宽高都是 0dp 也可以使用宽高比,这种情况系统会使用满足所有约束条件和宽高比率的最大尺寸。
如果要根据其中一个尺寸来约束另外一个尺寸,可以在比率数值前添加 "W/H" 来分别约束宽度或者高度。
例如:给图片宽度设置match_parent,高度设置成约束尺寸0dp,然后再添加一个layout_constraintDimensionRatio 属性,属性的值可以设置h,16:9 其中h表示宽高比的比值,也可以设置成将值设置w,9:16,w表示高宽比的比值。 - goneMargin:用来控制当约束目标可见性为 gone 的时候,设置不同的间距值。
A 不可见的时候,需要保证 B 的布局位置不变,这个时候设置 B
的 goneMarginStart 的值为 A 的宽度加上 A 的 marginStart 加上 B 的 marginStart ,就可以满足该需求。 - wrap_content 下的强制约束:可用于在使用 wrap_content 但仍然需要强制约束条件来限制它的宽高(不让其过宽或过高)。
- 链式布局(chain):chain 为同一个方向(水平或者垂直)上的多个控件提供一个类似群组的概念。其他的方向则可以单独控制。
多个控件相互在同一个方向上双向引用就可以创建一个 chain。
chain 链也能够支持按照 weight 等分剩余空间。
chain 链对父控件的剩余空间有三种分配方式,即 spread、spread inside、packed,默认值是 spread 即将控件包括第一个控件和最后一个两边均匀分配。其它两种作用参照下图。
- 圆形定位:将一个控件的中心以一定的角度和距离约束到另一个控件的中心上,相当于在一个圆上放置一个控件。
- 辅助线:Guideline 是 ConstraintLayout 布局里面的一个工具类,其作用相当于一条辅助线,默认不可见,可以用于辅助布局。
- 屏障:Barrier 和 Guideline 一样,本身不可见,用户辅助布局,可以选择关联的视图某个方向上的最大值为其位置,方便其它视图和该 Barrier 设置约束关系。
- 可视化分组:Group 可以对一组的控件同时设置其可见性的值Visible、Invisible或者Gone,以及同时设定深度(elevation)属性。
被 Group 引用的控件会导致它们的自身的可见性和深度失效。 - ConstraintSet 可用于在 ConstraintLayout 中动态修改约束的类。可以通过 ConstraintSet 在运行时改变界面布局。 还可以使用 ConstraintSet 实现关键帧动画。
- 属性:
- 给父布局添加约束
属性名称 | 功能 |
---|---|
app:layout_constraintBottom_toBottomOf="parent" | 底部约束 |
app:layout_constraintEnd_toEndOf="parent" | 右边约束 |
app:layout_constraintStart_toStartOf="parent" | 左边约束 |
app:layout_constraintTop_toTopOf="parent" | 顶部约束 |
- 将约束添加到其他控件的属性
属性名称 | 功能 |
---|---|
app:layout_constraintBottom_toTopOf="@+id/button3" | 底部添加约束到其他控件顶部 |
app:layout_constraintEnd_toEndOf="@+id/button3" | 右边添加约束到其他控件右边 |
app:layout_constraintStart_toStartOf="@+id/button3" | 左边添加约束到其他控件左边 |
- 完全由约束决定宽高
属性名称 | 功能 |
---|---|
android:layout_width="0dp" | 完全由约束决定宽度 |
android:layout_height="0dp" | 完全由约束决定高度 |
比如很多app的首页都是由底部 banner 和顶部 fragment 组成,而 fragment 的高度通常不是固定的,如果用其他的布局 fragment 的高度就很难调节,
这时候可以用 0dp(代表由约束决定)动态调节 fragment 的尺寸高度,给顶部的 fragment 设置其高度为 0dp,给其上下各添加一个约束,这时候其高度为父布局的顶部到底部控件的顶部。
<androidx.constraintlayout.widget.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:id="@+id/textView5"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/teal_700"
android:gravity="center"
android:text="顶部fragment"
app:layout_constraintBottom_toTopOf="@+id/textView4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/purple_200"
android:text="底部banner"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
0dp 约束决定
给上面的一些视图添加 padding 和 margin 看一下对布局的计算影响,修改如下:
<androidx.constraintlayout.widget.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:layout_marginBottom="20dp"
android:layout_marginStart="20dp"
android:paddingStart="20dp"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView5"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="20dp"
android:background="@color/teal_700"
android:gravity="center"
android:text="顶部fragment"
app:layout_constraintBottom_toTopOf="@+id/textView4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginStart="20dp"
android:background="@color/purple_200"
android:text="底部banner"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
padding 和 margin 对布局的影响
可以发现:
上述与父视图对齐相关(当前视图的包含 margin 的 content-box 盒子与父视图的 border-box 里面除去 padding 的 content 区域对齐)。
上述与其它子视图的对齐相关(当前视图的包含 margin 的 content-box 盒子区域与其它视图的不包含 margin 的 border-box 盒子区域对齐),这点非常特殊。
- 按比例分配
属性名称 | 功能 |
---|---|
app:layout_constraintHorizontal_weight | 配合 layout_width="0dp" 使用,与 LinearLayout 中作用一样 |
app:layout_constraintVertical_weight | 配合 layout_height="0dp" 使用,与 LinearLayout 中作用一样 |
放置 4 个按钮分别在左上、左下、右上、右下位置,等分宽高。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/teal_200"
android:layout_marginLeft="20dp"
android:paddingLeft="50dp">
<Button
android:id="@+id/button_top_left"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="左上角"
android:textSize="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_top_right"
app:layout_constraintBottom_toTopOf="@id/button_bottom_left"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintVertical_weight="1"
android:layout_marginLeft="50dp"/>
<Button
android:id="@+id/button_bottom_left"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="左下角"
android:textSize="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_top_left"
app:layout_constraintEnd_toStartOf="@id/button_bottom_right"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintVertical_weight="1"/>
<Button
android:id="@+id/button_top_right"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="右上角"
android:textSize="30dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/button_top_left"
app:layout_constraintBottom_toTopOf="@id/button_bottom_right"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintVertical_weight="1"
android:layout_marginLeft="50dp"/>
<Button
android:id="@+id/button_bottom_right"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="右下角"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/button_bottom_left"
app:layout_constraintTop_toBottomOf="@id/button_top_right"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintVertical_weight="1"/>
</androidx.constraintlayout.widget.ConstraintLayout>
layout_constraintHorizontal_weight、layout_width="0dp"
- bias
属性名称 | 功能 |
---|---|
app:layout_constraintHorizontal_bias | 左边占总横向剩余空间的比例(默认为0.5) |
app:layout_constraintVertical_bias | 上边占总竖向剩余空间的比例(默认为0.5) |
下面这个例子实现想实现和上面 RelativeLayout 中样例一样的效果。
固定宽高的 6 个按钮分别放在父视图的左上、右上、中间、左下、右下角、中间上面的位置。
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/teal_200"
android:layout_marginLeft="20dp"
android:paddingLeft="50dp">
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:text="左上角"
android:textSize="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="50dp"/>
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:text="左下角"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1"/>
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:text="右上角"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0"/>
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:text="右下角"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1"/>
<Button
android:id="@+id/center_button"
android:layout_width="150dp"
android:layout_height="150dp"
android:text="中间"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="20dp"/>
<Button
android:layout_width="150dp"
android:layout_height="150dp"
android:text="中间上方"
android:textSize="30dp"
app:layout_constraintBottom_toTopOf="@id/center_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginBottom="20dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
layout_constraintHorizontal/Vertical_bias
上述与父视图对齐相关(当前视图的包含 margin 的 content-box 盒子与父视图的 border-box 里面除去 padding 的 content 区域对齐)。
上述与其它子视图的对齐相关(当前视图的包含 margin 的 content-box 盒子区域与其它视图的不包含 margin 的 border-box 盒子区域对齐),这点非常特殊。
(中间的那个按钮的 marginTop 设置任意值都不会影响到中间上方的按钮位置)。
- 百分比布局
属性名称 | 功能 |
---|---|
app:layout_constraintWidth_percent | 宽度占父布局百分比比例(例如:0.3,代表占30%父视图宽度) |
app:layout_constraintHeight_percent | 高度占父布局百分比比例(例如:0.3,代表占30%父视图宽度) |
修改上面的 bias 中的样例,如下:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/teal_200"
android:layout_marginLeft="20dp"
android:paddingLeft="50dp">
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintHeight_percent="0.3"
android:text="左上角"
android:textSize="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="50dp"/>
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintHeight_percent="0.3"
android:text="左下角"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1"/>
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintHeight_percent="0.3"
android:text="右上角"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0"/>
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintHeight_percent="0.3"
android:text="右下角"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1"/>
<Button
android:id="@+id/center_button"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintHeight_percent="0.3"
android:text="中间"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="20dp"/>
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintHeight_percent="0.3"
android:text="中间上方"
android:textSize="30dp"
app:layout_constraintBottom_toTopOf="@id/center_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginBottom="20dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
layout_constraintWidth/Height_percent
- 宽高比
属性名称 | 功能 |
---|---|
app:layout_constraintDimensionRatio | 宽高都不确定的情况下,可以设置一个float值。 其中宽或高一个确定情况下,另一个为 0dp, 属性的值可以设置h,16:9 其中h表示宽高比的比值,也可以设置成将值设置w,9:16,w表示高宽比的比值 |
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="宽高比为16:9"
android:textSize="20dp"
android:gravity="center"
android:textColor="@color/white"
android:background="@color/teal_700"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintDimensionRatio="h,16:9"/>
</androidx.constraintlayout.widget.ConstraintLayout>
layout_constraintDimensionRatio
- goneMargin
属性名称 | 功能 |
---|---|
app:goneMarginStart | 用来控制当约束目标可见性为 gone 的时候,设置不同的间距值。 |
app:goneMarginEnd | 同app:goneMarginStart。 |
app:goneMarginTop | 同app:goneMarginStart。 |
app:goneMarginBottom | 同app:goneMarginStart。 |
A 不可见的时候,需要保证 B 的布局位置不变,这个时候设置 B
的 goneMarginStart 的值为 A 的宽度加上 A 的 marginStart 加上 B 的 marginStart ,就可以满足该需求。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/left_view"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="20dp"
android:background="@color/teal_200"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="visible"/>
<View
android:id="@+id/right_view"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@color/teal_700"
android:layout_marginStart="20dp"
app:layout_goneMarginStart="90dp"
app:layout_constraintStart_toEndOf="@id/left_view"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
goneMargin
- wrap_content 下的 layout_constrainedWidth/Height 强制约束
属性名称 | 功能 |
---|---|
app:layout_constrainedWidth | true/false,width 为 wrap_content 时仍需满足约束 |
app:layout_constrainedHeight | true/false,height 为 wrap_content 时仍需满足约束 |
某个元素使用 app:layout_constraintHorizontal_chainStyle="packed",将会使相互水平方向上有相互对称依赖约束的子视图表现的像一个整体,同时其内部是一个单行 chain,超长也不会换行,支持 packed、spread、spread_inside 三种模式。
头像位置固定,中间文字长度可变,最右侧按钮跟在文字右侧,但不能超出屏幕(packed)。
头像位置固定,中间文字长度可变,文字过长会显示...,最右侧按钮始终在最右侧(spread_inside)。
上述两种情况去掉 layout_constrainedWidth 属性设置,则过长情况下都会超出显示,不满足约束条件。
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/teal_700">
<ImageView
android:id="@+id/iv_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@mipmap/luffy"/>
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:singleLine="true"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
app:layout_constraintEnd_toStartOf="@id/tv_action"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/iv_avatar"
app:layout_constraintTop_toTopOf="@+id/iv_avatar"
android:text="ConstraintLayout is available as a support library"/>
<TextView
android:id="@+id/tv_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_text"
app:layout_constraintTop_toTopOf="@+id/iv_avatar"
android:text="查看"/>
</androidx.constraintlayout.widget.ConstraintLayout>
wrap_content、layout_constrainedWidth
- 链式布局(chain)
属性名称 | 功能 |
---|---|
app:layout_constraintHorizontal_chainStyle | packed、spread、spread_inside |
app:layout_constraintHorizontal_weight | 需要搭配子视图的 0dp |
app:layout_constraintVertical_chainStyle | 同上 |
app:layout_constraintVertical_weight | 同上 |
app:layout_constraintHorizontal_bias | 需要 style 为 packed 时使用 |
app:layout_constraintVertical_bias | 需要 为 packed 时使用 |
使 chain 中子视图按照 layout_constraintHorizontal_weight 比例均分。
<androidx.constraintlayout.widget.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="60dp"
android:background="@color/teal_700">
<ImageView
android:id="@+id/iv_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@mipmap/luffy"/>
<TextView
android:id="@+id/tv_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:background="@color/gray"
android:textColor="@color/white"
android:singleLine="true"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
app:layout_constraintEnd_toStartOf="@id/tv_action"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/iv_avatar"
app:layout_constraintTop_toTopOf="@+id/iv_avatar"
android:text="ConstraintLayout is available as a support library"/>
<TextView
android:id="@+id/tv_action"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:background="@color/teal_200"
android:textColor="@color/white"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintBottom_toBottomOf="@+id/iv_avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_text"
app:layout_constraintTop_toTopOf="@+id/iv_avatar"
android:text="查看"/>
</androidx.constraintlayout.widget.ConstraintLayout>
chainStyle,layout_constraintHorizontal_weight, layout_width="0dp"
右侧图片和文字,需要整体跟左边头像居中。
某个元素使用 app:layout_constraintHorizontal_chainStyle="packed",将会使可以让某一个方向上相互对称依赖约束的子视图表现的像一个整体,
同时其内部是一个单行 chain,超长也不会换行,支持 packed、spread、spread_inside 三种模式。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="300dp"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/teal_700">
<ImageView
android:id="@+id/iv_avatar"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginStart="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3"
android:src="@mipmap/luffy" />
<ImageView
android:id="@+id/iv_pic"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="50dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="@+id/tv_content"
app:layout_constraintStart_toEndOf="@id/iv_avatar"
app:layout_constraintTop_toTopOf="@id/iv_avatar"
app:layout_constraintVertical_chainStyle="packed"
android:src="@mipmap/luffy2" />
<TextView
android:id="@+id/tv_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="@id/iv_avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/iv_pic"
app:layout_constraintTop_toBottomOf="@+id/iv_pic"
android:text="chain 可以让某一个方向上相互对称依赖约束的子视图表现的像一个整体。 " />
</androidx.constraintlayout.widget.ConstraintLayout>
layout_constraintVertical_chainStyle
- 圆形定位
属性名称 | 功能 |
---|---|
app:layout_constraintCircle | 引用另一个控件的 id。 |
app:layout_constraintCircleRadius | 到另一个控件中心的距离。 |
app:layout_constraintCircleAngle | 控件的角度(顺时针,0 - 360 度)。 |
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/view_one"
android:background="@color/teal_700"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<View
android:id="@+id/view_two"
android:background="@color/teal_200"
android:layout_width="20dp"
android:layout_height="20dp"
app:layout_constraintCircle="@id/view_one"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="60" />
</androidx.constraintlayout.widget.ConstraintLayout>
圆形定位
- 辅助线(Guideline)
属性名称 | 功能 |
---|---|
android:orientation | vertical/horizontal |
app:layout_constraintGuide_begin | 指定距离左/上边开始的固定位置 |
app:layout_constraintGuide_end | 指定距离右/下边开始的固定位置 |
app:layout_constraintGuide_percent | 指定位于布局中所在的百分比 |
<androidx.constraintlayout.widget.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">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<View
android:id="@+id/view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/cyan"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/teal_700"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
Guideline
- 屏障(Barrier)
属性名称 | 功能 |
---|---|
app:barrierDirection | 选择关联的视图中哪个方向的最大值 |
app:contraint_referenced_ids | 取那些视图的 direction 最大值为自己的位置 |
<androidx.constraintlayout.widget.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/textView"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="short text info"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="long text info to show"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="textView,textView2" />
<View
android:id="@+id/view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/teal_700"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/barrier2"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Barrier
- 可视化分组(Group)
属性名称 | 功能 |
---|---|
android:visibility | 可见性 |
android:elevation | 深度 |
app:constraint_referenced_ids | 选入分组的视图 id 列表 |
<androidx.constraintlayout.widget.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">
<View
android:id="@+id/view1"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/cyan"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/view2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/teal_700"
app:layout_constraintBottom_toTopOf="@+id/view3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@+id/view1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="1" />
<View
android:id="@+id/view3"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/teal_200"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/view2"
app:layout_constraintTop_toBottomOf="@+id/view2"
app:layout_constraintVertical_weight="1" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="visible"
app:constraint_referenced_ids="view2,view3" />
</androidx.constraintlayout.widget.ConstraintLayout>
Group
FlowLayout(流式布局):
-
用途:
根据 ViewGroup 的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行(也可以是按垂直方向流式布局)。和 css 中的 float 布局非常相似。注意:Flow 管理的 view 自身可以不设置 layout_constraintStart_toEndOf 等位置。自身需要 width="0dp"。关联的视图 app:layout_constraintWidth_default="wrap",如果是 TextView 最好设置 android:singleLine="true"。
-
属性:
属性名称 | 功能 |
---|---|
android:orientation | horizontal、vertical,水平方向的流式,还是垂直方向。 |
app:flow_wrapMode | none(默认值):不换行 chain:换行 aligned:换行并且上下对齐 |
app:flow_horizontalGap | 横向行间距 |
app:flow_verticalGap | 纵向行间距 |
app:flow_verticalAlign | top、bottom、center、baseline,在 android:orientation 为 horizontal 时有效 |
app:flow_horizontalAlign | start、end、center,在 android:orientation 为 vertical 时有效 |
app:flow_horizontalStyle | 约束所有水平链样式(packed,spread,spread_inside) |
app:flow_verticalStyle | 约束所有垂直链样式(packed,spread,spread_inside) |
app:flow_firstHorizontalStyle | 约束水平样式首行链样式(packed,spread,spread_inside) |
app:flow_firstVerticalStyle | 约束垂直样式首行链样式(packed,spread,spread_inside) |
app:flow_lastHorizontalStyle | 约束水平样式尾行链样式(packed,spread,spread_inside) |
app:flow_lastVerticalStyle | 约束垂直样式尾行链样式(packed,spread,spread_inside) |
app:flow_horizontalBias | wrapMode 为 chian 时并且 style 为 packed 时有效 |
app:flow_verticalBias | wrapMode 为 chian 时并且 style 为 packed 时有效 |
app:flow_firstHorizontalBias | wrapMode 为 chian 时并且 style 为 packed 时有效 |
app:flow_firstVerticalBias | wrapMode 为 chian 时并且 style 为 packed 时有效 |
app:flow_lastHorizontalBias | wrapMode 为 chian 时并且 style 为 packed 时有效 |
app:flow_lastVerticalBias | wrapMode 为 chian 时并且 style 为 packed 时有效 |
app:flow_maxElementsWrap | 配置每行(horizontal)或者每列(vertical)最多的视图数量 |
flow_wrap 为 none 一行放不下也不换行。
flow_wrap 为 noneflow_wrap 为 chain 一行放不下则换行,最后一行放不下,默认 style 为 spread。
flow_wrap 为 chainflow_wrap 为 aligned 则所有列按照最大的宽度决定,并且对齐。
flow_wrap 为 aligned可以使用下面的样式测试各种属性效果:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:flow_horizontalGap="10dp"
app:flow_verticalGap="10dp"
app:flow_wrapMode="chain"
app:flow_horizontalStyle="packed"
app:flow_verticalAlign="top"
app:flow_horizontalBias="0"
app:flow_maxElementsWrap="5"
app:constraint_referenced_ids="image1,image2,image3,image4,image5,image6,image7,image8,image9" />
<ImageView
android:id="@+id/image1"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image2"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image3"
android:layout_width="130dp"
android:layout_height="130dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image4"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image5"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image6"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image7"
android:layout_width="130dp"
android:layout_height="130dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image8"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/image9"
android:layout_width="110dp"
android:layout_height="110dp"
android:src="@mipmap/ic_launcher" />
</androidx.constraintlayout.widget.ConstraintLayout>
下面的样例:
第一行显示一个从左到右拉通的文本,右边设置间距避免和右边的按钮重叠,过长则尾部显示...
第二行设置 0.3 水平辅助线,左边为需要换行文本,中间为 chain,右边一个固定按钮。
<androidx.constraintlayout.widget.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="wrap_content">
<View
android:id="@+id/top_margin"
android:layout_width="match_parent"
android:layout_height="8dp"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/teal_700"
android:paddingHorizontal="12dp"
android:paddingVertical="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_margin">
<TextView
android:id="@+id/tv_device_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="70dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="这是一段很长很长很长很长很长很长很长很长的文字" />
<TextView
android:id="@+id/tv_org_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/black"
android:textSize="14sp"
app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_device_name"
app:layout_constraintWidth_default="wrap"
android:text="这也是一段很长很长很长很长很长很长很长很长的文字" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
app:constraint_referenced_ids="tv_type,tv_online_state,tv_switch_state"
app:flow_firstHorizontalBias="0"
app:flow_firstHorizontalStyle="packed"
app:flow_horizontalBias="0"
app:flow_horizontalGap="10dp"
app:flow_lastHorizontalBias="0"
app:flow_lastHorizontalStyle="packed"
app:flow_verticalGap="8dp"
app:flow_wrapMode="chain"
app:layout_constraintEnd_toStartOf="@id/ibt_go"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toTopOf="@id/tv_org_name" />
<TextView
android:id="@+id/tv_type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/blue"
android:paddingHorizontal="8.5dp"
android:paddingVertical="3dp"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintWidth_default="wrap"
android:text="标签1的文案" />
<TextView
android:id="@+id/tv_online_state"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/yellow"
android:paddingHorizontal="8.5dp"
android:paddingVertical="3dp"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintWidth_default="wrap"
android:text="标签2" />
<TextView
android:id="@+id/tv_switch_state"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/sky"
android:paddingHorizontal="8.5dp"
android:paddingVertical="3dp"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintWidth_default="wrap"
android:text="标签3的文案也很长怎么办" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/ibt_go"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:src="@mipmap/icon_arrow_2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Flow
AbsoluteLayout(绝对布局):
注:已被弃用,且不建议使用。
它以绝对坐标的方式精确定位视图,即可以通过指定相对于父容器左上角的精确坐标来确定视图的位置。
在 AbsoluteLayout 中,每个子视图的位置和大小都是通过设置其 android:layout_x 和 android:layout_y 属性来确定的。
使用 AbsoluteLayout 的优点是可以精确地控制视图的位置和布局,适用于一些特定场景,比如创建自定义的视图布局或实现某些特殊效果。然而,由于Android设备的多样性和不同屏幕尺寸的存在,使用绝对坐标来布局视图可能会导致在不同设备上显示效果的不一致,可能会出现重叠、截断或遮挡的情况。
属性名称 | 功能 |
---|---|
android:layout_x | 指定控件的左上角的x坐标 |
android:layout_y | 指定控件的左上角的y坐标 |
android:layout_width | 具体值(如100dp)、match_parent、wrap_content |
android:layout_height | 具体值(如100dp)、match_parent、wrap_content |
可以与下述的对齐属性一起使用:
属性名称 | 功能 |
---|---|
android:layout_alignParentTop | true/false,将控件相对于父布局的顶部对齐 |
android:layout_alignParentBottom | true/false,将控件相对于父布局的底部对齐 |
android:layout_alignParentLeft | true/false,将控件相对于父布局的左边对齐 |
android:layout_alignParentRight | true/false,将控件相对于父布局的右边进行对齐 |
android:layout_alignTop | 将控件相对于其他控件的顶部对齐 |
android:layout_alignBottom | 将控件相对于其他控件的底部对齐 |
android:layout_alignLeft | 将控件相对于其他控件的左边对齐 |
android:layout_alignRight | 将控件相对于其他控件的右边对齐 |
android:layout_centerHorizontal | 将控件在水平方向上居中对齐 |
android:layout_centerVertical | 将控件在垂直方向上居中对齐 |
AbsoluteLayout已被弃用,并不推荐在Android应用程序开发中使用。
TableLayout(表格布局):
-
用途:
TableLayout 可以将子视图组织成表格形式,类似于 HTML 的表格布局。它包含多个 TableRow,每个 TableRow 包含多个子视图。 -
属性:
属性名称 | 功能 |
---|---|
android:layout_column | 设置该控件显示位置,如android:Layout_column="1"表示第2个位置显示。 |
android:layout_span | 设置该控件占几行,默认是1行。 |
android:stretchColumns | 设置可被拉伸的列。 |
android:shrinkColumns | 设置可被收缩的列。 |
android:collapseColumns | 设置可被隐藏的列。 |
注意:列的宽度是由该列中最宽那个决定
- 样例:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="2"
android:shrinkColumns="1"
android:collapseColumns="0">
<TableRow>
<Button
android:layout_column="0"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="按钮1"
android:textSize="30sp"
/>
<Button
android:layout_column="1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="按钮2"
android:textSize="30sp"
/>
</TableRow>
<TableRow>
<Button
android:layout_column="1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="按钮3"
android:textSize="30sp"
/>
<Button
android:layout_column="2"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="按钮4"
android:textSize="30sp"
/>
</TableRow>
<TableRow>
<Button
android:layout_column="2"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="按钮5"
android:textSize="30sp"
/>
</TableRow>
<TableRow>
<Button
android:layout_column="0"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="按钮6"
android:textSize="30sp"
/>
<Button
android:layout_column="2"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="按钮7"
android:textSize="30sp"
/>
</TableRow>
</TableLayout>
TableLayout
GridLayout(网格布局):
-
用途:
GridLayout 将子视图组织成网格形式,类似于表格布局。
可以通过 android:layout_row 和android:layout_column 属性指定子视图的行和列位置。 -
属性:
属性名称 | 功能 |
---|---|
android:alignmentMode | 使视图的外边界之间进行校准。可以取以下值: alignBounds -- 对齐子视图边界。 alignMargins -- 对齐子视图边距。 |
android:layout_gravity | 用来设置该 View 相对与父 View 的位置 |
android:orientation | 设置布局内控件的排列顺序 |
android:columnCount | 设置列数 |
android:rowCount | 设置行数 |
android:layout_columnSpan | 横跨几列 |
android:layout_rowSpan | 横跨几行 |
android:layout_column | 第几列 |
android:layout_row | 第几行 |
- 样例:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="4"
android:orientation="horizontal"
android:layout_marginHorizontal="0dp"
android:rowCount="6" >
<TextView
android:layout_columnSpan="4"
android:layout_gravity="fill"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="#D5DEDF"
android:text="0"
android:textSize="50sp" />
<Button
android:layout_columnSpan="2"
android:layout_gravity="fill"
android:text="回退" />
<Button
android:layout_columnSpan="2"
android:layout_gravity="fill"
android:text="清空" />
<Button android:text="+" />
<Button android:text="1" />
<Button android:text="2" />
<Button android:text="3" />
<Button android:text="-" />
<Button android:text="4" />
<Button android:text="5" />
<Button android:text="6" />
<Button android:text="*" />
<Button android:text="7" />
<Button android:text="8" />
<Button android:text="9" />
<Button android:text="/" />
<Button android:layout_width="wrap_content" android:text="." />
<Button android:text="0" />
<Button android:text="=" />
</GridLayout>
GridLayout
ScrollView(滚动布局):
-
用途:
ScrollView 是 Android 平台上的一个可滚动视图容器,它用于在一个可滚动区域内显示大量内容。当布局超过屏幕大小时,ScrollView 会自动启用滚动功能,用户可以通过滑动屏幕来查看隐藏部分的内容。在 ScrollView 中,只能包含一个直接子视图(ViewGroup),通常是一个垂直方向的线性布局或相对布局。如果需要水平滚动效果,可以使用 HorizontalScrollView 作为替代。
-
属性:
属性名称 | 功能 |
---|---|
android:fillViewport | 用于指定内容是否填充 ScrollView 的视口。设置为 true 表示内容将充满整个ScrollView,默认为 false。 |
android:scrollbars | 定义滚动条的显示方式。可选值有"none"(不显示)、"vertical"(只显示垂直滚动条)和"horizontal"(只显示水平滚动条)。 |
android:scrollbarStyle | 指定滚动条的样式。可选值有"default"(默认样式)、"insideOverlay"(覆盖在内容上方)和"outsideOverlay"(位于内容旁边)。 |
android:fadeScrollbars | 控制滚动条是否在不活动状态时渐隐。设置为true表示滚动条会渐隐,默认为false。 |
- 方法:
方法名称 | 功能 |
---|---|
scrollTo(int x, int y) | 将 ScrollView 滚动到指定的位置,参数x和y分别代表目标位置的水平和垂直偏移量。 |
fullScroll(int direction) | 使 ScrollView 滚动到指定的边界,参数 direction 可以是 View.FOCUS_UP(滚动到顶部)或 View.FOCUS_DOWN(滚动到底部)。 |
smoothScrollTo(int x, int y) | 平滑地将ScrollView滚动到指定的位置,会有滚动动画效果。 |
smoothScrollBy(int dx, int dy) | 平滑地将ScrollView滚动指定的偏移量,会有滚动动画效果。 |
isSmoothScrollingEnabled() | 判断平滑滚动是否启用。 |
- 样例:
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadeScrollbars="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:background="@color/sky"
android:text="这是 ScrollView 中第一行文本。" />
<TextView
android:layout_width="match_parent"
android:layout_height="1000dp"
android:gravity="center"
android:background="@color/teal_700"
android:text="这是 ScrollView 中间一个占用空间很大的视图。" />
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:background="@color/cyan"
android:text="这是 ScrollView 中最后一行文本。" />
</LinearLayout>
</ScrollView>
ScrollView
CoordinatorLayout(协调布局):
- 用途:
CoordinatorLayout 是用于处理子视图之间的协调动作的特殊布局。它常用于实现一些复杂的交互效果,如滚动时的视图协调。通过为 CoordinatorLayout 的子 View 设置 Behavior,可以实现不同的交互效果。通常会与 AppbarLayout、CollapsingToolbarLayout 结合使用。
通常如果界面需要实现一个顶部优先折叠/出现的视图,可以使用 CoordinatorLayout 结合 Behavior 或者 AppbarLayout、CollapsingToolbarLayout 使用。
此处不展开讨论具体使用方法。
自定义布局:
-
用途:
上面介绍了很多 Android 系统提供的布局方式,其实我们也可以自己动手实现一个自定义布局。
可以根据一些自己项目的需要,定制一些特殊的布局方式,方便日常开发中复用。 -
样例:
下面实现一个类似上面 ConstraintLayout 中 Flow 的流式布局的自定义布局,暂时命名为 FlowLayoutM,暂时只支持子视图设置 Margin,直接使用系统的 MarginLayoutParams(需要自己添加支持的布局属性,以及子视图添加支持的布局属性,可以参照 FrameLayout 的实现)。
新建一个类 FlowLayoutM 继承自 ViewGroup。重点重写其中的 onMeasure() 和 onLayout() 方法。
onMeasure() 中计算所有 childView 的宽和高,然后根据childView的宽和高,计算自己的宽和高。(当然,如果不是 wrap_content,直接使用父 ViewGroup 传入的计算值即可。)
onLayout() 中对所有的 childView 进行布局。
FlowLayoutM 代码:
package com.example.layoutstudy;
import android.view.ViewGroup;
import java.util.ArrayList;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.graphics.Canvas;
public class FlowLayoutM extends ViewGroup {
public int horizontalSpacing; // 水平间距
public int verticalSpacing; // 竖直间距
public ArrayList<Line> lineList;
public FlowLayoutM(Context context) {
super(context,null);
}
public FlowLayoutM(Context context, AttributeSet attrs) {
super(context, attrs,0);
}
public FlowLayoutM(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setHorizontalSpacing(int horizontalSpacing) {
this.horizontalSpacing = horizontalSpacing;
this.requestLayout();
}
public void setVerticalSpacing(int verticalSpacing) {
this.verticalSpacing = verticalSpacing;
this.requestLayout();
}
/**
* 分行:遍历所有的子View,判断哪几个子View在同一行(排座位表)
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (lineList != null) {
lineList.clear();
}
lineList = new ArrayList<>();
System.out.println("================onMeasure()方法");
// 1.FlowLayout 的宽度,也就是申请宽度
int width = MeasureSpec.getSize(widthMeasureSpec);
// 2.获取用于实际比较的宽度,就是除去2边的padding的宽度
int noPaddingWidth = width - getPaddingLeft() - getPaddingRight();
// 3.遍历所有的子View,拿子View的宽和noPaddingWidth进行比较
Line line = new Line();
for (int i = 0; i < getChildCount();i++) {
View subView = getChildAt(i);
// 保证能够获取到宽高
subView.measure(0,0);
if (line.getViewList().size() == 0) {
// 4.如果当前line中没有子View,则不用比较直接放入line中,因为要保证每行至少有一个子View;
line.addLineView(subView);
} else if (line.getLineWidth() + horizontalSpacing + subView.getMeasuredWidth() > noPaddingWidth) {
// 5.如果当前line的宽+水平间距+子View的宽大于noPaddingWidth,则child需要换行
lineList.add(line);
line = new Line();
line.addLineView(subView);
} else {
line.addLineView(subView);
}
if (i == getChildCount() - 1) {
// 6.如果当前child是最后的子View,那么需要保存最后的line对象
lineList.add(line);
}
}
int height = getPaddingTop() + getPaddingBottom(); // 申请高度
for (int i = 0; i < lineList.size(); i++) {
height += lineList.get(i).getLineHeight();
}
height += (lineList.size() - 1) * verticalSpacing;
// 7.设置当前控件的宽高,或者向父 view 申请宽高
setMeasuredDimension(width, height);
System.out.println("======width=" + width + ",height=" + height);
}
/**
* 让每个子视图递归 layout
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
System.out.println("================onLayout()方法");
int paddingTop = getPaddingTop();
int paddingLeft = getPaddingLeft();
for (int i = 0; i < lineList.size(); i++) {
// 1.获取line的view的集合
Line line = lineList.get(i);
if (i > 0) {
// 从第二行要加行距
paddingTop += verticalSpacing + lineList.get(i - 1).getLineHeight();
}
ArrayList<View> viewList = line.getViewList();
int reMainSpacing = getReMainSpacing(line); // 计算空白空间,等分给各个子视图
int spacing = reMainSpacing / viewList.size();
for (int j = 0; j < viewList.size(); j++) { // 2.获取每一行的子view的集合
View view = viewList.get(j);
int specWidth = MeasureSpec.makeMeasureSpec(view.getMeasuredWidth() + spacing, MeasureSpec.EXACTLY);
view.measure(specWidth, MeasureSpec.UNSPECIFIED); // 保证能够获取到宽高
if (j == 0) {
// 如果是第一个子view就放在左边就可以了
view.layout(paddingLeft, paddingTop,paddingLeft + view.getMeasuredWidth(),paddingTop + view.getMeasuredHeight());
} else {
View viewLast = viewList.get(j - 1);
int left = viewLast.getRight() + horizontalSpacing;
view.layout(left, viewLast.getTop(),left + view.getMeasuredWidth(), viewLast.getBottom());
}
}
}
}
/**
* 获取每行空白的宽度
*/
public int getReMainSpacing(Line line) {
return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - line.getLineWidth(); // 测量的宽分别减去...
}
/**
* 封装每行的数据
*/
class Line {
int width;
int height;
ArrayList<View> viewList;
public Line() {
viewList = new ArrayList<>();
}
/**
* 换行的方法
*/
public void addLineView(View child) {
if (!viewList.contains(child)) {
viewList.add(child);
if (viewList.size() == 1) {
width = child.getMeasuredWidth();
} else {
width += child.getMeasuredWidth() + horizontalSpacing;
}
}
height = Math.max(height, child.getMeasuredHeight());
}
/**
* 获取当前行的宽度
* @return
*/
public int getLineWidth() {
return width;
}
/**
* 获取当前行的高度
* @return
*/
public int getLineHeight() {
return height;
}
/**
* 获取当前行的所有子view
* @return
*/
public ArrayList<View> getViewList() {
return viewList;
}
}
}
Activity 中代码:
public class LayoutStudyActivity extends AppCompatActivity {
private Context context;
private int dimens;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_layout_study);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
context = this;
FlowLayoutM flowLayout = (FlowLayoutM)findViewById(R.id.flow_layout_01);
FlowLayoutM flowLayout2 = (FlowLayoutM)findViewById(R.id.flow_layout_02);
FlowLayoutM flowLayout3 = (FlowLayoutM)findViewById(R.id.flow_layout_03);
dimens = getDimens(R.dimen.common10dp);
flowLayout.setVerticalSpacing(dimens);
flowLayout.setHorizontalSpacing(dimens);
flowLayout.setPadding(dimens, dimens, dimens, dimens);
flowLayout2.setVerticalSpacing(dimens);
flowLayout2.setHorizontalSpacing(dimens);
flowLayout2.setPadding(dimens, dimens, dimens, dimens);
flowLayout3.setVerticalSpacing(dimens);
flowLayout3.setHorizontalSpacing(dimens);
flowLayout3.setPadding(dimens, dimens, dimens, dimens);
final ArrayList<String> list = getToAddStringList();
if (list != null) {
for (int g = 0; g < list.size(); g++) {
Button tv = new Button(LayoutStudyActivity.this);
tv.setText(list.get(g));
tv.setTextColor(Color.CYAN);
tv.setTextSize(16);
tv.setGravity(Gravity.CENTER);
tv.setPadding(dimens, dimens, dimens, dimens);
Drawable pressed = createDrawable(getRadmoColor(), dimens);
Drawable normal = createDrawable(getRadmoColor(), dimens);
tv.setBackgroundDrawable(createSelector(pressed, normal)); //设置按下的颜色
flowLayout.addView(tv);
final int finalG = g;
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Toast.makeText(LayoutStudyActivity.this, list.get(finalG).toString(), Toast.LENGTH_SHORT).show();
System.out.println("您点击了标签:" + list.get(finalG).toString());
}
});
}
flowLayout.requestLayout();
}
}
public static int getRadmoColor() {
Random random = new Random();
return Color.rgb(random.nextInt(150), random.nextInt(150), random.nextInt(150));
}
public static Drawable createSelector(Drawable pressed, Drawable normal) {
StateListDrawable drawable = new StateListDrawable();
// 添加按压状态以及图图片
drawable.addState(new int[]{android.R.attr.state_pressed}, pressed);
// 添加正常状态以及图片
drawable.addState(new int[]{}, normal);
// 设置状态选择器的过渡动画,让其平滑的执行
drawable.setEnterFadeDuration(500);
drawable.setExitFadeDuration(500);
return drawable;
}
public static Drawable createDrawable(int color, float radius) {
GradientDrawable drawable = new GradientDrawable();
drawable.setShape(GradientDrawable.RECTANGLE); // 设置形状为圆角矩形
drawable.setColor(color); // 设置颜色
drawable.setCornerRadius(radius); // 设置角度
return drawable;
}
public static ArrayList<String> getToAddStringList() {
ArrayList<String> list = new ArrayList<>();
String[] labelTexts = new String[]{ " 代码添加标签1 ", "代码添加标签2", "代码添加标签3" }; // 设置数据
for (int i = 0; i < labelTexts.length; i++) {
list.add(labelTexts[i]);
}
return list;
}
public int getDimens(int id) {
return context.getResources().getDimensionPixelSize(id);
}
}
布局文件中代码:
<LinearLayout
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"
android:layout_marginTop="50dp"
android:background="@color/teal_700"
android:orientation="vertical"
>
<com.example.layoutstudy.FlowLayoutM
android:id="@+id/flow_layout_01"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
style="@style/text_flag_01"
android:text="线性布局" />
<TextView
style="@style/text_flag_01"
android:text="相对布局" />
<TextView
style="@style/text_flag_01"
android:text="帧布局" />
<TextView
style="@style/text_flag_01"
android:text="约束布局" />
<TextView
style="@style/text_flag_01"
android:text="表格布局" />
<TextView
style="@style/text_flag_01"
android:text="网格布局" />
<TextView
style="@style/text_flag_01"
android:text="自定义布局" />
</com.example.layoutstudy.FlowLayoutM>
<com.example.layoutstudy.FlowLayoutM
android:id="@+id/flow_layout_02"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="线性布局"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="相对布局"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="帧布局"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="约束布局"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="表格布局"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="网格布局"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="自定义布局"
android:textColor="#888888" />
</com.example.layoutstudy.FlowLayoutM>
<com.example.layoutstudy.FlowLayoutM
android:id="@+id/flow_layout_03"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="线性布局"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="相对布局"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="帧布局"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="约束布局"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="表格布局"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="网格布局"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="自定义布局"
android:textColor="#43BBE7" />
</com.example.layoutstudy.FlowLayoutM>
</LinearLayout>
相关边框及样式:
flag_01.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#7690A5"></solid>
<corners android:radius="5dp"/>
<padding
android:bottom="2dp"
android:left="10dp"
android:right="10dp"
android:top="2dp" />
</shape>
flag_02.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"></solid>
<corners android:radius="40dp"/>
<stroke android:color="#C9C9C9" android:width="2dp"/>
<padding
android:bottom="2dp"
android:left="10dp"
android:right="10dp"
android:top="2dp" />
</shape>
flag_03.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" ></solid>
<corners android:radius="40dp"/>
<padding
android:bottom="2dp"
android:left="10dp"
android:right="10dp"
android:top="2dp" />
</shape>
styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="text_flag_01">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_margin">4dp</item>
<item name="android:background">@drawable/flag_01</item>
<item name="android:textColor">#ffffff</item>
</style>
</resources>
自定义流式布局
网友评论