react-native布局与组件

2019-09-17 16:58:01 浏览数 (1)

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里面写,就不会生效了。

代码语言:javascript复制
{/* 错误的实例:不生效 */}
<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。

0 人点赞