React Native基础&入门教程:初步使用Flexbox布局
在上篇中,笔者分享了部分安装并调试React Native应用过程里的一点经验,如果还没有看过的同学请点击《React Native基础&入门教程:调试React Native应用的一小步》。
在本篇里,让我们一起来了解一下,什么是Flexbox布局,以及如何使用。
一、长度的单位
在开始任何布局之前,让我们来首先需要知道,在写React Native组件样式时,长度的不带单位的,它表示“与设备像素密度无关的逻辑像素点”。
这个怎么理解呢?
我们知道,屏幕上一个发光的最小点,对应着一个pixel(像素)点。
假设下面三个矩形,代表三个屏幕大小一样的设备,但是,它们拥有的分辨率(resolution)不同:

图1.相同尺寸的设备 不同的分辨率
图上的每一个小格子,其实就代表了一个像素(pixel)。可以看到,一个像素点的大小,在这个三个物理尺寸一样但拥有不同分辨率的设备上,是不一样的。
如果我们以像素为单位来设置一个界面元素的大小,比如说2px的高度,那么这2px的长度上面的设备中就会是下面这个样子:

图2.不同分辨率下的2px实际高度
它们真实显示出的长度是不一样的。
我们想要一种长度单位,在同样物理尺寸大小的屏幕上(不论分辨率谁高谁低,只要物理尺寸大小一样即可),1个单位的长度所代表的物理尺寸是一样的。这种单位就应该是独立于分辨率的,把它起一个名字叫做 density-independent pixels,简称dp。这其实就是Android系统中所使用的长度单位。
举例来说,2dp宽,2dp高的内容,在不同分辨率但屏幕尺寸一样的设备上所显示出的物理大小是一样的。(一个题外话:有些Android开发者建议所有可点击的按钮,宽高都不应该少于48dp。)

图3. 2dp * 2dp大小的内容 在同样尺寸的屏幕中所占据的物理大小一致
Android中字体大小使用另外一个单位,叫做scale independent pixels,简称sp。这个单位和dp很类似,不过它通常是用在对字体大小的设置中。通过它设置的字体,可以根据系统字体大小的变化而变化。
pixel与dp存在一个公式:px = dp * (dpi/160)。
dpi表示dot per inch,是每英寸上的像素点,它也有个自己的计算公式,具体这里就不展开了。只需要知道我们之所以要使用一个独立于设备分辨率的单位,主要是为了让应用在不同分辨率的设备中,看起来一致。
在RN中,同样也拥有一个类似于dp的长度单位。如果我们想知道自己的屏幕以这种长度的计量下是多少单位,可以通过引入react-native包中的Dimensions拿到,同时还可以查看本机的像素比例是多少。
import {
Text,
View,
Dimensions,
PixelRatio
} from 'react-native';
const { height, width } = Dimensions.get('window');
const pxRatio = PixelRatio.get();
<View style={styles.container}>
<Text style={styles.welcome}>
{`width: ${width}, height: ${height}`}
</Text>
<Text style={styles.welcome}>
{`pixel radio: ${pxRatio}`}
</Text>
</View>
显示如下:

