React Native實(shí)戰(zhàn)系列第十二篇——ScrollView

前言

  • 從iOS開發(fā)的經(jīng)驗來看,scrollView無疑是移動開發(fā)中很重要的一個組件,比如后面會學(xué)到的ListView就是繼承自它。那么,在開發(fā)中比如:焦點(diǎn)圖、引導(dǎo)頁等地方都有其的影子,那接下來我們一起來搞定它!

一個包裝了平臺的ScrollView(滾動視圖)的組件,同時還集成了觸摸鎖定的“響應(yīng)者”系統(tǒng)。

  • 兩個要點(diǎn):

  • ScrollView必須有一個確定的高度才能正常工作
    它實(shí)際上所做的就是將一系列不確定高度的子組件裝進(jìn)一個確定高度的容器(通過滾動操作)。

  • 通常有兩種做法:

    • 第一種: 直接給該ScrollView進(jìn)行設(shè)置高度(不建議);
    • 第二種: ScrollView中不要加{flex:1}。
  • ScrollView內(nèi)部的其他響應(yīng)者尚無法阻止ScrollView本身成為響應(yīng)者

<p></p>
<p></p>
<p></p>
<p></p>

一、ScrollView中常用的屬性

  • contentContainerStyle StyleSheetPropType(ViewStylePropTypes)
    這些樣式會應(yīng)用到一個內(nèi)層的內(nèi)容容器上,所有的子視圖都會包裹在內(nèi)容容器內(nèi)。

  • horizontal bool
    當(dāng)此屬性為true的時候,所有的的子視圖會在水平方向上排成一行,而不是默認(rèn)的在垂直方向上排成一列。默認(rèn)值為false。

  • keyboardDismissMode enum('none', "interactive", 'on-drag')
    用戶拖拽滾動視圖的時候,是否要隱藏軟鍵盤。
    none(默認(rèn)值),拖拽時不隱藏軟鍵盤。
    on-drag 當(dāng)拖拽開始的時候隱藏軟鍵盤。
    interactive 軟鍵盤伴隨拖拽操作同步地消失,并且如果往上滑動會恢復(fù)鍵盤。安卓設(shè)備上不支持這個選項,會表現(xiàn)的和none一樣。

  • keyboardShouldPersistTaps bool
    當(dāng)此屬性為false的時候,在軟鍵盤激活之后,點(diǎn)擊焦點(diǎn)文本輸入框以外的地方,鍵盤就會隱藏。如果為true,滾動視圖不會響應(yīng)點(diǎn)擊操作,并且鍵盤不會自動消失。默認(rèn)值為false。

  • onScroll function
    在滾動的過程中,每幀最多調(diào)用一次此回調(diào)函數(shù)。調(diào)用的頻率可以用scrollEventThrottle屬性來控制。

  • refreshControl element
    指定RefreshControl組件,用于為ScrollView提供下拉刷新功能。

  • removeClippedSubviews bool
    (實(shí)驗特性):當(dāng)此屬性為true時,屏幕之外的子視圖(子視圖的overflow樣式需要設(shè)為hidden)會被移除。這個可以提升大列表的滾動性能。默認(rèn)值為true。

  • showsHorizontalScrollIndicator bool
    當(dāng)此屬性為true的時候,顯示一個水平方向的滾動條。

  • showsVerticalScrollIndicator bool
    當(dāng)此屬性為true的時候,顯示一個垂直方向的滾動條。

  • alwaysBounceHorizontal bool
    當(dāng)此屬性為true時,水平方向即使內(nèi)容比滾動視圖本身還要小,也可以彈性地拉動一截。當(dāng)horizontal={true}時默認(rèn)值為true,否則為false。

  • [ios] alwaysBounceVertical bool
    當(dāng)此屬性為true時,垂直方向即使內(nèi)容比滾動視圖本身還要小,也可以彈性地拉動一截。當(dāng)horizontal={true}時默認(rèn)值為false,否則為true。

  • [ios] automaticallyAdjustContentInsets bool
    如果滾動視圖放在一個導(dǎo)航條或者工具條后面的時候,iOS系統(tǒng)是否要自動調(diào)整內(nèi)容的范圍。默認(rèn)值為true。(譯注:如果你的ScrollView或ListView的頭部出現(xiàn)莫名其妙的空白,嘗試將此屬性置為false)

  • [ios] bounces bool
    當(dāng)值為true時,如果內(nèi)容范圍比滾動視圖本身大,在到達(dá)內(nèi)容末尾的時候,可以彈性地拉動一截。如果為false,尾部的所有彈性都會被禁用,即使alwaysBounce*屬性為true。默認(rèn)值為true。

  • [ios] bouncesZoom bool
    當(dāng)值為true時,使用手勢縮放內(nèi)容可以超過min/max的限制,然后在手指抬起之后彈回min/max的縮放比例。否則的話,縮放不能超過限制。

  • [ios] canCancelContentTouches bool
    當(dāng)值為false時,一旦有子節(jié)點(diǎn)響應(yīng)觸摸操作,即使手指開始移動也不會拖動滾動視圖。默認(rèn)值為true(在以上情況下可以拖動滾動視圖。)

  • [ios] centerContent bool
    當(dāng)值為true時,如果滾動視圖的內(nèi)容比視圖本身小,則會自動把內(nèi)容居中放置。當(dāng)內(nèi)容比滾動視圖大的時候,此屬性沒有作用。默認(rèn)值為false。

  • [ios] contentInset {top: number, left: number, bottom: number, right: number}
    內(nèi)容范圍相對滾動視圖邊緣的坐標(biāo)。默認(rèn)為{0, 0, 0, 0}。

  • [ios] contentOffset PointPropType
    用來手動設(shè)置初始的滾動坐標(biāo)。默認(rèn)值為{x: 0, y: 0}。

  • [ios] decelerationRate number
    一個浮點(diǎn)數(shù),用于決定當(dāng)用戶抬起手指之后,滾動視圖減速停下的速度。常見的選項有:
    Normal: 0.998 (默認(rèn)值)
    Fast: 0.9

  • [ios] directionalLockEnabled bool
    當(dāng)值為真時,滾動視圖在拖拽的時候會鎖定只有垂直或水平方向可以滾動。默認(rèn)值為false。

  • [ios] maximumZoomScale number
    允許的最大縮放比例。默認(rèn)值為1.0。

  • [ios] minimumZoomScale number
    允許的最小縮放比例。默認(rèn)值為1.0。

  • [ios] onScrollAnimationEnd function
    當(dāng)滾動動畫結(jié)束之后調(diào)用此回調(diào)。

  • [ios] pagingEnabled bool
    當(dāng)值為true時,滾動條會停在滾動視圖的尺寸的整數(shù)倍位置。這個可以用在水平分頁上。默認(rèn)值為false。

  • [ios] scrollEnabled bool
    當(dāng)值為false的時候,內(nèi)容不能滾動,默認(rèn)值為true。

  • [ios] scrollEventThrottle number
    這個屬性控制在滾動過程中,scroll事件被調(diào)用的頻率(單位是每秒事件數(shù)量)。更大的數(shù)值能夠更及時的跟蹤滾動位置,不過可能會帶來性能問題,因為更多的信息會通過bridge傳遞。默認(rèn)值為0,意味著每次視圖被滾動,scroll事件只會被調(diào)用一次。

  • [ios] scrollIndicatorInsets {top: number, left: number, bottom: number, right: number}
    決定滾動條距離視圖邊緣的坐標(biāo)。這個值應(yīng)該和contentInset一樣。默認(rèn)值為{0, 0, 0, 0}。

  • [ios] scrollsToTop bool
    當(dāng)此值為true時,點(diǎn)擊狀態(tài)欄的時候視圖會滾動到頂部。默認(rèn)值為true。

  • [ios] snapToAlignment enum('start', "center", 'end')
    當(dāng)設(shè)置了snapToInterval,snapToAlignment會定義停駐點(diǎn)與滾動視圖之間的關(guān)系。
    start (默認(rèn)) 會將停駐點(diǎn)對齊在左側(cè)(水平)或頂部(垂直)
    center 會將停駐點(diǎn)對齊到中間
    end 會將停駐點(diǎn)對齊到右側(cè)(水平)或底部(垂直)

  • [ios] snapToInterval number
    當(dāng)設(shè)置了此屬性時,會讓滾動視圖滾動停止后,停止在snapToInterval的倍數(shù)的位置。這可以在一些子視圖比滾動視圖本身小的時候用于實(shí)現(xiàn)分頁顯示。與snapToAlignment組合使用。

  • [ios] stickyHeaderIndices [number]
    一個子視圖下標(biāo)的數(shù)組,用于決定哪些成員會在滾動之后固定在屏幕頂端。舉個例子,傳遞stickyHeaderIndices={[0]}會讓第一個成員固定在滾動視圖頂端。這個屬性不能和horizontal={true}一起使用。

  • [ios] zoomScale number
    滾動視圖內(nèi)容初始的縮放比例。默認(rèn)值為1.0。

  • onMomentumScrollEnd function
    當(dāng)一幀滾動完畢的時候調(diào)用,e.nativeEvent.contentOffset。

  • onScrollBeginDrag fuction
    當(dāng)開始手動拖拽的時候調(diào)用。

  • onScrollEndDrag fuction
    當(dāng)結(jié)束手動拖拽的時候調(diào)用。

