好久不見的 style。
自我檢測
- 我知道怎麼在 APP 中加入 Icon
- 我知道怎麼在 APP 中使用客製化 Font
- 我知道怎麼利用 module 把 Stylesheet 抽出去變成全域樣式
- 我知道什麼是
View
、Text
、Button
等等基本的 RN 元件 - 我知道怎麼用 RN 中的
StyleSheet
、Flex
和Position
來做排版 - 我知道 RN 裡面不會寫 px 值,而是要透過 scale factor 來計算結果
- 我知道怎麼透過
AppLoading
來處理第一次載入時的處理
block / inline
在 RN 裡面沒有所謂的 block / inline,所以你可以把所有東西都視為 block。
不過更精確一點來說是只有「Flex」,因為每一個 Component 預設就是 Flex-box,所以你才會看到像這樣的寫法:
1 | export default function App () { |
Styles
想要在 RN 做樣式設定的話,可以用 style
這個 props 傳入「物件」來設定樣式:
1 | import { StyleSheet, Text, View } from 'react-native'; |
這邊透過 StyleSheet.create()
的用意是「如果用了不對的 key / value
時」,會直接噴 Error 並停止程式。
但其實只傳入一般的純物件也是 OK 的,像這樣:
1 | import { StyleSheet, Text, View } from 'react-native'; |
這樣有什麼差嗎?
有哦!在只傳入物件的情況下,如果我寫了不對的 key / value 是不會噴錯誤的。但如果是透過 StyleSheet.create()
來建立的物件,只要我有地方寫錯他就一定會編譯失敗並顯示錯誤。
所以一般會建議用 StyleSheet.create()
的方式來撰寫,但如果你真的很懶的話用純物件的方式也不是不行啦。
RN 裡沒有繼承的概念
在經過 CSS 多年的摧殘下,如果我想讓文字粗體,可能會下意識這樣寫:
1 | export default function App() { |
但這樣是沒用的,我一定要把這個 fontWeight
寫在 <Text>
身上才有用。
雖然有個例外情形,就是 <Text>
裡面再放一個 <Text>
,像這樣:
1 | export default function App() { |
這樣的話 Child Text
就也會吃到粗體的效果。
但這只是例外情形,你要知道大多數情形下還是不會有「繼承」的觀念的。
現在 <View>
就等於 flex-box,<Text>
就等於 flex-item,總之要知道這個在 RN 裡面最基本的運作模式。
關於尺寸的單位
因為要考慮「Scale Factor」的關係,所以實際的寬可能跟你想得不一樣。
參考這張圖:
一個 iPhone4 的像素是 320 x 480
,但 Scale Factor 是「2x」,所以實際的解析度是 640 x 960(乘以二)。
所以我如果把藍色方塊的寬度設為 150
,實際在手機上就會是 300px
。
總之這邊是要讓你搞清楚妳設定的「值」代表什麼?
- 如果 Scale factor 為 2,則每個單位就代表 2px
- 如果 Scale factor 為 3,則每個單位就代表 3px
知道這些就好了,不用特別去背這些規格。如果真的碰到單位的問題,可以試著用 %
的方式來設值。
Flex
在 RN 裡面主要會用 Flex 的方式來排版(也是預設的排版方式),主要概念都跟 CSS 裡的 Flex box 差不多,所以這邊只會把我覺得幾個比較特別或需要瞭解的觀念給記錄下來。
複習幾個 flex 相關屬性
flexBasis
設定相對於「主軸」的寬度,會覆蓋掉 width 的設定flexGrou
根據「主軸」的剩餘空間來分配 ??? 到這個 itemflexShrink
根據「主軸」的溢出空間來壓縮 ??? 到這個 itemjustifyContent
設定 item 的對齊方式(根據主軸)alignItems
設定 item 的對齊方式(根據次軸)flex: 1
這個其實是在說flexGrow: 1
的意思,只是個簡寫
附註一下 RN 跟 CSS 不同的幾個地方:
flexDirection
的預設值是column
flexShrink
的預設值是 0
Direction
在 React Native 中,如果這樣設定:
1 | export default function App() { |
最後出來的東西會是三個「直向排列」的 item,跟你在寫 CSS 的時候還蠻不一樣的對吧?如果是 CSS 的話預設會是「橫向排列」。
簡單來說,這是因為在 RN 裡面 Flex 預設的方向是 column
而不是 row
的關係。
仔細想想的話也蠻合理的,畢竟我們在用手機的時後大多是以「直向」為主,所以把 Flex 預設成直向好像也挺直覺的!
alignContent
這個只會在有 flewWrap: wrap
的時候才有作用,記得這個就好了,舉例來說:
1 | export default function App() { |
這時候的結果是長這樣:
但如果把 alignContent
拿掉的話就會變這樣:
簡單來說,如果你的 flex-box 有啟用「換行」,然後你又想「把裡面的 flex-item 全部放到正中央」,那就會需要 alignContent
而不是 alignItems
。
alignItems
是用來處理「沒有換行」的時候才會用的屬性。
Position
在 RN 裡面 Position 的預設值是 relative
,所以我可以直接這樣寫:
1 | export default function App() { |
輸出結果:
用法其實都跟 CSS 一樣,所以就不特別介紹怎麼用了。這邊只是想特別提一下預設值是 relative
這一點。
至於 absolute
的話也跟 CSS 一樣,記得加上 position: 'absolute'
來打開,然後判斷好「父層的參考點」就好了。
順道一提,在 RN 裡面 position 就只有這兩種而已,不會有什麼 fixed
或 sticky
跟 static
這種東西。
載入 Font
FOIT 跟 FOUT 是什麼?
附註:如果你想看範例的話可以到 這邊
這邊科普一下兩個載入字體的使用手法:
- FOIT 先隱藏所有文字內容,等載入完成後才顯示(但其他的東西一開始會被顯示)
- FOUT 先顯示預設字型,等載入後再更新成新的字型
簡單來說就是不同的設計哲學而已,還蠻有趣的。
第一種做法(hook)
這種作法是透過 hook 來做的,簡單來說就是:
- 先下載字型的 package,
@expo-google-fonts/{googleFont}
- 接著用下面的方式來使用即可
附註:假設 <Home />
裡面有用到我們指定的字型。
1 | import AppLoading from 'expo-app-loading'; |
useFonts
會回傳一個陣列,陣列的第一個值是 boolean,代表這字型載入完了沒?
接著下面寫了一個 if
判斷,意思就是如果還在載入就顯示 <AppLoading />
,等到載入完以後才顯示 <Home />
。
雖然還蠻好奇他是怎麼更新 fontsLoaded
這個 state 然後觸發 re-render 的,不過這確實是可行的!也是最簡潔的做法。
第二種做法(Font.loadAsync)
這應該算是比較舊的做法,不過也是能參考一下:
附註:Font.loadAsync
也可以載入外部資源,只要把 require
改成對應的 URI 就行了
1 | import AppLoading from 'expo-app-loading'; |
跟第一種作法差不多,只是 state 跟 call function 的部分得自己來:
getFonts
用來載入字型的 function<AppLoading startAsync={getFonts}>
去 call 載入字型的 function<AppLoading onFinish={() => setFontsLoaded(true)}>
載入完成後更新 state
設定 Icon
關於 Icon 的部分,在我們用 expo init
專案的時候其實就會順便載入相關的套件了,所以我們只要直接 import 進來用就可以了,像這樣:
1 | import { MaterialIcons } from '@expo/vector-icons'; |