图4. 当前手机的屏幕信息
它反映出,当前手机屏幕的宽度占据360个单位,高度占据640个单位。像素比例是3,实际上这就是一个 1080 * 1920 像素的手机。其中1080 = width * pixelRadio, 1920 = height * pixelRatio
二、Flexbox布局
Flexbox布局,也就是弹性盒模型布局。也许有Android开发经验的朋友还对LinearLayout,RelativeLayout,FrameLayout等布局方法记忆犹新,但是对于更了解CSS的Web开发者而言,使用flexbox布局肯定会让他感受到更加顺手的开发体验。
RN中的flexbox布局,其实源于CSS中的flexbox(弹性盒子)布局规范。其实它在CSS中还处于Last Call Working Draft(最终征求意见稿)阶段,但是主流浏览器对它都有了良好的支持。在RN中,几乎完全借鉴了其中的布局语义,同时更没有浏览器兼容的烦恼,用起来是很方便的。RN中只是把CSS的属性用camelCase写法代替连字符写法。后面还还会看到,默认的flex方向也不同。
理解弹性盒模型布局,首先要知道四个最基本的概念:Flex Container(容器),Flex Item(项),Flex Direction(方向)和Axis(轴)。
1.Flex Container
就是包裹内容的容器,需要把它的display设置为‘flex’(或者'inline-flex')。
以下6个属性设置在容器上。
- alignItems 指定item在侧轴上的对齐方式
- alignContent 指定item在多条轴上的对齐方式
- flexDirection 指定主轴方向
- flexWrap 指定item在主轴方向如何换行
- flexFlow flexDirection属性和flexWrap属性的简写形式
- justifyContent 指定item在主轴上的分布方式
2.Flex Item
容器做直接包裹的元素。所谓弹性盒布局,通常想要布局的东西就是它们。
以下6个属性设置在项目上。
- alignSelf 每个item可以单独设置对齐方式 覆盖Flex Container给设置的alignItems
- order 指定item排列顺序 数字越小越靠前
- flexGrow 指定item的拉伸比例
- flexShrink 指定item的压缩比例
- flexBasis 指定item在分配多余空间之前,占主轴的大小
- flex 其实是 flexGrow flexShrink flexBasis的简写
3.Flex Direction and Axis
在弹性盒子中,项目默认沿着main axis(主轴)排列,和主轴垂直的轴叫做cross axis,叫做侧轴,或者交叉轴。
在盒子中,排列项目又四个方向:水平的正反两个,垂直的正反两个。
结构代码:
<View>
<View style={styles.row}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
<View style={styles.rowReverse}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
<View style={styles.column}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
<View style={styles.columnReverse}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
</View>
样式代码:
row: {
backgroundColor: '#ffe289',
flexDirection: 'row'
},
rowReverse: {
flexDirection: 'row-reverse'
},
column: {
backgroundColor: '#ffe289',
flexDirection: 'column'
},
columnReverse: {
flexDirection: 'column-reverse'
},

图5. flexDirection
由于网上关于flex布局讲解的资源挺丰富的,读者可以参考最后给出的连接,或者自行上网搜索,CSS中的和RN是相通的。
这里主要分享个人在学习过程中,觉得容易引起混淆的两个小点。
首先,justify-content和align-content这两个属性,可能比较容易搞错它们作用的方向。
其中,justify-content是设置items沿着主轴上是如何分布的。align-content是设置items沿着侧轴如何对齐的。
还是拿之前的例子,默认情况下,flex的方向是column(这个与移动端与web页面不同,在web页面用CSS设置flex布局,默认的fiex-direction是row,即水平从左往右)。
在移动端,主轴默认是垂直方向,从上往下。让我们把它的高度设置高一点,放3个item在里面:
结构代码:
<View>
<View style={styles.defaultFlex}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
</View>
样式代码:
defaultFlex: {
height: 300,
backgroundColor: '#ffe289',
display: 'flex'
}

图6. 默认的flex
justify-content设置items在主轴方向的如何分布,比如,如果我们加上justifyContent: 'space-between'
defaultFlex: {
height: 300,
backgroundColor: '#ffe289',
display: 'flex',
justifyContent: 'space-between'
}
items就沿主轴分开了。

图7. justifyContent: 'space-between'
如果我们设置alignItems: 'center',项目就沿侧轴(这里就是水平轴)居中了。注意这两个属性是可以同时起作用的。

图8. justifyContent: 'space-between' 以及 alignItems: 'center'
然后,值得指出的是,flex这个属性,其实是flexGrow, flexShrink, flexBasis(对应的CSS属性flex-grow, flex-shrink和flex-basis)三个属性的结合。
我们通常在移动端看到的flex:1这个设置,其实是对flex-grow的设置。后者的默认值为0。使用把flex-grow设置为正整数的方法,可以让item按比例分布,或者在其他item为固定大小时撑满剩余的盒子空间,就仿佛具有弹性一样。
结构代码:
<View style={styles.container}>
<View style={styles.flex1}></View>
<View style={styles.flex2}></View>
<View style={styles.flex3}></View>
</View>
样式代码:
container: {
flex: 1
},
flex1: {
// height: 99,
flexGrow: 1,
backgroundColor: 'orange',
},
flex2: {
flexGrow: 2,
backgroundColor: 'lightblue',
},
flex3: {
flexGrow: 3,
backgroundColor: 'green',
},

