react-native布局與組件
- 2019 年 10 月 4 日
- 筆記
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個邏輯像素。
<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方式,也可以這麼寫:
<text style={[styles.aaa,{color:'red'}]}></text>
所有文本的樣式應該直接加在text上面,如果你在view裡面寫,就不會生效了。
{/* 錯誤的實例:不生效 */} <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 會自動根據系統的各種導航欄、工具欄等預留出空間來渲染內部內容。更重要的 是,它還會考慮到設備螢幕的局限,比如螢幕四周的圓⻆角或是頂部中間不可顯示的「非安全」區域。
<SafeAreaView style={{backgroundColor:'red'}}></SafeAreaView>

webview:載入網頁容器(即將被移除)
創建一個原生的webview,用於載入網頁.我們可結合safeAreaView使用:
<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布局,而是采⽤用文本布局。這意味著內部的元素不再是】一個個矩 形,而可能會在行末進⾏摺疊。
<Text ellipsizeMode={"tail"} //這個屬性通常和下⾯面的 numberOfLines 屬性配合使⽤用,⽂文本超出 numberOfLines設定的⾏行行數時,截取⽅方式:head- 從⽂文本內容頭部截取顯示省略略號。例例如: "...efg",middle - 在⽂文本內容中間截取顯示省略略號。例如: "ab...yz",tail - 從⽂文本內容尾 部截取顯示省略略號。例例如: "abcd...",clip - 不不顯示省略略號,直接從尾部截斷。 numberOfLines={1} //配合ellipsizeMode設置⾏行行數 onPress={...} //點擊事件 selectable={true}//決定⽤用戶是否可以⻓長按選擇⽂文本,以便便複製和粘貼。 >123 </Text>
Image:圖片容器
類似img元素。但支援更多但來源,比如網路圖片,本機磁碟圖片,照相機圖片等。
下⾯的例⼦分別演示了如何顯示從本地快取、網路乃至base64拉取圖片。
{/* 顯示本地圖 */} <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差不多:
{/* 顯示網路圖 */} <ImageBackground style={{width: 50%, height: 50%}} //網路和 base64 數據的圖⽚需要⼿動指定尺⼨ source={{uri: 'https://facebook.github.io/react-native/docs/assets/favicon.png'}}> <Text>...</Text> </ImageBackground>
Button:按鈕
一個簡單的跨平台的按鈕組件。可以進行一些簡單的訂製。如圖,前者為Android,後者為ios。
<Button onPress={onPressLearnMore} //⽤戶點擊此按鈕時所調用的處理理函數 title="Learn More" //按鈕內顯示的⽂文本 color="#841584" //文本的顏⾊(iOS),或是按鈕的背景⾊(Android) disabled={false} //按鈕是否可以點擊 accessibilityLabel="Learn more about this purple button" //用於給殘障人⼠顯示的文本(比如讀屏應⽤可能會讀取這一內容) />


ActivityIndicator loading的小菊花
顯示一個loading提示符Android設備時一個Google式半圓環,在ios設備上則顯示一朵小菊花。
<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通過維護一個有限的渲染窗⼝(其中包含可⻅的元素),並將渲染窗⼝之外的元素全部用合適的定⻓空⽩空間代替的⽅式,極⼤的改善了記憶體使⽤,提⾼了大量數據情況下的渲染性能。這個渲染窗⼝能響應滾動行為,元素離可視區越遠優先順序越低,越近優先順序越高,當用戶滑動速度過快時,會出現短暫空⽩的情況。
<FlatList data={[{key: 'a'}, {key: 'b'}]} renderItem={({item}) => <Text>{item.key}</Text>} />
缺點:
(1)為了優化記憶體占⽤同時保持滑動的流暢,列表內容會在螢幕外非同步繪製。這意味著如果用戶滑動的速度超過渲染的速度,則會先看到空白的內容。
(2)不支援分組列列表
扯了那麼多理論,如果列表寫不了想說自己懂rn是很扯的。是時候開始寫一個了。
需求:列表的下拉刷新和上划動載入

看今日頭條等新聞列表類app時,都需要用到。
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。
