ScrollView是RN中的一个滚动视图组件,它必须有一个确定的高度才能正常工作,因为在应用时往往会把将一系列不确定高度的子组件装进一个确定高度的容器。关于滚动视图高度这一点,不建议直接在样式中设置一个固定的height值(在目前最新版本中直接无效),而是通过设置flex: 1以使其自动填充父容器的空余空间,但前提条件是所有的父容器本身也设置了flex或者指定了高度,否则就会导致无法正常滚动。

ScrollView组件常被应用到的几个场景是:

  1. 在视图最顶部往下滑动,实现数据刷新,如微信朋友圈。
  2. 在到达视图最底部时往上滑动,提示"已经没有内容",如斗鱼APP中的鱼吧。
  3. 左右整页滑屏,类似于走马灯的广告页或分屏功能。

本篇博客主要就是详细介绍ScrollView的几个应用实例及api。

ScrollView的属性与方法

属性描述

style

滚动视图样式

scrollEnabled

当为false时表示禁止滚动,默认为: true

horizontal

当此属性为true的时候,所有的子视图会在水平方向上排成一行, 即水平滚动, 默认为: false

keyboardShouldPersistTaps

当点击TextInput以外的子组件会使当前的软键盘收起,默认为: never。当为always时相反,软键盘不会被收起

showsVerticalScrollIndicator

当此属性为true的时候,显示一个垂直方向的滚动条,默认为: true

showsHorizontalScrollIndicator

当此属性为true的时候,显示一个水平方向的滚动条,默认为: true

pagingEnabled

当值为true时,滚动条会停在滚动视图的尺寸的整数倍位置,这个可以用在水平分页上。默认为: false

refreshControl

当ScrollView处于竖直方向的起点位置(scrollY: 0),此时下拉会触发一个onRefresh事件

stickyHeaderIndices

让某个成员固定在滚动视图顶端,如当stickyHeaderIndices={[0]}时表示第一个item将永远不会被滚动到边界外

onMomentumScrollBegin

滚动惯性动画开始时触发的函数

onMomentumScrollEnd

滚动惯性动画结束时触发的函数

onScrollBeginDrag

拖拽开始时触发的函数

onScrollEndDrag

拖拽结束时触发的函数

onScroll

视图滚动时触发的函数

方法描述

scrollTo

滚动到指定坐标,如scrollTo({x: 0, y: 0, animated: true})

scrollToEnd

滚动到最尾部,如scrollToEnd()
flashScrollIndicators短暂地显示滚动指示器

要触发滚动视图方法,可通过 ref 引用实现,如:

<ScrollView
    ref={(view) => { this.myScrollView = view; }}
>

// 调用
this.myScrollView.scrollTo({x: 0, y: 550, animated: true}); // 滚动到指定x,y位置
this.myScrollView.scrollToEnd(); // 滚动到尾部

应用场景1:滚动视图实现数据刷新

这种情景用到了RefreshControl组件,它为ScrollView提供下拉刷新功能。只能用于垂直视图,即horizontal不能为true。贴上代码:

import React, { Component } from 'react';
import { 
    View, 
    Text, 
    ScrollView, 
    StyleSheet, 
    RefreshControl, 
    Alert 
} from 'react-native';

class ScrollViewComp extends Component {
    
    state = {
        isRefreshing: false,
        dataList: []
    };

    // 初始化列表数据
    componentDidMount(){
        let initList = [];
        for(let i = 0; i < 20; i++) {
            initList.push(i+1);
        }
        this.setState({dataList: initList});
    }

    // 渲染列表
    _renderList = () => {
        const { dataList } = this.state;
        let result = [];
        for(let i = 0; i < dataList.length; i++) {
            result.push(this._renderItem(dataList[i]));
        }
        return result;
    }

    // 渲染列表项
    _renderItem = (id) => {
        return (
            <View key={id} style={styles.itemViewStyle}>
                <Text style={styles.itemTextStyle}>Item{id}</Text>
            </View>
        );
    }

    // 刷新过程
    _onRefresh = () => {
        this.setState({isRefreshing: true});
        setTimeout(()=>
        {
            const newDatas = ['-new1','-new2','-new3'];
            const newList = newDatas.concat(this.state.dataList);
            this.setState({dataList:newList,isRefreshing: false});
            this._alert('数据加载完毕');
        }, 2000);
    }

    // 提示框
    _alert = (text) => {
        Alert.alert(
            '通知',
            text,
            [
              {text: '知道了', onPress: () => console.log('OK Pressed')},
            ],
            { cancelable: false }
        );
    }

    render() {
        return (
            <View style={{flex:1}}>
                <View style={styles.titleViewStyle}>
                    <Text style={styles.titleTextStyle}>我的APP</Text>
                </View>
                <ScrollView
                    style={styles.scrollViewStyle}
                    // 当ScrollView处于竖直方向的起点位置(scrollY: 0),此时下拉会触发一个onRefresh事件。
                    refreshControl={
                        <RefreshControl
                            refreshing={this.state.isRefreshing}
                            onRefresh={this._onRefresh.bind(this)}
                            tintColor="#ffffff" // 指定刷新指示器的背景色(iOS)
                            title="加载中..." // 指定刷新指示器下显示的文字(iOS)
                            titleColor="#000000" // 指定刷新指示器下显示的文字的颜色(iOS)
                            colors={['#ff0000', '#00ff00', '#0000ff']} // 刷新指示器在刷新期间的过渡颜色(Android)
                            progressBackgroundColor="#ffffff" // 指定刷新指示器的背景色(Android)
                        />
                    }
                >
                    {this._renderList()}
                </ScrollView>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    scrollViewStyle: {
        flex: 1, 
        marginLeft:10, 
        marginRight: 10, 
        marginBottom: 10
    },
    titleViewStyle: {
        height: 50,
        backgroundColor: '#f4511e',
        justifyContent: 'center',
        alignItems: 'center'
    },
    titleTextStyle: {
        fontSize: 20,
        color: '#FFFFFF'
    },
    itemViewStyle: {
        height: 70,
        borderWidth: 1,
        borderRadius: 10,
        marginTop: 5,
        justifyContent: 'center',
        alignItems: 'center'
    },
    itemTextStyle: {
        color: '#000000',
        fontSize: 20
    }
});

export default ScrollViewComp;

效果:

应用场景2:到达最底部时触发监听

这种情景需要用到触发onScroll事件监听,在滚动的过程中,每帧最多调用一次此回调函数。贴上代码:

import React, { Component } from 'react';
import { 
    View, 
    Text, 
    ScrollView, 
    StyleSheet,
    Alert 
} from 'react-native';

class ScrollViewComp extends Component {
    