图9. 按比例分布
需要注意的是,如果父容器的尺寸为零(即没有设置宽高,或者没有设定flex),即使子组件如果使用了flex,也是无法显示的。
所以这里最外层的使用了flex布局的,flex:1,表示让它占据了垂直的整个空间。
三、小小实战演练
让我们来简单使用flex布局,对之前的例子稍加调整,实现一个头部,底部固定高度,中间内容占满剩下的屏幕的布局:
第一步,调整结构:
<View style={styles.container}>
<View style={styles.header}></View>
<View style={styles.body}></View>
<View style={styles.footer}></View>
</View>
调整样式:
container: {
flex: 1
},
header: {
height: 60,
backgroundColor: 'orange',
},
body: {
flexGrow: 1,
backgroundColor: 'lightblue',
},
footer: {
height: 60,
backgroundColor: 'green',
}

图10. 有头尾的布局
第二部,给header添加标题。
我们让头部的分成3部分,左边模拟一个返回按钮,中间显示标题文字,右边模拟一把小叉:
<View style={styles.header}>
<Text style={styles.back}>返回</Text>
<Text style={styles.title}>这是一个标题</Text>
<Text style={styles.exit}>×</Text>
</View>
需要把header的flexDirection设置为水平方向:
header: {
height: 60,
backgroundColor: 'orange',
flexDirection: 'row',
alignItems: 'center'
},
back: {
color: 'white',
marginLeft: 15
},
title: {
flexGrow: 1,
fontSize: 20,
color: 'white',
textAlign: 'center'
},
exit: {
marginRight: 20,
fontSize: 20,
color: 'white'
}

图11. header有了标题
第三步,我们可以把footer三等分,模拟成菜单的样子:
<View style={styles.footer}>
<Text style={styles.firstMenu}>添加</Text>
<Text style={styles.menu}>删除</Text>
<Text style={styles.menu}>修改</Text>
</View>
添加样式:
footer: {
height: 60,
backgroundColor: 'green',
flexDirection: 'row',
alignItems: 'center'
},
menu: {
flexGrow: 1,
textAlign: 'center',
borderColor: 'white',
borderLeftWidth: 1,
color: 'white'
},
firstMenu: {
flexGrow: 1,
textAlign: 'center',
color: 'white'
},

图12. footer三等分 模拟菜单
最后,让我们在body里也填入几个带按钮的输入框。
引入TextInput和Button组件,然后把它们分三组放入body中,
<View style={styles.body}>
<View style={styles.inputRow}>
<TextInput style={styles.textInput}></TextInput>
<Button style={styles.btn} onPress={() => {}} title="确定"></Button>
</View>
<View style={styles.inputRow}>
<TextInput style={styles.textInput}></TextInput>
<Button style={styles.btn} onPress={() => {}} title="非常确定"></Button>
</View>
<View style={styles.inputRow}>
<TextInput style={styles.textInput}></TextInput>
<Button style={styles.btn} onPress={() => {}} title="确定一定以及肯定"></Button>
</View>
</View>
添加样式:
body: {
flexGrow: 1,
backgroundColor: 'lightblue',
},
inputRow: {
flexDirection: 'row',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
},
textInput: {
flex: 1
},
btn: {
minWidth: 60
}

