一、前言
從iOS開發的經驗來看,scrollView無疑是移動開發中很重要的一個組件,比如后面會學到的ListView就是繼承自它。那么,在開發中比如:焦點圖、引導頁等地方都有其的影子,那接下來我們一起來搞定它!
一個包裝了平台的ScrollView(滾動視圖)的組件,同時還集成了觸摸鎖定的“響應者”系統。
兩個要點:
a)ScrollView必須有一個確定的高度才能正常工作
它實際上所做的就是將一系列不確定高度的子組件裝進一個確定高度的容器(通過滾動操作)。
通常有兩種做法:
第一種: 直接給該ScrollView進行設置高度(不建議);
第二種: ScrollView中不要加{flex:1}。
b)ScrollView內部的其他響應者尚無法阻止ScrollView本身成為響應者
二、ScrollView中常用的屬性
contentContainerStyle StyleSheetPropType(ViewStylePropTypes)
這些樣式會應用到一個內層的內容容器上,所有的子視圖都會包裹在內容容器內。
horizontal bool
當此屬性為true的時候,所有的的子視圖會在水平方向上排成一行,而不是默認的在垂直方向上排成一列。默認值為false。
keyboardDismissMode enum('none', "interactive", 'on-drag')
用戶拖拽滾動視圖的時候,是否要隱藏軟鍵盤。
-
none(默認值),拖拽時不隱藏軟鍵盤。 -
on-drag當拖拽開始的時候隱藏軟鍵盤。 -
interactive軟鍵盤伴隨拖拽操作同步地消失,並且如果往上滑動會恢復鍵盤。安卓設備上不支持這個選項,會表現的和none一樣。
keyboardShouldPersistTaps bool
當此屬性為false的時候,在軟鍵盤激活之后,點擊焦點文本輸入框以外的地方,鍵盤就會隱藏。如果為true,滾動視圖不會響應點擊操作,並且鍵盤不會自動消失。默認值為false。
onScroll function
在滾動的過程中,每幀最多調用一次此回調函數。調用的頻率可以用scrollEventThrottle屬性來控制。
refreshControl element
指定RefreshControl組件,用於為ScrollView提供下拉刷新功能。
removeClippedSubviews bool
(實驗特性):當此屬性為true時,屏幕之外的子視圖(子視圖的overflow樣式需要設為hidden)會被移除。這個可以提升大列表的滾動性能。默認值為true。
showsHorizontalScrollIndicator bool
當此屬性為true的時候,顯示一個水平方向的滾動條。
showsVerticalScrollIndicator bool
當此屬性為true的時候,顯示一個垂直方向的滾動條。
alwaysBounceHorizontal bool
當此屬性為true時,水平方向即使內容比滾動視圖本身還要小,也可以彈性地拉動一截。當horizontal={true}時默認值為true,否則為false。
ios alwaysBounceVertical bool
當此屬性為true時,垂直方向即使內容比滾動視圖本身還要小,也可以彈性地拉動一截。當horizontal={true}時默認值為false,否則為true。
ios automaticallyAdjustContentInsets bool
如果滾動視圖放在一個導航條或者工具條后面的時候,iOS系統是否要自動調整內容的范圍。默認值為true。(譯注:如果你的ScrollView或ListView的頭部出現莫名其妙的空白,嘗試將此屬性置為false)
ios bounces bool
當值為true時,如果內容范圍比滾動視圖本身大,在到達內容末尾的時候,可以彈性地拉動一截。如果為false,尾部的所有彈性都會被禁用,即使alwaysBounce*屬性為true。默認值為true。
ios bouncesZoom bool
當值為true時,使用手勢縮放內容可以超過min/max的限制,然后在手指抬起之后彈回min/max的縮放比例。否則的話,縮放不能超過限制。
ios canCancelContentTouches bool
當值為false時,一旦有子節點響應觸摸操作,即使手指開始移動也不會拖動滾動視圖。默認值為true(在以上情況下可以拖動滾動視圖。)
ios centerContent bool
當值為true時,如果滾動視圖的內容比視圖本身小,則會自動把內容居中放置。當內容比滾動視圖大的時候,此屬性沒有作用。默認值為false。
ios contentInset {top: number, left: number, bottom: number, right: number}
內容范圍相對滾動視圖邊緣的坐標。默認為{0, 0, 0, 0}。
ios contentOffset PointPropType
用來手動設置初始的滾動坐標。默認值為{x: 0, y: 0}。
ios decelerationRate number
一個浮點數,用於決定當用戶抬起手指之后,滾動視圖減速停下的速度。常見的選項有:
-
Normal: 0.998 (默認值) -
Fast: 0.9
ios directionalLockEnabled bool
當值為真時,滾動視圖在拖拽的時候會鎖定只有垂直或水平方向可以滾動。默認值為false。
ios maximumZoomScale number
允許的最大縮放比例。默認值為1.0。
ios minimumZoomScale number
允許的最小縮放比例。默認值為1.0。
ios onRefreshStart function
已過期
請使用refreshControl 屬性代替。
ios onScrollAnimationEnd function
當滾動動畫結束之后調用此回調。
ios pagingEnabled bool
當值為true時,滾動條會停在滾動視圖的尺寸的整數倍位置。這個可以用在水平分頁上。默認值為false。
ios scrollEnabled bool
當值為false的時候,內容不能滾動,默認值為true。
ios scrollEventThrottle number
這個屬性控制在滾動過程中,scroll事件被調用的頻率(單位是每秒事件數量)。更大的數值能夠更及時的跟蹤滾動位置,不過可能會帶來性能問題,因為更多的信息會通過bridge傳遞。默認值為0,意味着每次視圖被滾動,scroll事件只會被調用一次。
ios scrollIndicatorInsets {top: number, left: number, bottom: number, right: number}
決定滾動條距離視圖邊緣的坐標。這個值應該和contentInset一樣。默認值為{0, 0, 0, 0}。
ios scrollsToTop bool
當此值為true時,點擊狀態欄的時候視圖會滾動到頂部。默認值為true。
ios snapToAlignment enum('start', "center", 'end')
當設置了snapToInterval,snapToAlignment會定義停駐點與滾動視圖之間的關系。
-
start(默認) 會將停駐點對齊在左側(水平)或頂部(垂直) -
center會將停駐點對齊到中間 -
end會將停駐點對齊到右側(水平)或底部(垂直)
ios snapToInterval number
當設置了此屬性時,會讓滾動視圖滾動停止后,停止在snapToInterval的倍數的位置。這可以在一些子視圖比滾動視圖本身小的時候用於實現分頁顯示。與snapToAlignment組合使用。
ios stickyHeaderIndices [number]
一個子視圖下標的數組,用於決定哪些成員會在滾動之后固定在屏幕頂端。舉個例子,傳遞stickyHeaderIndices={[0]}會讓第一個成員固定在滾動視圖頂端。這個屬性不能和horizontal={true}一起使用。
ios zoomScale number
滾動視圖內容初始的縮放比例。默認值為1.0。
OnMomentumScrollEnd function
當一幀滾動完畢的時候調用,e.nativeEvent.contentOffset。
onScrollBeginDrag fuction
當開始手動拖拽的時候調用。
onScrollEndDrag fuction
當結束手動拖拽的時候調用。
*注意:ScrollView是繼承自View,所以View中所有的屬性同樣適用於ScrollView。
三、Demo鞏固練習
3.1 用scrollView實現輪播圖效果
技術實現要點
1)需要導入計時器類庫
npm i react-timer-mixin --save
在項目中需要引入並注冊:
引入: var TimerMixin = require('react-timer-mixin');
注冊:mixins: [TimerMixin],
2)在上下文中拿到組件需要給組件綁定ref

