美文网首页
React Native填坑之旅--布局篇

React Native填坑之旅--布局篇

作者: uncle_charlie | 来源:发表于2016-10-21 00:24 被阅读121次

    代码在这里:
    https://github.com/future-challenger/petshop/tree/master/client/petshop/src/controller

    回头看看RN的填坑之旅系列,发现一路写下来都是我在开发中遇到的问题如何解决的。兴之所至,不问顺序。于是出现一个问题,填坑系列和学习知识的顺序不是很一致。比如今天要说的布局问题。其实在一个app开发之前,就应该有所了解。否则的话每次看到的<View style={{flex: 1}} />代表的是什么呢?上来就是一个疑问。

    在这个系列进行到一定程度的时候,我会花时间重新整理全系列的文章,让各位可以按照知识本来学习的顺序循序渐进的学习React Native的知识,避免在开发中遇到不应该遇到的问题。

    正文开始。本文主要是介绍基础知识,但是不适用基础的代码。所以,如果你在看的时候有什么问题弄不清楚的话,随时查阅官网。那么问题是什么?三个View放在一个父View里显示出来会是设么样子的?这五个子View每次只要显示一个的话应该如何处理?

    首先我们先来一点开胃小菜:

    NavigationBar的布局

    默认的情况下NavigationBar的左侧回退按钮看起来是这样的:

    NavigationBar

    图中可见,按钮紧贴上沿。这个时候的布局是这样的:

        <TouchableHighlight
            style={{backgroundColor: 'red', width: 50}}
            onPress={() => {
                if (index > 0) {
                    navigator.pop();
                }
            }}>
            <Text style={{
                marginLeft: 10,
                backgroundColor: 'yellow'
            }}>Back</Text>
        </TouchableHighlight>
    

    如何让NavigationBar的按钮竖直居中呢?不要想在TouchableHighlight上添加的样式可以起作用。只有在外层再包裹一层View才可以。并给外层的View设置样式,让其中的内容竖直居中才可以。

        <View style={{flex:1, justifyContent: 'center'}}>
            <TouchableHighlight
                style={{backgroundColor: 'red', width: 50}}
                onPress={() => {
                    if (index > 0) {
                        navigator.pop();
                    }
                }}>
                <Text style={{
                    marginLeft: 10,
                    backgroundColor: 'yellow'
                }}>Back</Text>
            </TouchableHighlight>
        </View>
    

    开始填坑

    下面要实现一个效果。我们已经有三个横向排列的,等宽度的View。对应的还有三个按钮,每一个按钮对应一个View。但是,不要三个View都显示出来。每次只显示一个。点一个按钮就显示出对应的View来。

    Flex的方向

    先来看看制造问题的代码重现:

      <View style={{flex: 1, marginTop: 64}}>
        <View style={{flex: 1, backgroundColor: 'red'}}></View>
        <View style={{flex: 1, backgroundColor: 'orange'}}></View>
        <View style={{flex: 1, backgroundColor: 'yellow'}}></View>
      </View>
    

    看起来是这样的:

    column layout

    默认的,在一个View里的子View都是竖直依次排列的。

    React Native使用Flexbox来实现布局的。Flexbox两个方向,一个是column,一个是row

    • column默认的flexbox方向,是竖直的,从上到下的方向。如上例的图片中截面里红、橙、黄三个颜色的view排列的方向。
    • row是横向的,从左到右的方向。

    我们来看看flexbox在row方向的样子:

      <View style={{flex: 1, flexDirection: 'row', marginTop: 64}}>
        <View style={{flex: 1, backgroundColor: 'red'}}></View>
        <View style={{flex: 1, backgroundColor: 'orange'}}></View>
        <View style={{flex: 1, backgroundColor: 'yellow'}}></View>
      </View>
    
    layout row

    添加了按钮以后的界面看起来是这样的:


    with buttons

    JustifyContent & AlignItems

    在flow已经决定了子view排列的方向的时候。还需要进一步的调整子view的时候就会用到justifyContentalignItems

    在和flow指定的方向同一方向上调整的时候使用justifyContent。flow指定的方向为主方向。要在次方向上指定子view如何排列就是用alignItems。如果flexDirection指定的是column(竖直方向),那么主方向就是竖直的,次方向就是水平的。

    Flex的值

    上面介绍了flex的方向,这里来说说flex的值。

    在兄弟姐妹关系的View中,如果他们的flex都是1,那么他们评分父view的空间。如果,有一个flex的值是2,那么就是说这个子View的宽(高)是其他的兄弟姐妹的2倍。

    在flex为0的时候,View的宽高就完全需要靠自己了。也就是这个view的宽和高需要设置具体的数值。系统不会替你计算。

    如果flow为-1,那么View的宽度和高度,由width和height决定。*在空间不够的情况下,view的宽度和高度会缩小到minWidthminHeight

    看看我们要实现的效果和代码

    效果:

    效果

    实现代码:

    export default class FlexDemo extends React.Component {
      _onPress: (buttonIndex: number) => void;
    
      constructor(props) {
        super(props);
        this.state = {
          view1Style: {flex: 1, width: 0}, // If flex is 1, width does not work.
          view2Style: {flex: 0, width: 0},
          view3Style: {flex: 0, width: 0}
        };
    
        this._onPress = this._onPress.bind(this);
      }
    
      _onPress(buttonIndex) {
        switch(buttonIndex) {
          case 0:
            this.setState({
              view1Style: {flex: 1, width: 0}, // If flex is 1, width does not work.
              view2Style: {flex: 0, width: 0},
              view3Style: {flex: 0, width: 0}
            });
            break;
          case 1:
            this.setState({
              view1Style: {flex: 0, width: 0}, // If flex is 1, width does not work.
              view2Style: {flex: 1, width: 0},
              view3Style: {flex: 0, width: 0}
            });
            break;
          case 2:
            this.setState({
              view1Style: {flex: 0, width: 0}, // If flex is 1, width does not work.
              view2Style: {flex: 0, width: 0},
              view3Style: {flex: 1, width: 0}
            });
            break;
          default:
            this.setState({
              view1Style: {flex: 1, width: 0}, // If flex is 1, width does not work.
              view2Style: {flex: 0, width: 0},
              view3Style: {flex: 0, width: 0}
            });
            break;
        }
      }
    
      render() {
        return (
      <View style={{flex: 1, flexDirection: 'row', marginTop: 64}}>
        <View key="view1" style={[this.state.view1Style, {backgroundColor: 'red'}]}></View>
        <View key="view2" style={[this.state.view2Style, {backgroundColor: 'orange'}]}></View>
        <View key="view3" style={[this.state.view3Style, {backgroundColor: 'yellow'}]}></View>
    
        <View style={{
          flex: 1,
          flexDirection: 'row',
          justifyContent: 'space-between',
          alignItems: 'center',
          position: 'absolute',
          height: 50,
          left:0,
          right: 0,
          bottom: 0,
          backgroundColor: 'black',
          opacity: 0.6
        }}>
          <View style={{flex: 1, justifyContent:'center'}} key='b1'>
            <TouchableOpacity
              style={styles.button}
              onPress={()=>this._onPress(0)}>
              <Text style={styles.buttonText}>1</Text>
            </TouchableOpacity>
          </View>
          <View style={{flex: 1, justifyContent: 'center'}} key='b2'>
            <TouchableOpacity
              style={styles.button}
              onPress={()=>this._onPress(1)}>
              <Text style={styles.buttonText}>2</Text>
            </TouchableOpacity>
          </View>
          <View style={{flex: 1, justifyContent: 'center'}} key='b3'>
            <TouchableOpacity
              style={styles.button}
              onPress={()=>this._onPress(2)}>
              <Text style={styles.buttonText}>3</Text>
            </TouchableOpacity>
          </View>
        </View>
      </View>
        );
      }
    };
    

    实现原理:
    如何去改变一个组件的外观布局?自然少不了setState方法。一开始,我们就把这几个子view的布局都放在state里。

    在下面的三个按钮的点击事件中,设置不同的state,那么赤、橙和黄三个view就会发生变化。

    前面提高flex的值为0的时候widthheight起作用,flex不为0的时候不起作用。这就是核心算法之所在了。在一开始让红色的view的flex为1。其他的view的flex为0,并且宽度也为0

    之后每次点击了一个按钮的时候,对应的view的布局设置为flex等于1,其他的view的flex为0。这样就实现了上面的效果。

    小小的练习

    最后留一个练习题,上面的效果对于一个简单的View来说也可以使用conditional render来搞定。那么就请你做一个这样的练习。

    最后

    上面的讲解都是我遇到的问题的讲解。包含了一些flexbox布局的基础,但是更难理解一下。React Native的布局包括的东西还有很多。后续会在本文或者系列的其他的文章中讲解。

    相关文章

      网友评论

          本文标题:React Native填坑之旅--布局篇

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