React Native中ScrollView元件輪播圖與ListView渲染列表元件用法例項分析
本文例項講述了React Native中ScrollView元件輪播圖與ListView渲染列表元件用法。分享給大家供大家參考,具體如下:
1、Scroll View
ScrollView是React Native提供的滾動檢視元件,渲染一組檢視,使用者可以進行滑動響應互動,其常用屬性如下:
滾動的偏移量:通過event.nativeEvent.contentOffset.x可以得到水平偏移量。
- horizontal={bool},屬性為true時,所有子檢視在水平方向排列,否則在縱向排列。預設為false。
- pagingEnabled={bool},屬性為true時,滾動會停留在檢視尺寸整數倍位置上,即正好顯示某個檢視,預設為false
- scrollEnabled={bool},值為false時,檢視不能滾動,預設true
- showsHorizontalScrollIndicator={bool},值為true在滾動時會在螢幕底部顯示一個滾動條,預設true
- showsVerticalScrollIndicator={bool},值為true在滾動時顯示垂直方向的滾動條,預設true。
- keyboardDismissMode="none"/"on-drag",滑動檢視時是否隱藏軟鍵盤,預設none不隱藏。
- onContentChange={function},當ScrollView檢視大小發生變化時呼叫函式。
- onScroll={function},當滾動檢視時呼叫函式。
- onMomentumScrollStart={function},滾動開始呼叫函式。
- onMomentumScrollEnd={function},滾動結束時呼叫函式。
元件所屬的方法有:
- scrollTo({x:num,y:num,animated:bool}),元件檢視滾動到指定x,y位置,第三個引數為是否啟用動畫
- scrollToEnd({animated:bool}),滾動到檢視末尾。
例如利用ScrollView來實現一個Banner輪播:
頁面結構如下:
<View style={styles.banner}> <ScrollView ref="scrollView" horizontal={true} pagingEnabled={true} showsHorizontalScrollIndicator={false} onMomentumScrollEnd={(e)=>this.slide(e)} onScrollBeginDrag={()=>{this.stopTimer()}} //使用者拖拽時停止自動輪播 onScrollEndDrag={()=>{this.setTimer()}} //拖拽結束後開始自動切換 > {/*渲染輪播圖片*/} {this.renderBanner()} </ScrollView> <View style={styles.indicateBar}> {/*渲染底部指示標籤點*/} {this.renderIndicate()} </View> </View>
利用map遍歷資料陣列zodiac,將圖片渲染到頁面
renderBanner(){ return zodiac.map((item,index)=> <Image key={index} source={{uri:'asset:/zodiac/'+item.image+'.jpg'}} style={styles.itemImage} /> ) }
在底部渲染指示點:
renderIndicate(){ let jsx=[]; for (let i=0;i<zodiac.length;i++){ //判斷是否為當前頁,若為當前頁則指示點color為藍色,否則為白色 if (i===this.state.pageIndex){ jsx.push(<Text key={i} style={{fontSize:15,color:'#5cb0ff'}}>●</Text>) }else { jsx.push(<Text key={i} style={{fontSize:15,color:'#ffffff'}}>●</Text>) } } return jsx; }
當用戶滑動結束時觸發ScrollView的onMomentumScrollEnd方法,呼叫slide函式,並傳遞event引數給slide。通過計算得出使用者滑到的當前頁的索引pageIndex,其中頁碼的計算就是將x偏移量除以每個檢視的寬度然後取整
slide(e){ let offset=e.nativeEvent.contentOffset.x; //獲取x偏移量 let index=Math.floor(offset/DevWidth); //通過偏移量計算出當前頁碼 this.setState({ pageIndex:index }) }
設定定時器讓檢視自動更換,通過setInterval讓pageIndex隔一段時間自動+1,然後讓圖片偏移到頁碼對應的圖片,令頁面索引乘以每個頁面寬度即為當前頁面對應的偏移量:
setTimer(){ this.timer=setInterval(()=>{ this.setState((preState)=>{ //更新pageIndex if(preState.pageIndex>=(zodiac.length-1)){ //如果頁碼達到上界則歸零 return {pageIndex:0} }else { return {pageIndex:preState.pageIndex+1} //否則頁碼加一 } }); // 讓圖片偏移到頁碼所對應的頁面 let offset=this.state.pageIndex*DevWidth; this.refs.scrollView.scrollTo({x:offset,y:0,animated:true}); },2000) }
在元件銷燬時清除定時器
componentWillUnmount() { clearInterval(this.timer); }
2、List View
<ListView>用於將一組相同型別的資料渲染到頁面上,你只需要定義好資料來源與單個元件如何渲染,它便會將所有資料渲染完成。例如將如下左邊json資料渲染為右邊icon列表:
使用步驟如下
1、定義資料來源,在constructor中初始化state,建立一個DataSource物件,在state中定義資料來源iconSource為外部匯入的json資料icons,格式如下:
let icons=require('./mockdata/icons.json').data; constructor(props){ super(props); let dataSource = new ListView.DataSource({rowHasChanged:(r1,r2)=>r1!==r2}); this.state={ iconSource:dataSource.cloneWithRows(icons),} }
其中{rowHasChaged:(r1,r2)=>r1!==r2}
,是告訴ListView當資料來源變化時再重新渲染。
2、在頁面使用<ListView>,設定資料來源dataSource,內部樣式contentContainerStyle,每個元素的渲染方式renderRow為renderIcon
<ListView dataSource={this.state.iconSource} contentContainerStyle={styles.iconList} renderRow={this.renderIcon} />
3、實現渲染函式renderIcon,預設傳入四個引數:
- rowData:每個元素對應的資料
- sectionId:元素所屬分割槽
- rowId:元素的id
- highlightRow:通過呼叫此方法可以使某一行處於高亮
在renderIcon函式中定義每一個icon圖示的渲染的方式,並返回JSX:
renderIcon(rowData,sectionId,rowId,highlightRow){ return( <TouchableOpacity activeOpacity={0.5}> <View key={rowId} style={styles.iconItem}> <Image style={styles.iconImg} source={{uri:'mipmap/'+rowData.image}} /> <Text style={styles.iconTitle}>{rowData.title}</Text> </View> </TouchableOpacity> ) }
3、使用ListView渲染二維資料
以上例子中的data是個一維陣列,陣列每個元素中包含title與image兩個欄位,如果data是個二維陣列,例如
其中data陣列的一維元素中包含title與cars,而cars又是一個數組。使用ListView將其渲染為上面右圖所示按首字母分類的列表。
儲存原理:
ListView使用DataBlob來儲存二維資料,其結構如下:
DataBlob按照一定的格式組織二維資料,如上左圖。DataBlob首先儲存陣列的第一維section併為其分配ID,例如將上面的一維陣列的"title":"A",儲存為DataBlob[0]="title":"A",分配sectionID為0,"title":"B",儲存為DataBlob[1]="title":"B",分配ID為1......以此類推。
之後再儲存陣列的第二維row,例如"cars":[{"name":"奧迪","icon": "m_9_100.png"}],它的第一維sectionID為0,第二維rowID為2,將其儲存為DataBlob[0:2]={"name":"奧迪","icon": "m_9_100.png"}。
ListView使用步驟如下:
1、設定資料來源
與一維ListView使用類似,首先在constructor中設定state為DataSource物件:
this.state={ carData:new ListView.DataSource({ getSectionData:(dataBlob,sectionID)=>dataBlob[sectionID],//設定sectionData獲取方式 getRowData:(dataBlob,sectionID,rowID)=>dataBlob[sectionID+':'+rowID],//設定rowData獲取方式 sectionHeaderHasChanged:(s1,s2)=>s1!==s2,//設定section更新方式 rowHasChanged:(r1,r2)=>r1!==r2 //設定row更新方式 }) }
在新建DataSource物件時需要傳遞四個函式引數
- getSectionData:定義獲取section的方式,它接收兩個引數,dataBlob物件與sectionId,例如要獲取上面提到的DataBlob[0]="title":"A" ,則通過dataBlob[sectionID]就可以返回"title":"A"。
- getRowData:獲取row的方式,同理,通過DataBlob[0:2]可以得到{"name":"奧迪","icon": "m_9_100.png"}
- sectionHeaderHasChanged:定義section什麼時候更新,接收兩個引數s1,s2分別為前後兩個狀態,不同時才會重新渲染section
- rowHasChanged:定義row什麼時候更新
2、在頁面中使用ListView
使用List View時設定其資料來源及渲染方法
<ListView style={styles.carList} dataSource={this.state.carData} //定義資料來源 renderRow={this.renderCarRow} //定義row的渲染方法 renderSectionHeader={this.renderCarSection} //定義SectionHeader渲染方法 />
3、實現渲染方法,方法預設會傳入引數rowData與sectionData
renderCarSection(sectionData){ return( <View style={styles.sectionBar}> <Text style={styles.sectionTxt}>{sectionData}</Text> </View> ) } renderCarRow(rowData){ return( <TouchableOpacity activeOpacity={0.5}> <View style={styles.carItem}> <Image source={{uri:'asset:/cars/'+rowData.icon}} style={styles.carImg} /> <Text style={styles.carTitle}>{rowData.name}</Text> </View> </TouchableOpacity> ) }
4、將資料放入dataBlob
在元件掛載完成後將資料按照格式放入dataBlob並更新資料來源,使資料載入到頁面
componentDidMount() { this.loadCarData(); } loadCarData(){ let dataBlob={},//dataBlob物件 sectionIDs=[],//sectionID陣列 rowIDs=[],//rowID陣列 cars=[]; for (let i=0;i<carData.length;i++){ //迴圈遍歷二維資料carData sectionIDs.push(i); //將一維下標i當作sectionID dataBlob[i]=carData[i].title; //將section資料放入dataBlob第一維 rowIDs[i]=[]; //初始化rowID陣列的每個元素為一個數組 cars=carData[i].cars; //拿到每個section下的cars陣列 for (let j=0;j<cars.length;j++){ //遍歷section下的cars陣列 rowIDs[i].push(j); //二維陣列rowIDs[i][j] dataBlob[i+':'+j]=cars[j]; //將每行row資料放入dataBlob[i:j]第二維 } } this.setState({ //更新state中的資料來源carData,需要傳入三個引數 carData:this.state.carData.cloneWithRowsAndSections(dataBlob,sectionIDs,rowIDs) }) }
希望本文所述對大家React程式設計有所幫助。