Flexbox 布局的基本概念
通过设置元素的 display
属性值为 flex
即 display:flex
可以将一个元素变成一个 Flexbox 布局容器。而其中的子元素则为叫做 Flex Item
。
Flexbox 非常灵活和强大,一时半会是难以掌握的。下面我将结合我所了解到的其他布局容器的知识来进行对比以加深对布局容器的理解。
模拟 Android LinearLayout
布局的功能。
如果你知道 Android 中的 LinearLayout
的话,我可以告诉你,Flexbox 可以轻松的模拟 LinearLayout
布局的功能。
-
Flexbox 中有一个
flex-direction
对应android:orientation
属性。具体来说
flex-direction:row
对应android:orientation="horizontal"
这也是两者的默认值, 而flex-direction: column
对应android:orientation="vertical"
. 另外,Flexbox 还支持另外两个属性:row-reverse
和column-reverse
了解一下。 -
对于布局容器的子元素,Flexbox 有一个
flex-grow
属性对应android:weight
属性。 -
对于布局容器的子元素的
android:layout_gravity
, Flexbox 有一个align-self
属性来对应。 但是对于LinearLayout
horizontal
的容器,android:layout_gravity
有center_vertical|top|bottom
是可用的。
而对于vertical
的容器则有center_horizontal|left|right|start|end
可用。
由于 Flexbox 为了兼容其他的 RTL 等语言的布局,是没有 left,right,bottom,top 等值的。 在align-self
中有很多对齐方式供我们使用,例如可以用flex-start
对应left,top
,用flex-end
对应right,bottom
,用center
对应center_vertical, center_horizontal
.
值得注意的是align-self
还有一个stretch
值相当于是对应android:width
或android:height
的match_parent
属性值。
另外,在默认值上,Flexbox 子元素的表现是stretch
而LinearLayout
则表现是相当于 Flexbox 中的flex-start
。
Flexbox 除了能够模拟 LinearLayout
中的所有功能之外。 类似还有如下功能。
1)可以在布局容器上指定 align-items
属性。 align-self
是针对单个 Flex item 的。而 align-items
由于是在布局容器上声明,显示容易知道它是针对此 Flexbox 下的所有 Flex item 的。和 align-self
一样 align-items
敢有 stretch,flex-start, flex-end,center
等值。 但是值得说明的是 align-self
和 align-items
还支持更多的其他值。 比如还有 baseline
和 firstline
等对齐方式。
模拟 Android FrameLayout 布局功能。
例如我们要实现如下的 FrameLayout 中布局:
FrameLayout 布局效果示意图
本来呢。在 CSS 中利用 position:relative
这样的布局技术是更接近 FrameLayout
布局技术理理念的东西。 因为毕竟 Flex 的布局技术侧重于对所有的子元素应用统一的规则, 所有子元素表现得齐齐整整。 而 FrameLayout 则侧重于各个子元素可以在布局容器中可以各自以四面八方来对齐。(即组合不同的 layout_gravity
)
不过要用 Flexbox 实现上面的布局效果也不是什么难事。 Flexbox 讲究的就是行与列的排列组合。最后实现的代码如下:
首先是 HTML 代码。 将 AFC 分为一组。 BD 为一组。 E 单独一组。
<div class="box">
<div class="left">
<div class="item A">A</div>
<div class="item F">F</div>
<div class="item C">C</div>
</div>
<div class="item E">E</div>
<div class="right">
<div class="item B">B</div>
<div class="item D">D</div>
</div>
</div>
下面是具体的 CSS 布局代码:
.box{
width: 300px;
height: 300px;
background:#000;
display:flex;
justify-content: space-between;
align-items: stretch;
}
.item{
padding:10px;
color:white;
background:red;
border:1px solid gray;
text-align:center;
}
.left{
display:flex;
flex-direction: column;
justify-content: space-between;
}
.right{
display:flex;
flex-direction: column;
justify-content: space-between;
}
.item.E{
align-self:center;
}
解释说明如下:
-
对于
.box
容器利用justify-content:space-between
属性将使得剩余的布局空间在各元素之间等分,并且左边的靠左,右的靠右。 其中align-items:stretch
不是必须的,因为这是默认值。它使每一个分组占满整个垂直方向上的空间。 -
.left
和.right
设置的属性值相同。通过display:flex
使其本身也成为一个 Flexbox 布局容器。 通过flex-direction:column
指定其是按列排的。 再通过justify-content: space-between
使其子元素在主排列方向上等分剩余的空间。 -
对于中间的
E
元素块。 由于父 Flexbox 容器的justify-content:space-between
使得其已经在主排列方向上居中了。然后在通过align-self:center
使其在交叉排列方向上居中。
模拟 Android RelativeLayout 布局功能。
Android 文档中介绍 RelativeLayout
有这么一个示例效果图:
下面是模拟的效果:
Flexbox 模拟 RelativeLayout 布局效果图
对应 HTML 代码如下:
<div class="form">
<input class="name" value="提醒事项"/>
<div class="datetime">
<input class="date" value="2018-05-24" type="date"/>
<input class="time" value="08:30" type="time"/>
</div>
<button class="done">完成</button>
</div>
对应 CSS 代码如下:
body{ background: gray;}
.form{
width: 300px;
height: 300px;
background: white;
display:flex;
flex-direction: column;
padding:15px;
}
.datetime{
display:flex;
margin-top:8px;
}
.date{
flex-grow:1;
}
.done{
align-self: flex-end;
margin-top:8px;
}
说明如下:
- 整个表单声明为按列布局的布局容器:
display:flex; flex-direction:column;
- 将
.datetime
分组中的.date
设置为可以拉伸以占据剩下的空白。 - 将
.done
按钮通过align-self: flex-end;
来实现右对齐,规范点说是按排列方向的交叉方向的尾端对齐。简单来说在这里的场景下就是右对齐的意思。(对于在阿拉伯语环境下应该是会表现为左对齐。)
模拟 Android GridLayout 布局功能
在 Android Grid Layout 一文中有提到使用 GridLayout
实现布局的优势,并且提到使用 RelativeLayout 的不足等。
下面我们用 Flexbox 来实现一下。
用 Flexbox
来实现的话,可以说是相当的简单:
HTML 代码如下:
<div class="box">
<div class="pic">
</div>
<div class="user-comment">
<div class="avatar" > </div>
<p class="comment">
When we describe flexbox as being one dimensional
we are describing the fact that flexbox deals with layout in one dimension at a time
</p>
</div>
<div class="user-comment">
<div class="avatar" > </div>
<p class="comment">
The main axis is defined by flex-direction
</p>
</div>
</div>
对应 CSS 代码如下:
.box{
width: 300px;
height: 600px;
background: white;
display:flex;
flex-direction: column;
padding:15px;
}
.pic{
height:120px;
background:#000000;
}
.user-comment{
display: flex;
border: dashed blue;
}
.avatar{
width: 32px;
height: 32px;
border-radius: 16px;
background:gray;
flex: none;
margin-top: 1em;
}
.comment{
margin-left: 8px;
}
唯一需要说明的是我们将 .avatar
的 flex
属性设置为 none
,否则这个占位的圆形头像会被压缩。下面将稍微深入的了解一下 Flex Item 的 flex
属性。
flex
属性
其实 flex
属性是 flex-grow
, flex-shrink
和 flex-basis
这三个属性的简写形式。
-
flex-grow
上面简介过了,相同于android:weight
属性。默认值为0
,即不要拉伸。 -
flex-shrink
默认值为1
, 即可以压缩。 -
flex-basis
默认值为auto
, 即其基本大小由其本身的本质特性决定。 例如一个图片的话其本质大小即其图片本身的大小。
上面说了 flex
是简写形式。
也就是说。
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
这三行,相当于 flex: 0 1 auto
一行。
同时 flex
属性又为一些常见的组合值,提供了一些简写的关键词值。
例如:
-
flex: initial
就相当于flex: 0 1 auto
即默认值。 -
flex: auto
相当于flex: 1 1 auto
意味着元素能屈能伸。 -
flex: none
相当于flex: 0 0 auto
意味着元素不能拉伸也不能压缩。
Flexbox 实现 九宫格布局
一个简单的九宫格实现效果如图:
image.png
对应 html 如下:
<div class="box">
<div class="item"><span>1</span></div>
<div class="item"><span>2</span></div>
<div class="item"><span>3</span></div>
<div class="item"><span>4</span></div>
<div class="item"><span>5</span></div>
<div class="item"><span>6</span></div>
<div class="item"><span>7</span></div>
<div class="item"><span>8</span></div>
<div class="item"><span>9</span></div>
</div>
对应 CSS 如下:
.box{
width: 300px;
height: 300px;;
background:#000;
display:flex;
flex-wrap:wrap;
}
.item{
color:white;
background:red;
box-sizing: border-box;
text-align:center;
vertical-align:middle;
font-size:48px;
width: 33.33333%;
display: flex;
align-items: center;
justify-content: center;
}
.item:nth-child(3n+1){
border-right: 1px solid orange;
}
.item:nth-child(3n+2){
border-right: 1px solid orange;
}
.item:nth-child(-n+6){
border-bottom: 1px solid orange;
}
这里面利用到的 Flexbox 的主要技术就是。 flex-wrap: wrap
Flexbox 布局容器中 flex-wrap
默认属性是 nowrap
, 对于 flex-direction:row
的容器默认是不换行的。 因为 Flexbox 主要的定位是一维的布局容器。
When we describe flexbox as being one dimensional we are describing the fact that flexbox deals with layout in one dimension at a time — either as a row or as a column.
另外的一些实现要点说明:
-
将
.item
的宽度设置为 1/3 的宽度,即:wdith: 33.3333%
,同时将box-sizing
属性设置为border-box
即width
属性的值是包含了content-width
和border-width
的。以免由于有border
导致九宫的布局问题。 -
通过
:nth-child
伪类选择器将指定的宫格添加border
。:nth-child(-n+6)
表示前6个宫格有border-bottom
网友评论