    state = {
        isRefreshing: false,
        dataList: []
    };

    // 初始化列表数据
    componentDidMount(){
        let initList = [];
        for(let i = 0; i < 20; i++) {
            initList.push(i+1);
        }
        this.setState({dataList: initList});
    }

    // 渲染列表
    _renderList = () => {
        const { dataList } = this.state;
        let result = [];
        for(let i = 0; i < dataList.length; i++) {
            result.push(this._renderItem(dataList[i]));
        }
        return result;
    }

    // 渲染列表项
    _renderItem = (id) => {
        return (
            <View key={id} style={styles.itemViewStyle}>
                <Text style={styles.itemTextStyle}>Item{id}</Text>
            </View>
        );
    }

    _contentViewScroll = (e) => {
        var offsetY = e.nativeEvent.contentOffset.y; // 已经滚动的距离
        var oriageScrollHeight = e.nativeEvent.layoutMeasurement.height; // 可滚动的可见区域高度
        var contentSizeHeight = Math.round(e.nativeEvent.contentSize.height); // 可滚动的总高度
        if (Math.round(offsetY + oriageScrollHeight) >= contentSizeHeight){
            this._alert('滑动到底部了');
        }
    }

    // 提示框
    _alert = (text) => {
        Alert.alert(
            '通知',
            text,
            [
              {text: '知道了', onPress: () => console.log('OK Pressed')},
            ],
            { cancelable: false }
        );
    }

    render() {
        return (
            <View style={{flex:1}}>
                <View style={styles.titleViewStyle}>
                    <Text style={styles.titleTextStyle}>我的APP</Text>
                </View>
                <ScrollView
                    style={styles.scrollViewStyle}
                    onScroll = {this._contentViewScroll} // 获取滑动数据
                >
                    {this._renderList()}
                </ScrollView>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    scrollViewStyle: {
        flex: 1, 
        marginLeft:10, 
        marginRight: 10, 
        marginBottom: 10
    },
    titleViewStyle: {
        height: 50,
        backgroundColor: '#f4511e',
        justifyContent: 'center',
        alignItems: 'center'
    },
    titleTextStyle: {
        fontSize: 20,
        color: '#FFFFFF'
    },
    itemViewStyle: {
        height: 70,
        borderWidth: 1,
        borderRadius: 10,
        marginTop: 5,
        justifyContent: 'center',
        alignItems: 'center'
    },
    itemTextStyle: {
        color: '#000000',
        fontSize: 20
    }
});

export default ScrollViewComp;

效果:

应用场景3:左右滑屏

这种情景需要把horizontal设为true,即水平滚动。再把pagingEnabled设为true,表示整屏滚动。贴上代码:

import React, { Component } from 'react';
import { 
    View, 
    Text, 
    ScrollView, 
    StyleSheet,
    Dimensions
} from 'react-native';

const {width} = Dimensions.get('window');
class ScrollViewComp extends Component {
    
    state = {
        isRefreshing: false,
        dataList: []
    };

    // 初始化列表数据
    componentDidMount(){
        let initList = [];
        for(let i = 0; i < 3; i++) {
            initList.push(i+1);
        }
        this.setState({dataList: initList});
    }

    // 渲染列表
    _renderList = () => {
        const { dataList } = this.state;
        let result = [];
        for(let i = 0; i < dataList.length; i++) {
            result.push(this._renderItem(dataList[i]));
        }
        return result;
    }

    // 渲染列表项
    _renderItem = (id) => {
        return (
            <View key={id} style={styles.itemViewStyle}>
                <Text style={styles.itemTextStyle}>Item{id}</Text>
            </View>
        );
    }

    render() {
        return (
            <View style={{flex:1}}>
                <View style={styles.titleViewStyle}>
                    <Text style={styles.titleTextStyle}>我的APP</Text>
                </View>
                <ScrollView
                    style={styles.scrollViewStyle}
                    horizontal={true}
                    pagingEnabled={true}
                >
                    {this._renderList()}
                </ScrollView>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    scrollViewStyle: {
        flex: 1, 
        marginLeft:10, 
        marginRight: 10, 
        marginBottom: 10
    },
    titleViewStyle: {
        height: 50,
        backgroundColor: '#f4511e',
        justifyContent: 'center',
        alignItems: 'center'
    },
    titleTextStyle: {
        fontSize: 20,
        color: '#FFFFFF'
    },
    itemViewStyle: {
        width: width-20,
        flex:1,
        borderWidth: 1,
        borderRadius: 10,
        marginTop: 5,
        justifyContent: 'center',
        alignItems: 'center'
    },
    itemTextStyle: {
        color: '#000000',
        fontSize: 20
    }
});

export default ScrollViewComp;

效果:

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