RN布局与样式
布局
一款好的App离不开漂亮的布局,RN中的布局方式采⽤的是FlexBox(弹性布局) 。
经典资料参考:阮一峰flex 布局语法篇:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
FlexBox提供了在不同尺⼨设备上都能保持一致的布局⽅式 。在移动端,在这里不必担心兼容问题。
但是RN的flex布局和真正的css还是有所差别:
- flexDirection:RN中默认是flexDirection:’column’,Web Css中默认是 flex-direction:’row’,也就是说RN的flex默认就是打竖的。
- alignItems:RN中默认: ‘stretch’,在Web Css中默认 flex-start’,也就是说RN的flex是强制等高的。
- RN的flex属性,只能接收一个值
- 不支持的属性: align-content flex-basis order flex-flow flex-grow flex-shrink (平时也用得少)
样式
在移动端开发中,是没有像素概念的。所有量规无单位,表示的是是1个逻辑像素。
代码语言:javascript复制<View style={{width:100,height:100,margin:10,backgroundColor:'gray'}}>
<Text style={{fontSize:16,margin:20}}>尺⼨寸</Text>
</View>
上述代码,运⾏在Android上时,View的⻓宽被解释成:100dp 100dp,字体被解释成16sp,运⾏于 ios上时尺⼨单位被解释成pt,这些单位确保了布局在任何不同DPI的手机屏幕上,显示效果一致。
关于更详细的换算关系,查阅:http://www.woshipm.com/pmd/176328.html
写样式除了可以用传统react的css in js方式,也可以这么写:
代码语言:javascript复制<text style={[styles.aaa,{color:'red'}]}></text>
所有文本的样式应该直接加在text
上面,如果你在view
里面写,就不会生效了。
{/* 错误的实例:不生效 */}
<view style={[styles.aaa,{color:'red'}]}></view>
组件
react native的魅力在于能够使用系统原生的组件。他们和html标签相似,又有不少区别。
如果写过微信小程序,或许理解起来会比较快。因为前者”借用了”这些组件概念。
简单认知的话,组件和UI框架差不多,用什么引什么。以下对某些重要组件进行介绍。
view:万能容器
视图布局容器,可以理解为原生开发中的万能容器。可嵌套多层,支持flex。
一个组件通常是返回一个view包裹的,如果你想返回两个,可以使用[<View>...</View>,<View>...</View>]
的形式返回多个兄弟组件。
SafeAreaView:安全区
SafeAreaView 的目的是在一个“安全”的可视区域内渲染内容。具体来说就是因为目前有 iPhone X 这样的带有“刘海”的全面屏设备,所以需要避免内容渲染到不可⻅见的“刘海”范围内。本组件目前仅⽀持 iOS 设备以及 iOS 11 或更高版本。
SafeAreaView 会自动根据系统的各种导航栏、工具栏等预留出空间来渲染内部内容。更重要的 是,它还会考虑到设备屏幕的局限,比如屏幕四周的圆⻆角或是顶部中间不可显示的“非安全”区域。
代码语言:javascript复制<SafeAreaView style={{backgroundColor:'red'}}></SafeAreaView>
webview:加载网页容器(即将被移除)
创建一个原生的webview,用于加载网页.我们可结合safeAreaView使用:
代码语言:javascript复制 <SafeAreaView style={{flex:1}}>
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
</SafeAreaView>
在官方最新版本需要安装react-native-webview
需要明确的认知是:webview是有可能存在跨域问题的。
Text:文本容器
主要用于显示文本,具有响应之特性(表现为触摸时是否支持高亮)。同时支持多层嵌套,因此样式可继承(内部继承外部)。但是,不同于web css,字体样式(font color等)只有在text组件上才能起效——所以字体样式的实现只能依赖于text组件。
在Text内部的元素不再使⽤flexbox布局,而是采⽤用文本布局。这意味着内部的元素不再是】一个个矩 形,而可能会在行末进⾏折叠。
代码语言:javascript复制 <Text
ellipsizeMode={"tail"} //这个属性通常和下⾯面的 numberOfLines 属性配合使⽤用,⽂文本超出 numberOfLines设定的⾏行行数时,截取⽅方式:head- 从⽂文本内容头部截取显示省略略号。例例如: "...efg",middle - 在⽂文本内容中间截取显示省略略号。例如: "ab...yz",tail - 从⽂文本内容尾 部截取显示省略略号。例例如: "abcd...",clip - 不不显示省略略号,直接从尾部截断。
numberOfLines={1} //配合ellipsizeMode设置⾏行行数
onPress={...} //点击事件 selectable={true}//决定⽤用户是否可以⻓长按选择⽂文本,以便便复制和粘贴。
>123
</Text>
Image:图片容器
类似img元素。但支持更多但来源,比如网络图片,本机磁盘图片,照相机图片等。
下⾯的例⼦分别演示了如何显示从本地缓存、网络乃至base64拉取图片。
代码语言:javascript复制{/* 显示本地图 */}
<Image
source={require('./img/favicon.png')}
/>
{/* 显示网络图 */}
<Image
style={{width: 50, height: 50}}
//网络和 base64 数据的图⽚需要⼿动指定尺⼨
source={{uri: 'https://facebook.github.io/react-native/docs/assets/favicon.png'}}
/>
{/* 显示base64图 */}
<Image
style={{width: 66, height: 58}} //⽹网络和 base64 数据的图⽚片需要⼿手动指定尺⼨寸
source={{uri:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB 2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw P/eMrC5UTVAAAAABJRU5ErkJggg=='}}
/>
自从ios9.0之后,官方就一直推荐使用https协议的网络图片。
ImageBackground 背景图
用法和Image差不多:
代码语言:javascript复制{/* 显示网络图 */}
<ImageBackground
style={{width: 50%, height: 50%}}
//网络和 base64 数据的图⽚需要⼿动指定尺⼨
source={{uri: 'https://facebook.github.io/react-native/docs/assets/favicon.png'}}>
<Text>...</Text>
</ImageBackground>
Button:按钮
一个简单的跨平台的按钮组件。可以进行一些简单的定制。如图,前者为安卓,后者为ios。
代码语言:javascript复制<Button
onPress={onPressLearnMore} //⽤户点击此按钮时所调用的处理理函数
title="Learn More" //按钮内显示的⽂文本
color="#841584" //文本的颜⾊(iOS),或是按钮的背景⾊(Android)
disabled={false} //按钮是否可以点击
accessibilityLabel="Learn more about this purple button"
//用于给残障人⼠显示的文本(比如读屏应⽤可能会读取这一内容)
/>
ActivityIndicator loading的小菊花
显示一个loading提示符安卓设备时一个谷歌式半圆环,在ios设备上则显示一朵小菊花。
代码语言:javascript复制 <ActivityIndicator
size="large" //指示器器的大⼩,默认为'small'[enum('small', 'large'), number]。⽬前只能在 Android 上设定具体的数值
animating={true} //是否要显示指示器动画,默认为 true 表示显示,false 则隐藏。
hidesWhenStopped={false} //在animating为 false 的时候,是否要隐藏指示器(默认为 true)。如果animating和hidesWhenStopped都为 false,则显示⼀一个静⽌止的指示器。
color="#0000ff" />
ListView:列表
这个组件的性能比较差,尤其是当有大量的数据需要展示的时候,ListView对内存的占⽤用较多,常出现丢帧卡顿现象。
ListView底层实现,渲染组件Item是全量渲染,而且没有复用机制,当渲染较⼤数据量时,会不可避免地卡顿。
第⼀次打开与切换Tab时会出现卡顿或白屏的情况,比如ListView中有100个Item,只能等这 100条Item都渲染完成,ListView中的内容才会展示滑动列表时会出现卡顿。
未来有很⼤大可能性会被移除 。
VirtualizedList: 虚拟列表
替代ListView的主要解决方案就是VirtualizedList。RN0.43版本中引⼊了了FlatList,SectionList和VirtualizedList,其中VirtualizedList是FlatList和SectionList的底层实现。
FlatList 和 SectionList 的底层实现:VirtualizedList通过维护一个有限的渲染窗⼝(其中包含可⻅的元素),并将渲染窗⼝之外的元素全部用合适的定⻓空⽩空间代替的⽅式,极⼤的改善了内存使⽤,提⾼了大量数据情况下的渲染性能。这个渲染窗⼝能响应滚动行为,元素离可视区越远优先级越低,越近优先级越高,当用户滑动速度过快时,会出现短暂空⽩的情况。
代码语言:javascript复制<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) => <Text>{item.key}</Text>}
/>
缺点:
(1)为了优化内存占⽤同时保持滑动的流畅,列表内容会在屏幕外异步绘制。这意味着如果用户滑动的速度超过渲染的速度,则会先看到空白的内容。
(2)不支持分组列列表
扯了那么多理论,如果列表写不了想说自己懂rn是很扯的。是时候开始写一个了。
需求:列表的下拉刷新和上划动加载
看今日头条等新闻列表类app时,都需要用到。
代码语言:javascript复制import React,{Component} from 'react';
import {View,Text,StyleSheet,Button,FlatList,RefreshControl} from 'react-native';
const listData=Array(20).fill(1).map((x,i)=>{
return {
key:i,
value:`列表项${i 1}`
}
});
export default class HotPage extends Component{
static navigationOptions=({navigation})=>{
return {
headerTitle:navigation.getParam('title')
}
}
constructor(props){
super(props);
this.state={
listData,
isLoading:false
}
}
loadData(refresh){
if(refresh){
this.setState({
isLoading:refresh
});
}
setTimeout(()=>{
let _listData=[];
if(refresh){
for(let i=this.state.listData.length-1;i>=0;i--){
_listData.push(this.state.listData[i])
}
}else{
_listData=this.state.listData.concat(listData)
}
this.setState({
listData:_listData,
isLoading:false
})
},2000)
}
render(){
return (
<View style={styles.container}>
<FlatList
data={this.state.listData}
renderItem={({item}) =>
<View style={{
justifyContent:'center',
alignItems:'center',
flex:1,
height:60,
backgroundColor:'#ccc'
}}>
<Text>{item.value}</Text>
</View>
}
// 分割线:不会出现在第一行之前,也不会出现在第一行之后
ItemSeparatorComponent={()=>{
return <View style={{height:2,backgroundColor:'#eee'}}/>
}}
// 列表为空时渲染组件
ListEmptyComponent={()=>{
return <Text style={{textAlign:'center'}}>空空如也</Text>
}}
// 顶部组件
// ListHeaderComponent={()=>{
// }}
// 尾部组件
ListFooterComponent={()=>{
return <Text>我也是有底线的</Text>
}}
// 刷新相关:
// 如果设置了此选项,则会在列表头部增加一个标准的RefreshControl控件,
// 同时也需要正确设置refreshing属性
refreshControl={
<RefreshControl
title='loading'
colors={['red']}
// 如果设置该属性为true,列表将出现一个正在加载的符号
refreshing={this.state.isLoading}
onRefresh={()=>{
this.loadData(true)
}}
tintColor={'orange'}
/>
}
Threshold='0.4'
// 当列表滚动到地步距离不足Threshold时调用
onEndReached={()=>{
this.loadData();
}}
/>
</View>
)
}
}
const styles=StyleSheet.create({
container:{
flex:1,
width:'100%',
backgroundColor:'#f5f5f5'
},
text:{
fontSize:26,
marginBottom:20
}
})
其它组件(Switch/Modal)
可自行查阅api。