React Native填坑之旅--布局篇


代碼在這里:
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

圖中可見,按鈕緊貼上沿。這個時候的布局是這樣的:

	<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的布局包括的東西還有很多。后續會在本文或者系列的其他的文章中講解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM