您好,登錄后才能下訂單哦!
一、長度的單位
在開始任何布局之前,讓我們來首先需要知道,在寫React Native組件樣式時,長度的不帶單位的,它表示“與設(shè)備像素密度無關(guān)的邏輯像素點”。
這個怎么理解呢?
我們知道,屏幕上一個發(fā)光的最小點,對應(yīng)著一個pixel(像素)點。
假設(shè)下面三個矩形,代表三個屏幕大小一樣的設(shè)備,但是,它們擁有的分辨率(resolution)不同:
圖1.相同尺寸的設(shè)備 不同的分辨率
圖上的每一個小格子,其實就代表了一個像素(pixel)??梢钥吹?,一個像素點的大小,在這個三個物理尺寸一樣但擁有不同分辨率的設(shè)備上,是不一樣的。
如果我們以像素為單位來設(shè)置一個界面元素的大小,比如說2px的高度,那么這2px的長度上面的設(shè)備中就會是下面這個樣子:
圖2.不同分辨率下的2px實際高度
它們真實顯示出的長度是不一樣的。
我們想要一種長度單位,在同樣物理尺寸大小的屏幕上(不論分辨率誰高誰低,只要物理尺寸大小一樣即可),1個單位的長度所代表的物理尺寸是一樣的。這種單位就應(yīng)該是獨立于分辨率的,把它起一個名字叫做 density-independent pixels,簡稱dp。這其實就是Android系統(tǒng)中所使用的長度單位。
舉例來說,2dp寬,2dp高的內(nèi)容,在不同分辨率但屏幕尺寸一樣的設(shè)備上所顯示出的物理大小是一樣的。(一個題外話:有些Android開發(fā)者建議所有可點擊的按鈕,寬高都不應(yīng)該少于48dp。)
圖3. 2dp * 2dp大小的內(nèi)容 在同樣尺寸的屏幕中所占據(jù)的物理大小一致
Android中字體大小使用另外一個單位,叫做scale independent pixels,簡稱sp。這個單位和dp很類似,不過它通常是用在對字體大小的設(shè)置中。通過它設(shè)置的字體,可以根據(jù)系統(tǒng)字體大小的變化而變化。
pixel與dp存在一個公式:px = dp * (dpi/160)。
dpi表示dot per inch,是每英寸上的像素點,它也有個自己的計算公式,具體這里就不展開了。只需要知道我們之所以要使用一個獨立于設(shè)備分辨率的單位,主要是為了讓應(yīng)用在不同分辨率的設(shè)備中,看起來一致。
在RN中,同樣也擁有一個類似于dp的長度單位。如果我們想知道自己的屏幕以這種長度的計量下是多少單位,可以通過引入react-native包中的Dimensions拿到,同時還可以查看本機(jī)的像素比例是多少。
import {
Text,
View,
Dimensions,
PixelRatio
} from 'react-native';
const { height, width } = Dimensions.get('window');
const pxRatio = PixelRatio.get();
<View style={styles.container}>
<Text style={styles.welcome}>
{`width: ${width}, height: ${height}`}
</Text>
<Text style={styles.welcome}>
{`pixel radio: ${pxRatio}`}
</Text>
</View>
顯示如下:
圖4. 當(dāng)前手機(jī)的屏幕信息
它反映出,當(dāng)前手機(jī)屏幕的寬度占據(jù)360個單位,高度占據(jù)640個單位。像素比例是3,實際上這就是一個 1080 * 1920 像素的手機(jī)。其中1080 = width * pixelRadio, 1920 = height * pixelRatio
二、Flexbox布局
Flexbox布局,也就是彈性盒模型布局。也許有Android開發(fā)經(jīng)驗的朋友還對LinearLayout,RelativeLayout,F(xiàn)rameLayout等布局方法記憶猶新,但是對于更了解CSS的Web開發(fā)者而言,使用flexbox布局肯定會讓他感受到更加順手的開發(fā)體驗。
RN中的flexbox布局,其實源于CSS中的flexbox(彈性盒子)布局規(guī)范。其實它在CSS中還處于Last Call Working Draft(最終征求意見稿)階段,但是主流瀏覽器對它都有了良好的支持。在RN中,幾乎完全借鑒了其中的布局語義,同時更沒有瀏覽器兼容的煩惱,用起來是很方便的。RN中只是把CSS的屬性用camelCase寫法代替連字符寫法。后面還還會看到,默認(rèn)的flex方向也不同。
理解彈性盒模型布局,首先要知道四個最基本的概念:Flex Container(容器),F(xiàn)lex Item(項),F(xiàn)lex Direction(方向)和Axis(軸)。
1.Flex Container
就是包裹內(nèi)容的容器,需要把它的display設(shè)置為‘flex’(或者'inline-flex')。
以下6個屬性設(shè)置在容器上。
alignItems 指定item在側(cè)軸上的對齊方式
alignContent 指定item在多條軸上的對齊方式
flexDirection 指定主軸方向
flexWrap 指定item在主軸方向如何換行
flexFlow flexDirection屬性和flexWrap屬性的簡寫形式
justifyContent 指定item在主軸上的分布方式
2.Flex Item
容器做直接包裹的元素。所謂彈性盒布局,通常想要布局的東西就是它們。
以下6個屬性設(shè)置在項目上。
alignSelf 每個item可以單獨設(shè)置對齊方式 覆蓋Flex Container給設(shè)置的alignItems
order 指定item排列順序 數(shù)字越小越靠前
flexGrow 指定item的拉伸比例
flexShrink 指定item的壓縮比例
flexBasis 指定item在分配多余空間之前,占主軸的大小
flex flexGrow flexShrink flexBasis的簡寫
3.Flex Direction and Axis
在彈性盒子中,項目默認(rèn)沿著main axis(主軸)排列,和主軸垂直的軸叫做cross axis,叫做側(cè)軸,或者交叉軸。
在盒子中,排列項目又四個方向:水平的正反兩個,垂直的正反兩個。
結(jié)構(gòu)代碼:
<View>
<View style={styles.row}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
<View style={styles.rowReverse}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
<View style={styles.column}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
<View style={styles.columnReverse}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
</View>
樣式代碼:
row: {
backgroundColor: '#ffe289',
flexDirection: 'row'
},
rowReverse: {
flexDirection: 'row-reverse'
},
column: {
backgroundColor: '#ffe289',
flexDirection: 'column'
},
columnReverse: {
flexDirection: 'column-reverse'
},
圖5. flexDirection
由于網(wǎng)上關(guān)于flex布局講解的資源挺豐富的,讀者可以參考最后給出的連接,或者自行上網(wǎng)搜索,CSS中的和RN是相通的。
這里主要分享個人在學(xué)習(xí)過程中,覺得容易引起混淆的兩個小點。
首先,justify-content和align-content這兩個屬性,可能比較容易搞錯它們作用的方向。
其中,justify-content是設(shè)置items沿著主軸上是如何分布的。align-content是設(shè)置items沿著側(cè)軸如何對齊的。
還是拿之前的例子,默認(rèn)情況下,flex的方向是column(這個與移動端與web頁面不同,在web頁面用CSS設(shè)置flex布局,默認(rèn)的fiex-direction是row,即水平從左往右)。
在移動端,主軸默認(rèn)是垂直方向,從上往下。讓我們把它的高度設(shè)置高一點,放3個item在里面:
結(jié)構(gòu)代碼:
<View>
<View style={styles.defaultFlex}>
<Text style={styles.item}>1</Text>
<Text style={styles.item}>2</Text>
<Text style={styles.item}>3</Text>
</View>
</View>
樣式代碼:
defaultFlex: {
height: 300,
backgroundColor: '#ffe289',
display: 'flex'
}
圖6. 默認(rèn)的flex
justify-content設(shè)置items在主軸方向的如何分布,比如,如果我們加上justifyContent: 'space-between'
defaultFlex: {
height: 300,
backgroundColor: '#ffe289',
display: 'flex',
justifyContent: 'space-between'
}
items就沿主軸分開了。
圖7. justifyContent: 'space-between'
如果我們設(shè)置alignItems: 'center',項目就沿側(cè)軸(這里就是水平軸)居中了。注意這兩個屬性是可以同時起作用的。
圖8. justifyContent: 'space-between' 以及 alignItems: 'center'
然后,值得指出的是,flex這個屬性,其實是flexGrow, flexShrink, flexBasis(對應(yīng)的CSS屬性flex-grow, flex-shrink和flex-basis)三個屬性的結(jié)合。
我們通常在移動端看到的flex:1這個設(shè)置,其實是對flex-grow的設(shè)置。后者的默認(rèn)值為0。使用把flex-grow設(shè)置為正整數(shù)的方法,可以讓item按比例分布,或者在其他item為固定大小時撐滿剩余的盒子空間,就仿佛具有彈性一樣。
結(jié)構(gòu)代碼:
<View style={styles.container}>
<View style={styles.flex1}></View>
<View style={styles.flex2}></View>
<View style={styles.flex3}></View>
</View>
樣式代碼:
container: {
flex: 1
},
flex1: {
// height: 99,
flexGrow: 1,
backgroundColor: 'orange',
},
flex2: {
flexGrow: 2,
backgroundColor: 'lightblue',
},
flex3: {
flexGrow: 3,
backgroundColor: 'green',
},
圖9. 按比例分布
需要注意的是,如果父容器的尺寸為零(即沒有設(shè)置寬高,或者沒有設(shè)定flex),即使子組件如果使用了flex,也是無法顯示的。
所以這里最外層的使用了flex布局的,flex:1,表示讓它占據(jù)了垂直的整個空間。
三、小小實戰(zhàn)演練
讓我們來簡單使用flex布局,對之前的例子稍加調(diào)整,實現(xiàn)一個頭部,底部固定高度,中間內(nèi)容占滿剩下的屏幕的布局:
第一步,調(diào)整結(jié)構(gòu):
<View style={styles.container}>
<View style={styles.header}></View>
<View style={styles.body}></View>
<View style={styles.footer}></View>
</View>
調(diào)整樣式:
container: {
flex: 1
},
header: {
height: 60,
backgroundColor: 'orange',
},
body: {
flexGrow: 1,
backgroundColor: 'lightblue',
},
footer: {
height: 60,
backgroundColor: 'green',
}
圖10. 有頭尾的布局
第二部,給header添加標(biāo)題。
我們讓頭部的分成3部分,左邊模擬一個返回按鈕,中間顯示標(biāo)題文字,右邊模擬一把小叉:
<View style={styles.header}>
<Text style={styles.back}>返回</Text>
<Text style={styles.title}>這是一個標(biāo)題</Text>
<Text style={styles.exit}>×</Text>
</View>
需要把header的flexDirection設(shè)置為水平方向:
header: {
height: 60,
backgroundColor: 'orange',
flexDirection: 'row',
alignItems: 'center'
},
back: {
color: 'white',
marginLeft: 15
},
title: {
flexGrow: 1,
fontSize: 20,
color: 'white',
textAlign: 'center'
},
exit: {
marginRight: 20,
fontSize: 20,
color: 'white'
}
圖11. header有了標(biāo)題
第三步,我們可以把footer三等分,模擬成菜單的樣子:
<View style={styles.footer}>
<Text style={styles.firstMenu}>添加</Text>
<Text style={styles.menu}>刪除</Text>
<Text style={styles.menu}>修改</Text>
</View>
添加樣式:
footer: {
height: 60,
backgroundColor: 'green',
flexDirection: 'row',
alignItems: 'center'
},
menu: {
flexGrow: 1,
textAlign: 'center',
borderColor: 'white',
borderLeftWidth: 1,
color: 'white'
},
firstMenu: {
flexGrow: 1,
textAlign: 'center',
color: 'white'
},
圖12. footer三等分 模擬菜單
最后,讓我們在body里也填入幾個帶按鈕的輸入框。
引入TextInput和Button組件,然后把它們分三組放入body中,
<View style={styles.body}>
<View style={styles.inputRow}>
<TextInput style={styles.textInput}></TextInput>
<Button style={styles.btn} onPress={() => {}} title="確定"></Button>
</View>
<View style={styles.inputRow}>
<TextInput style={styles.textInput}></TextInput>
<Button style={styles.btn} onPress={() => {}} title="非常確定"></Button>
</View>
<View style={styles.inputRow}>
<TextInput style={styles.textInput}></TextInput>
<Button style={styles.btn} onPress={() => {}} title="確定一定以及肯定"></Button>
</View>
</View>
添加樣式:
body: {
flexGrow: 1,
backgroundColor: 'lightblue',
},
inputRow: {
flexDirection: 'row',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
},
textInput: {
flex: 1
},
btn: {
minWidth: 60
}
flex布局的一個常用實踐是,部分內(nèi)容固定寬高,讓剩下的內(nèi)容自適應(yīng)。
像上面這樣,我們給Button有一個最小寬度,且TextInput的flexGrow為1,這樣的做法可以實現(xiàn),TextInput總是占滿剩下的寬度,且可伸縮。
看了上面的例子,是否覺得在React Native中使用Flexbox布局也挺簡單呢?
希望這是個不錯的開始。
參考
優(yōu)達(dá)學(xué)城的安卓入門課程 https://cla***oom.udacity.com/courses/ud837/lessons/4027328704/concepts/42075886740923
闕喜濤 React Native跨平臺移動應(yīng)用開發(fā)(第2版)
阮一峰 http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
React Native官網(wǎng) https://facebook.github.io/react-native/
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。