写点什么

React Native 探索(二):布局篇

  • 2015-06-24
  • 本文字数:8007 字

    阅读完需:约 26 分钟

宽度单位和像素密度

react 的宽度不支持百分比,设置宽度时不需要带单位 {width: 10}, 那么 10 代表的具体宽度是多少呢?

不知道是官网文档不全还是我眼瞎,反正是没找到,那做一个实验自己找吧:

复制代码
var Dimensions = require('Dimensions');
<Text style={styles.welcome}>
window.width={Dimensions.get('window').width + '\n'}
window.height={Dimensions.get('window').height + '\n'}
pxielRatio={PixelRatio.get()}
</Text>

默认用的是 iphone6 的模拟器结果是:

复制代码
window.width=375
window.height=667
pxielRatio=2

我们知道 iphone 系列的尺寸如下图:

可以看到 iphone 6 的宽度为 375pt,对应了上边的 375,由此可见 react 的单位为 pt。 那如何获取实际的像素尺寸呢? 这对图片的高清化很重要,如果我的图片大小为 100*100 px. 设置宽度为 100 * 100. 那在 iphone 上的尺寸就是模糊的。 这个时候需要的图像大小应该是 100 * pixelRatio 的大小 。

react 提供了 PixelRatio 的获取方式 https://facebook.github.io/react-native/docs/pixelratio.html

复制代码
var image = getImage({
width: 200 * PixelRatio.get(),
height: 100 * PixelRatio.get()
});
<Image source={image} style={{width: 200, height: 100}} />

flex 的布局

默认宽度

我们知道一个 div 如果不设置宽度,默认的会占用 100% 的宽度, 为了验证 100% 这个问题, 做三个实验

  1. 根节点上方一个 View, 不设置宽度
  2. 固定宽度的元素上设置一个 View, 不设置宽度
  3. flex 的元素上放一个 View 宽度, 不设置宽度
复制代码
<Text style={[styles.text, styles.header]}>
根节点上放一个元素,不设置宽度
</Text>
<View style={{height: 20, backgroundColor: '#333333'}} />
<Text style={[styles.text, styles.header]}>
固定宽度的元素上放一个 View,不设置宽度
</Text>
<View style={{width: 100}}>
<View style={{height: 20, backgroundColor: '#333333'}} />
</View>
<Text style={[styles.text, styles.header]}>
flex 的元素上放一个 View,不设置宽度
</Text>
<View style={{flexDirection: 'row'}}>
<View style={{flex: 1}}>
<View style={{height: 20, backgroundColor: '#333333'}} />
</View>
<View style={{flex: 1}}/>
</View>

结果可以看到 flex 的元素如果不设置宽度, 都会百分之百的占满父容器。

水平垂直居中

css 里边经常会做的事情是去讲一个文本或者图片水平垂直居中,如果使用过 css 的 flexbox 当然知道使用alignItemsjustifyContent . 那用 react-native 也来做一下实验

复制代码
<Text style={[styles.text, styles.header]}>
水平居中
</Text>
<View style={{height: 100, backgroundColor: '#333333', alignItems: 'center'}}>
<View style={{backgroundColor: '#fefefe', width: 30, height: 30, borderRadius: 15}}/>
</View>
<Text style={[styles.text, styles.header]}>
垂直居中
</Text>
<View style={{height: 100, backgroundColor: '#333333', justifyContent: 'center'}}>
<View style={{backgroundColor: '#fefefe', width: 30, height: 30, borderRadius: 15}}/>
</View>
<Text style={[styles.text, styles.header]}>
水平垂直居中
</Text>
<View style={{height: 100, backgroundColor: '#333333', alignItems: 'center', justifyContent: 'center'}}>
<View style={{backgroundColor: '#fefefe', width: 30, height: 30, borderRadius: 15}}/>
</View>

网格布局

网格布局实验, 网格布局能够满足绝大多数的日常开发需求,所以只要满足网格布局的 spec,那么就可以证明 react 的 flex 布局能够满足正常开发需求

等分的网格

复制代码
<View style={styles.flexContainer}>
<View style={styles.cell}>
<Text style={styles.welcome}>
cell1
</Text>
</View>
<View style={styles.cell}>
<Text style={styles.welcome}>
cell2
</Text>
</View>
<View style={styles.cell}>
<Text style={styles.welcome}>
cell3
</Text>
</View>
</View>
styles = {
flexContainer: {
// 容器需要添加 direction 才能变成让子元素 flex
flexDirection: 'row'
},
cell: {
flex: 1,
height: 50,
backgroundColor: '#aaaaaa'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
}

左边固定, 右边固定,中间 flex 的布局

复制代码
<View style={styles.flexContainer}>
<View style={styles.cellfixed}>
<Text style={styles.welcome}>
fixed
</Text>
</View>
<View style={styles.cell}>
<Text style={styles.welcome}>
flex
</Text>
</View>
<View style={styles.cellfixed}>
<Text style={styles.welcome}>
fixed
</Text>
</View>
</View>
styles = {
flexContainer: {
// 容器需要添加 direction 才能变成让子元素 flex
flexDirection: 'row'
},
cell: {
flex: 1,
height: 50,
backgroundColor: '#aaaaaa'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
cellfixed: {
height: 50,
width: 80,
backgroundColor: '#fefefe'
}
}

嵌套的网格

通常网格不是一层的,布局容器都是一层套一层的, 所以必须验证在real world下面的网格布局

复制代码
<Text style={[styles.text, styles.header]}>
嵌套的网格
</Text>
<View style={{flexDirection: 'row', height: 200, backgroundColor:"#fefefe", padding: 20}}>
<View style={{flex: 1, flexDirection:'column', padding: 15, backgroundColor:"#eeeeee"}}>
<View style={{flex: 1, backgroundColor:"#bbaaaa"}}>
</View>
<View style={{flex: 1, backgroundColor:"#aabbaa"}}>
</View>
</View>
<View style={{flex: 1, padding: 15, flexDirection:'row', backgroundColor:"#eeeeee"}}>
<View style={{flex: 1, backgroundColor:"#aaaabb"}}>
<View style={{flex: 1, flexDirection:'row', backgroundColor:"#eeaaaa"}}>
<View style={{flex: 1, backgroundColor:"#eebbaa"}}>
</View>
<View style={{flex: 1, backgroundColor:"#bbccee"}}>
</View>
</View>
<View style={{flex: 1, backgroundColor:"#eebbdd"}}>
</View>
</View>
<View style={{flex: 1, backgroundColor:"#aaccaa"}}>
<ScrollView style={{flex: 1, backgroundColor:"#bbccdd", padding: 5}}>
<View style={{flexDirection: 'row', height: 50, backgroundColor:"#fefefe"}}>
<View style={{flex: 1, flexDirection:'column', backgroundColor:"#eeeeee"}}>
<View style={{flex: 1, backgroundColor:"#bbaaaa"}}>
</View>
<View style={{flex: 1, backgroundColor:"#aabbaa"}}>
</View>
</View>
<View style={{flex: 1, flexDirection:'row', backgroundColor:"#eeeeee"}}>
<View style={{flex: 1, backgroundColor:"#aaaabb"}}>
<View style={{flex: 1, flexDirection:'row', backgroundColor:"#eeaaaa"}}>
<View style={{flex: 1, backgroundColor:"#eebbaa"}}>
</View>
<View style={{flex: 1, backgroundColor:"#bbccee"}}>
</View>
</View>
<View style={{flex: 1, backgroundColor:"#eebbdd"}}>
</View>
</View>
<View style={{flex: 1, backgroundColor:"#aaccaa"}}>
</View>
</View>
</View>
<Text style={[styles.text, styles.header, {color: '#ffffff', fontSize: 12}]}>
{(function(){
var str = '';
var n = 100;
while(n--) {
str += '嵌套的网格' + '\n';
}
return str;
})()}
</Text>
</ScrollView>
</View>
</View>
</View>

好在没被我玩儿坏,可以看到上图的嵌套关系也是足够的复杂的,(我还加了一个 ScrollView,然后再嵌套整个结构)嵌套多层的布局是没有问题的。

图片布局

首先我们得知道图片有一个 stretchMode. 通过 Image.resizeMode 访问

找出有哪些 mode

复制代码
var keys = Object.keys(Image.resizeMode).join(' ');

打印出来的是 contain, cover, stretch 这几种模式, (官方文档不知道为什么不直接给出)

尝试使用这些 mode

复制代码
<Text style={styles.welcome}> 100px height </Text>
<Image style={{height: 100}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />

100px 高度, 可以看到图片适应 100 高度和全屏宽度,背景居中适应未拉伸但是被截断也就是 cover。

复制代码
<Text style={styles.welcome}> 100px height with resizeMode contain </Text>
<View style={[{flex: 1, backgroundColor: '#fe0000'}]}>
<Image style={{flex: 1, height: 100, resizeMode: Image.resizeMode.contain}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />
</View>

contain 模式容器完全容纳图片,图片自适应宽高

复制代码
<Text style={styles.welcome}> 100px height with resizeMode cover </Text>
<View style={[{flex: 1, backgroundColor: '#fe0000'}]}>
<Image style={{flex: 1, height: 100, resizeMode: Image.resizeMode.cover}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />
</View>

cover 模式同 100px 高度模式

复制代码
<Text style={styles.welcome}> 100px height with resizeMode stretch </Text>
<View style={[{flex: 1, backgroundColor: '#fe0000'}]}>
<Image style={{flex: 1, height: 100, resizeMode: Image.resizeMode.stretch}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />
</View>

stretch 模式图片被拉伸适应屏幕

复制代码
<Text style={styles.welcome}> set height to image container </Text>
<View style={[{flex: 1, backgroundColor: '#fe0000', height: 100}]}>
<Image style={{flex: 1}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />
</View>

随便试验了一下, 发现高度设置到父容器,图片 flex 的时候也会等同于 cover 模式

绝对定位和相对定位

复制代码
<View style={{flex: 1, height: 100, backgroundColor: '#333333'}}>
<View style={[styles.circle, {position: 'absolute', top: 50, left: 180}]}>
</View>
</View>
styles = {
circle: {
backgroundColor: '#fe0000',
borderRadius: 10,
width: 20,
height: 20
}
}

和 css 的标准不同的是, 元素容器不用设置position:'absolute|relative' .

复制代码
<View style={{flex: 1, height: 100, backgroundColor: '#333333'}}>
<View style={[styles.circle, {position: 'relative', top: 50, left: 50, marginLeft: 50}]}>
</View>
</View>

相对定位的可以看到很容易的配合 margin 做到了。 (我还担心不能配合 margin,所以测试了一下:-:)

padding 和 margin

我们知道在 css 中区分 inline 元素和 block 元素,既然 react-native 实现了一个超级小的 css subset。那我们就来实验一下 padding 和 margin 在 inline 和非 inline 元素上的 padding 和 margin 的使用情况。

*padding *

复制代码
<Text style={[styles.text, styles.header]}>
在正常的 View 上设置 padding
</Text>
<View style={{padding: 30, backgroundColor: '#333333'}}>
<Text style={[styles.text, {color: '#fefefe'}]}> Text Element</Text>
</View>
<Text style={[styles.text, styles.header]}>
在文本元素上设置 padding
</Text>
<View style={{padding: 0, backgroundColor: '#333333'}}>
<Text style={[styles.text, {backgroundColor: '#fe0000', padding: 30}]}>
text 元素上设置 paddinga
</Text>
</View>

在 View 上设置 padding 很顺利,没有任何问题, 但是如果在 inline 元素上设置 padding, 发现会出现上面的错误, paddingTop 和 paddingBottom 都被挤成 marginBottom 了。 按理说,不应该对 Text 做 padding 处理, 但是确实有这样的问题存在,所以可以将这个问题 mark 一下。

margin

复制代码
<Text style={[styles.text, styles.header]}>
在正常的 View 上设置 margin
</Text>
<View style={{backgroundColor: '#333333'}}>
<View style={{backgroundColor: '#fefefe', width: 30, height: 30, margin: 30}}/>
</View>
<Text style={[styles.text, styles.header]}>
在文本元素上设置 margin
</Text>
<View style={{backgroundColor: '#333333'}}>
<Text style={[styles.text, {backgroundColor: '#fe0000', margin: 30}]}>
text 元素上设置 margin
</Text>
<Text style={[styles.text, {backgroundColor: '#fe0000', margin: 30}]}>
text 元素上设置 margin
</Text>
</View>

我们知道,对于 inline 元素,设置 margin-left 和 margin-right 有效,top 和 bottom 按理是不会生效的, 但是上图的结果可以看到,实际是生效了的。所以现在给我的感觉是 Text 元素更应该理解为一个不能设置 padding 的 block。

算了不要猜了, 我们看看官方文档怎么说 Text, https://facebook.github.io/react-native/docs/text.html

复制代码
<Text>
<Text>First part and </Text>
<Text>second part</Text>
</Text>
// Text container: all the text flows as if it was one
// |First part |
// |and second |
// |part |
<View>
<Text>First part and </Text>
<Text>second part</Text>
</View>
// View container: each text is its own block
// |First part |
// |and |
// |second part|

也就是如果 Text 元素在 Text 里边,可以考虑为 inline, 如果单独在 View 里边,那就是 Block。

下面会专门研究一下文本相关的布局

文本元素

首先我们得考虑对于 Text 元素我们希望有哪些功能或者想验证哪些功能:

  1. 文字是否能自动换行?
  2. overflow ellipse?
  3. 是否能对部分文字设置样式 ,类似 span 等标签

先看看文字有哪些支持的 style 属性

复制代码
/*==========TEXT================*/
Attributes.style = {
color string
containerBackgroundColor string
fontFamily string
fontSize number
fontStyle enum('normal', 'italic')
fontWeight enum("normal", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')
lineHeight number
textAlign enum("auto", 'left', 'right', 'center')
writingDirection enum("auto", 'ltr', 'rtl')
}

实验 1, 2, 3

复制代码
<Text style={[styles.text, styles.header]}>
文本元素
</Text>
<View style={{backgroundColor: '#333333', padding: 10}}>
<Text style={styles.baseText} numberOfLines={5}>
<Text style={styles.titleText} onPress={this.onPressTitle}>
文本元素{'\n'}
</Text>
<Text>
{'\n'}In this example, the nested title and body text will inherit the fontFamily from styles.baseText, but the title provides its own additional styles. The title and body will stack on top of each other on account of the literal newlines, numberOfLines is Used to truncate the text with an elipsis after computing the text layout, including line wrapping, such that the total number of lines does not exceed this number.
</Text>
</Text>
</View>
styles = {
baseText: {
fontFamily: 'Cochin',
color: 'white'
},
titleText: {
fontSize: 20,
fontWeight: 'bold',
}
}

从结果来看 1,2,3 得到验证。 但是不知道各位有没有发现问题, 为什么底部空出了这么多空间, 没有设置高度啊。 我去除numberOfLines={5} 这行代码,效果如下:

所以实际上, 那段空间是文本撑开的, 但是文本被numberOfLines={5} 截取了,但是剩余的空间还在。 我猜这应该是个 bug。

其实官方文档里边把numberOfLines={5}这句放到的是长文本的 Text 元素上的,也就是子 Text 上的。 实际结果是不生效。 这应该又是一个 bug。

Text 元素的子 Text 元素的具体实现是怎样的, 感觉这货会有很多 bug, 看官文

复制代码
<Text style={{fontWeight: 'bold'}}>
I am bold
<Text style={{color: 'red'}}>
and red
</Text>
</Text>

Behind the scenes, this is going to be converted to a flat
NSAttributedString that contains the following information

复制代码
"I am bold and red"
0-9: bold
9-17: bold, red

好吧, 那对于numberOfLines={5} 放在子 Text 元素上的那种 bug 倒是可以解释了。

Text 的样式继承

实际上 React-native 里边是没有样式继承这种说法的, 但是对于 Text 元素里边的 Text 元素,上面的例子可以看出存在继承。 那既然有继承,问题就来了!

到底是继承的最外层的 Text 的值呢,还是继承父亲 Text 的值呢?

复制代码
<Text style={[styles.text, styles.header]}>
文本样式继承
</Text>
<View style={{backgroundColor: '#333333', padding: 10}}>
<Text style={{color: 'white'}}>
<Text style={{color: 'red'}} onPress={this.onPressTitle}>
文本元素{'\n'}
<Text> 我是 white 还是 red 呢?{'\n'} </Text>
</Text>
<Text> 我应该是 white 的 </Text>
</Text>
</View>

结果可见是直接继承父亲 Text 的。

总结

  1. react 宽度基于pt为单位, 可以通过Dimensions 来获取宽高,PixelRatio 获取密度,如果想使用百分比,可以通过获取屏幕宽度手动计算。
  2. 基于 flex 的布局
  3. view 默认宽度为 100%
  4. 水平居中用alignItems, 垂直居中用justifyContent
  5. 基于 flex 能够实现现有的网格系统需求,且网格能够各种嵌套无 bug
  6. 图片布局
  7. 通过Image.resizeMode来适配图片布局,包括contain, cover, stretch
  8. 默认不设置模式等于 cover 模式
  9. contain 模式自适应宽高,给出高度值即可
  10. cover 铺满容器,但是会做截取
  11. stretch 铺满容器,拉伸
  12. 定位
  13. 定位相对于父元素,父元素不用设置 position 也行
  14. padding 设置在 Text 元素上的时候会存在 bug。所有 padding 变成了 marginBottom
  15. 文本元素
  16. 文字必须放在 Text 元素里边
  17. Text 元素可以相互嵌套,且存在样式继承关系
  18. numberOfLines 需要放在最外层的 Text 元素上,且虽然截取了文字但是还是会占用空间
2015-06-24 00:4838943

评论

发布
暂无评论
发现更多内容

使用APICloud AVM多端框架开发课程表功能

YonBuilder低代码开发平台

前端开发 APP开发 APICloud 多端开发 AVM

用 Golang 重写 rsync(2):方案的选择

百家饭隐私计算平台创业者

c golang

一站式智能运维解决方案,企业系统的隐形守护者

云桌派

阿里云 IT 解决方案 智能运维 客户案例

【高并发】又一个朋友面试栽在了Thread类的stop()方法和interrupt()方法上!

冰河

并发编程 多线程 高并发 异步编程 6月月更

智能自助洗车设备什么牌子好

共享电单车厂家

自助洗车机 智能自助洗车设备 智能共享洗车机

如何删除 git 仓库中的 .idea 文件?

程序员小航

git IDEA

3.0.0 alpha 重磅发布!九大新功能、全新 UI 解锁调度系统新能力

亚马逊云科技 (Amazon Web Services)

UI 系统 新功能 Tech 专栏

数据库:高并发下的数据字段变更

C++后台开发

数据库 高并发 后端开发 Linux服务器开发 C++后台开发

低代码开发是新瓶装老酒吗?

菜根老谭

flask框架自主学习

恒山其若陋兮

6月月更

隐藏在 graph-ocean 背后的星辰大海

NebulaGraph

ORM框架 图数据库 知识图谱 Nebula Graph

无人24小时自助洗车代理怎么做

共享电单车厂家

自助洗车加盟 自助洗车代理

进出口管理系统解决方案

低代码小观

供应链 企业管理系统

ESB基础样例前置资源配置

agileai

数据治理 系统集成 数据集成 企业服务总线 预置样例

flutter系列之:构建Widget的上下文环境BuildContext详解

程序那些事

flutter 程序那些事 6月月更

小游戏开发是进行网游的必需环节,你知道吗?

开源直播系统源码

软件开发 小游戏开发 直播系统 app源码

Data Summit 2022 大会资料分享(共23个)

墨天轮

数据库 AI 数据仓库 数据湖 BI

2022年公共充电站运营端用户体验指数(UEI)发布

易观分析

汽车 新能源

如何挑选合适的共享自助洗车机

共享电单车厂家

自助洗车机 自助洗车机价格

网站FAQ页面有必要做吗?该如何做?

小炮

openGauss“用户故事”正式上线!一键分享实践经验,限量版礼物等你拿

opengauss 开源社区 用户故事

高分神器,百万考生都在用的高效记忆方法,助你过目不忘,决胜高考!

图灵教育

高考 脑科学

半监督式机器学习提升银行业对于团伙欺诈交易行为的风控能力

易观分析

银行

哈希彩hash竞猜系统开发逻辑游戏玩法(源代码)

开发微hkkf5566

没有行业经验能否加盟自助洗车

共享电单车厂家

自助洗车加盟

想做自助洗车不知道怎么加盟?

共享电单车厂家

自助洗车加盟

OpenHarmony 3.1 Release版本关键特性解析——HDI硬件设备接口介绍

OpenHarmony开发者

OpenHarmony 3.1 Release

中建普联与数商云达成战略合作协议,共同打造建设行业数智化发展新高地

数商云

产业互联网 数字化转型 企业数字化

为什么不能使用 datax 直接读写 hive acid 事务表?

明哥的IT随笔

百问百答第41期:应用性能探针监测原理-Java探针

博睿数据

智能运维 博睿数据 性能检测

TiFlash 源码阅读(三)TiFlash DeltaTree 存储引擎设计及实现分析 - Part 1

PingCAP

React Native探索(二):布局篇_移动_陈学家_InfoQ精选文章