注意:ScrollView是繼承自View,所以View中所有的屬性同樣適用于ScrollView。

三、綜合小案例——輪播圖效果

  • 用scrollView實(shí)現(xiàn)輪播圖效果

  • 技術(shù)實(shí)現(xiàn)要點(diǎn):

    • 在上下文中拿到組件需要給組件綁定ref
      render() {
      return (
         <ScrollView ref="myScrollView">
           {/*
              在上下文中可以通過this.refs.myScrollView拿到當(dāng)前的scrollView組件
           */}
         </ScrollView> 
      );
    }
    
  • 根據(jù)上下文求出偏移量

   e.nativeEvent.contentOffset.x; 
  • 讓scrollView根據(jù)偏移量進(jìn)行滾動
var currentX = activePage * screenWidth;
scrollView.scrollResponderScrollTo({x:currentX, y:0, animated:true});
  • 詳細(xì)代碼
  import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    ScrollView,
    Image
} from 'react-native';

// 1.導(dǎo)入本地的數(shù)據(jù)
const imageDataArr = require('./localData/ImageData.json').data;

// 2. 獲取寬度和高度
const Dimensions = require('Dimensions');
const {width} = Dimensions.get('window');

export default class XZHScrollImg extends Component {
    // 構(gòu)造
      constructor(props) {
        super(props);
        // 初始狀態(tài)
        this.state = {
            currentPage: 0
        };
      }

