美文网首页Android开发经验谈
写给 Android 开发者的 CSS Flexbox 布局学习

写给 Android 开发者的 CSS Flexbox 布局学习

作者: 一半晴天 | 来源:发表于2018-05-19 17:27 被阅读686次

    Flexbox 布局的基本概念

    通过设置元素的 display 属性值为 flexdisplay:flex 可以将一个元素变成一个 Flexbox 布局容器。而其中的子元素则为叫做 Flex Item
    Flexbox 非常灵活和强大,一时半会是难以掌握的。下面我将结合我所了解到的其他布局容器的知识来进行对比以加深对布局容器的理解。

    模拟 Android LinearLayout 布局的功能。

    如果你知道 Android 中的 LinearLayout 的话,我可以告诉你,Flexbox 可以轻松的模拟 LinearLayout 布局的功能。

    1. Flexbox 中有一个 flex-direction 对应 android:orientation 属性。具体来说
      flex-direction:row 对应 android:orientation="horizontal" 这也是两者的默认值, 而 flex-direction: column 对应 android:orientation="vertical". 另外,Flexbox 还支持另外两个属性: row-reversecolumn-reverse 了解一下。

    2. 对于布局容器的子元素,Flexbox 有一个 flex-grow 属性对应 android:weight 属性。

    3. 对于布局容器的子元素的 android:layout_gravity, Flexbox 有一个 align-self属性来对应。 但是对于 LinearLayout
      horizontal 的容器,android:layout_gravitycenter_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:widthandroid:heightmatch_parent属性值。
      另外,在默认值上,Flexbox 子元素的表现是 stretchLinearLayout 则表现是相当于 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-selfalign-items 还支持更多的其他值。 比如还有 baselinefirstline 等对齐方式。

    模拟 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;
            }
    

    解释说明如下:

    1. 对于 .box 容器利用 justify-content:space-between 属性将使得剩余的布局空间在各元素之间等分,并且左边的靠左,右的靠右。 其中 align-items:stretch 不是必须的,因为这是默认值。它使每一个分组占满整个垂直方向上的空间。

    2. .left.right 设置的属性值相同。通过 display:flex 使其本身也成为一个 Flexbox 布局容器。 通过 flex-direction:column 指定其是按列排的。 再通过 justify-content: space-between 使其子元素在主排列方向上等分剩余的空间。

    3. 对于中间的 E 元素块。 由于父 Flexbox 容器的 justify-content:space-between 使得其已经在主排列方向上居中了。然后在通过 align-self:center 使其在交叉排列方向上居中。

    模拟 Android RelativeLayout 布局功能。

    Android 文档中介绍 RelativeLayout 有这么一个示例效果图:

    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;
            }
    

    说明如下:

    1. 整个表单声明为按列布局的布局容器: display:flex; flex-direction:column;
    2. .datetime 分组中的 .date 设置为可以拉伸以占据剩下的空白。
    3. .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;
            }
    
    

    唯一需要说明的是我们将 .avatarflex 属性设置为 none ,否则这个占位的圆形头像会被压缩。下面将稍微深入的了解一下 Flex Item 的 flex 属性。

    flex 属性

    其实 flex 属性是 flex-grow, flex-shrinkflex-basis 这三个属性的简写形式。

    1. flex-grow 上面简介过了,相同于 android:weight 属性。默认值为 0,即不要拉伸。
    2. flex-shrink 默认值为 1, 即可以压缩。
    3. flex-basis 默认值为 auto, 即其基本大小由其本身的本质特性决定。 例如一个图片的话其本质大小即其图片本身的大小。

    上面说了 flex 是简写形式。
    也就是说。

    flex-grow: 0;
    flex-shrink: 1;
    flex-basis: auto;
    

    这三行,相当于 flex: 0 1 auto 一行。
    同时 flex 属性又为一些常见的组合值,提供了一些简写的关键词值。
    例如:

    1. flex: initial 就相当于 flex: 0 1 auto 即默认值。
    2. flex: auto 相当于 flex: 1 1 auto 意味着元素能屈能伸。
    3. 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.

    另外的一些实现要点说明:

    1. .item 的宽度设置为 1/3 的宽度,即: wdith: 33.3333%,同时将 box-sizing 属性设置为 border-boxwidth 属性的值是包含了 content-widthborder-width 的。以免由于有 border 导致九宫的布局问题。

    2. 通过 :nth-child 伪类选择器将指定的宫格添加 border:nth-child(-n+6) 表示前6个宫格有 border-bottom

    相关文章

      网友评论

        本文标题:写给 Android 开发者的 CSS Flexbox 布局学习

        本文链接:https://www.haomeiwen.com/subject/ridsdftx.html