3)根據上下文求出偏移量
e.nativeEvent.contentOffset.x;
4)讓scrollView根據偏移量進行滾動
var currentX = activePage * screenWidth;
scrollView.scrollResponderScrollTo({x:currentX, y:0, animated:true});
案例代碼:
/** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View, ScrollView, Image } from 'react-native'; //引入計時器類庫 var TimerMixin = require('react-timer-mixin'); //引入JSON文件 var ImageData = require('./ImageData.json'); var Dimensions = require('Dimensions'); var {width} = Dimensions.get('window'); export default class FScrollViewDemo1 extends Component{ //注冊計時器 mixins: [TimerMixin] //設置固定值 static defaultProps = { duration:1000 } //設置改變和初始值 constructor(props){ super(props); this.state = { currentPage:0 }; } render(){ return( <View style={styles.container}> <ScrollView ref="scrollView" horizontal ={true} pagingEnabled={true} //當一幀滾動結束 onMomentumScrollEnd={(e)=>this.onAnimationEnd(e)} //開始拖拽 onScrollBeginDrag={this.onScrollBeginDrag} //停止拖拽 onScrollEndDrag={this.onScrollEndDrag} > {this.renderAllImage()} </ScrollView> <View style = {styles.pageViewStyle}> {this.renderPageCircle()} </View> </View> ); } //停止拖拽 onScrollBeginDrag(){ //停止定時器 this.clearInterval(this.timer); } onScrollEndDrag(){ //開啟定時器 this.startTimer(); } componentDidMount(){ //開啟定時器 this.startTimer(); } //開啟定時器 startTimer(){ //1.拿到scrollview var scrollView = this.refs.scrollView; var imgCount =ImageData.data.length; //2.添加定時器 this.timer = setInterval(()=>{ //2.1設置圓點 var activePage = 0; //2.2判斷 if((this.state.currentPage+1) >= imgCount){ activePage = 0; }else{ activePage = this.state.currentPage+1; } //更新狀態機 this.setState({ currentPage:activePage }); // // var offSetX = activePage * width; // scrollView.scrollResponderScrollTo({x:offSetX,y:0,animated:true}); },this.props.duration); } renderPageCircle(){ var indicatorArr = []; var style; var imgsArr = ImageData.data; for(var i=0;i<imgsArr.length;i++){ style = (i == this.state.currentPage) ? {color:'orange'} : {color:'white'}; indicatorArr.push( <Text key={i} style={[{fontSize:25},style]}>•</Text> ); } return indicatorArr; } renderAllImage(){ var allImage = []; //拿到圖像數組 var imgsArr = ImageData.data; for(var i = 0;i<imgsArr.length;i++){ var imgItem = imgsArr[i]; allImage.push( <Image key={i} source ={{uri:imgItem.img}} style={{width:width,height:120}}/> ); } return allImage; } //當一幀滾動結束后調用 onAnimationEnd(e){ //1.計算水平偏移量 var offSetX = e.nativeEvent.contentOffset.x; //2.求出當前的頁數 var currentPage = Math.floor(offSetX / width); console.log(currentPage); //3.更新狀態機,重新繪制UI this.setState({ currentPage:currentPage }); } } const styles = StyleSheet.create({ container:{ marginTop:25, backgroundColor:'white' }, pageViewStyle:{ width:width, height:25, backgroundColor:'rgba(0,0,0,0.4)', //定位 position:'absolute', bottom:0, //設置主軸的方向 flexDirection:'row', alignItems:'center', justifyContent:'center' } });