    render() {
        return (
            <View style={styles.container}>
                {/*上部分*/}
                <ScrollView
                    horizontal={true}
                    showsHorizontalScrollIndicator={false}
                    pagingEnabled={true}
                    onMomentumScrollEnd={(e)=>this._scrollEnd(e)}
                    ref="myScrollView"
                    // 開始拖拽
                    onScrollBeginDrag={()=>{clearInterval(this.timer)}}
                    // 結(jié)束拖拽
                    onScrollEndDrag={()=>this._startTimer()}
                >
                    {this._renderImg()}
                </ScrollView>
                {/*下部分*/}
                <View style={styles.bottomViewStyle}>
                    {this._renderIndicator()}
                </View>
            </View>
        );
    }

    /**
     * 處理復(fù)雜和耗時的操作
     */
    componentDidMount() {
        this._startTimer();
    }

    /**
     * 開啟定時器
     */
    _startTimer(){

        // 1. 定義當(dāng)前選中的索引
        var currentIndex = 0;
        const countPage = imageDataArr.length;
        const myScrollView = this.refs.myScrollView;

        this.timer = setInterval(()=>{
            // 2. 判斷
            if(this.state.currentPage+1 >= countPage){
                currentIndex = 0;
            }else {
                currentIndex = (this.state.currentPage += 1);
            }

            // console.log(currentIndex);

            // 3. 改變索引
            this.setState({
                currentPage: currentIndex
            });

            // 4. 讓scrollView滾起來
            myScrollView.scrollResponderScrollTo({x: currentIndex * width, y: 0, animated: true})

        }, 1000);
    }

    /**
     * 加載滾動的內(nèi)容
     * @private
     */
    _renderImg(){
        // 1. 組件數(shù)組
        var itemArr = [];

        // 2.遍歷
        imageDataArr.forEach((value, index)=>{
            itemArr.push(
                <Image key={index} source={{uri: value.img}} style={styles.imgStyle}/>
            )
        });

        // 3. 返回組件數(shù)組
        return itemArr;
    }

    /**
     * 返回指示器
     * @private
     */
    _renderIndicator(){
        // 1. 組件數(shù)組
        var indicatorArr = [], style;

        // 2.遍歷
        for(var i=0; i<imageDataArr.length; i++){
            // 2.1 設(shè)置樣式
            style = (this.state.currentPage == i) ? {color:'orange'} : {color: 'white'}
            // 2.2 裝入組件
            indicatorArr.push(
                <Text key={i} style={[{fontSize:35}, style]}>?</Text>
            );
        }

        // 3. 返回組件數(shù)組
        return indicatorArr;
    }

    /**
     * 當(dāng)一幀滾動結(jié)束
     * @private
     */
    _scrollEnd(e){
        // console.log(e.nativeEvent);
        // 1. 獲取水平方向滾動的偏移量
        const offsetX = e.nativeEvent.contentOffset.x;
        const index = parseInt(offsetX / width);

        // 2. 更新狀態(tài)機(jī)
        this.setState({
            currentPage: index
        });

    }
}

const styles = StyleSheet.create({
    container: {
        backgroundColor: '#F5FCFF',
    },

    imgStyle:{
        width:width,
        height: width * 0.5
    },

    bottomViewStyle:{
        width:width,
        height:36,
        backgroundColor:'rgba(0, 0, 0, .5)',

        position:'absolute',
        left:0,
        bottom:0,

        flexDirection:'row',
        alignItems:'center',
        paddingLeft:10
    }
});
  • 運(yùn)行結(jié)果:
輪播圖特效

<p></p>
<p></p>
<p></p>
<p></p>

長按圖片-->識別圖中二維碼

近期正在把公眾賬號的文章轉(zhuǎn)移到簡書,如果要了解第一動態(tài),請關(guān)注我的微信公眾賬號,帶你從零到一的進(jìn)行React Native開發(fā)實(shí)戰(zhàn),在其他文章中會有對應(yīng)的code和資料發(fā)放!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容