flex布局的一个常用实践是,部分内容固定宽高,让剩下的内容自适应。
像上面这样,我们给Button有一个最小宽度,且TextInput的flexGrow为1,这样的做法可以实现,TextInput总是占满剩下的宽度,且可伸缩。
看了上面的例子,是否觉得在React Native中使用Flexbox布局也挺简单呢?
希望这是个不错的开始。
移动端应用开发利器:
SpreadJS纯前端表格控件、WijmoJS纯前端控件集为您的移动应用带来更加灵活的操作体验和更佳美观的外观风格,欢迎下载。
扩展阅读:
React Native基础&入门教程:初步使用Flexbox布局的更多相关文章
- React Native基础&入门教程:以一个To Do List小例子,看props和state
本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 在上篇中,我们介绍了什么是Flexbox布局,以及如何使用Flexb ...
- React Native基础&入门教程:调试React Native应用的一小步
React Native(以下简称RN)为传统前端开发者打开了一扇新的大门.其中,使用浏览器的调试工具去Debug移动端的代码,无疑是最吸引开发人员的特性之一. 试想一下,当你在手机屏幕按下一个按钮, ...
- React Native学习(九)—— 使用Flexbox布局
本文基于React Native 0.52 Demo上传到Git了,有需要可以看看,写了新内容会上传的.Git地址 https://github.com/gingerJY/React-Native-D ...
- 一起来点React Native——你必须要会点FlexBox布局
一.FlexBox布局 1.1 FlexBox是什么意思呢? flexible(形容词):能够伸缩或者很容易变化,以适应外界条件的变化 box(名词):通用的矩形容器 1.2 什么是FlexBox布 ...
- react native基础与入门
react native基础与入门 一.react native 的优点 1.跨平台(一才两用) 2.低投入高回报 (开发成本低.代码复用率高) 3.性能高:拥有独立的js渲染引擎,比传统的h5+ w ...
- React Native小白入门学习路径——二
万万没想到,RN组仅剩的一个学长也走了,刚进实验室没几天就被告知这样的事情,一下子还真的有点接受不了,现在RN组就成了为一个没有前辈带的组了,以后学习就更得靠自己了吧.唉,看来得再努力一点了. 这一周 ...
- React Native 从入门到原理一
React Native 从入门到原理一 React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却 ...
- React Native小白入门学习路径——五
React Native小白入门学习路径--五 序 "哦天呐!" 这句话成了我在实验室的口头禅, 老师可能觉得我们都是大神吧,都还在看着基础就给布置了那么多任务:写一个RN的TDD ...
- [置顶] IOS 基础入门教程
IOS 基础入门教程 教程列表: IOS 简介 IOS环境搭建 Objective C 基础知识 创建第一款iPhone应用程序 IOS操作(action)和输出口(Outlet) iOS - 委托( ...
随机推荐
- 高德地图JS API获取经纬度,根据经纬度获取城市
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- leetcode-比特位计数
一.题目描述 给定一个非负整数 num.对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回. 示例 1: 输入: 2 输出: [0,1,1] 示例 ...
- [Swift]LeetCode119. 杨辉三角 II | Pascal's Triangle II
Given a non-negative index k where k ≤ 33, return the kth index row of the Pascal's triangle. Note t ...
- [Swift]LeetCode325. 最大子数组之和为k $ Maximum Size Subarray Sum Equals k
Given an array nums and a target value k, find the maximum length of a subarray that sums to k. If t ...
- [Swift]LeetCode488. 祖玛游戏 | Zuma Game
Think about Zuma Game. You have a row of balls on the table, colored red(R), yellow(Y), blue(B), gre ...
- [Reversing.kr] Easy Crack Writeup
题目:http://reversing.kr/ Easy Crack IDA打开.分析可知Sub_401080是关键函数.F5后. 当满足 则跳转成功.拼接后得到flag flag: Ea5yR3ve ...
- [Abp 源码分析]六、工作单元的实现
0.简介 在 Abp 框架内部实现了工作单元,在这里讲解一下,什么是工作单元? Unit Of Work(工作单元)模式用来维护一个由已经被业务事物修改(增加.删除或更新)的业务对象组成的列表.Uni ...
- Solaris 11 配置IP地址
查看ipipadm show-addr 删除IP地址ipadm delete-addr net0/v4 配置IP地址ipadm create-addr –T static –a local=10.90 ...
- pymysql.err.InternalError: (1205, 'Lock wait timeout exceeded; try restarting transaction')错误处理
问题描述: 在使用pymysql库时,利用游标执行插入操作,产生错误,会出现pymysql.err.InternalError: (1205, 'Lock wait timeout exceeded; ...
- 【SpringCloud】HystrixCommand的threadPoolKey默认值及线程池初始化
关于threadPoolKey默认值的疑问 使用SpingCloud必然会用到Hystrix做熔断降级,也必然会用到@HystrixCommand注解,@HystrixCommand注解可以配置的除了 ...