ReactNative开发——滑动组件

前端之家收集整理的这篇文章主要介绍了ReactNative开发——滑动组件前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

ReactNative开发——滑动组件

环境

window android react-native 0.45

ScrollView

介绍

ScrollView是一个可以滑动的组件,它内部可以是一个高度不受控制的View,但它自身必须要有个固定的高度。这里如果我们不给直接他设置高度,它的上层空间有固定高度的话也是可以的。

<ScrollView> VS <FlatList>我们应该选择哪个?

ScrollView 的工作是简单的渲染所有的子组件,它的用法比较简单。

FlatList 是惰性渲染item,只有当item出现在界面上时才渲染,并且从屏幕滑出去之后该item会被移除。所以它更省内存,也更节省cpu处理的时间。当然他的用法也比较复杂。

用法

我们来看一下用法示例,大家可以直接copy我的代码运行试试看:

/** * Created by blueberry on 6/9/2017. * @flow */

import React,{Component} from 'react';
import {
    AppRegistry,StyleSheet,View,Text,ScrollView,ListView,FlatList,RefreshControl,Dimensions,Button,TextInput,} from 'react-native';

let totalWidth = Dimensions.get('window').width;
let totalHeight = Dimensions.get('window').height;

export default class MainPage extends Component {

    state = {
        isRefresh: false,}

    _onRefresh() {
        console.log('onRefresh.');

        this.setState({isRefresh: true});
        // 模拟获取数据需要三秒
        setTimeout(() => this.setState({isRefresh: false}),3000);
    }

    _onScroll() {
        console.log('onScroll.');
    }

    render() {
        return (
            <View style={styles.container}> <Button title="滑动到底部" onPress={() => _outScrollView.scrollToEnd({animated: false})}/> <ScrollView  ref={(scrollView) => { _outScrollView = scrollView; }} contentContainerStyle={styles.outScrollView} onScroll={this._onScroll} //回调 scrollEventThrottle={100} // ios : 控制scroll回调的频率,没秒触发多少次 showsVerticalScrollIndicator={false} //设置不显示垂直的滚动条 keyboardDismissMode={'on-drag'} // 'none'默认值,滑动时不隐藏软件盘, // ‘on-drag'滑动时隐藏软件盘.interactive :ios可用。上滑可以回复键盘 keyboardShouldPersistTaps={'always'} //'never'默认值,点击TextInput以外的组件,软键盘收起, // 'always'不会收起,`handle` 当点击事件被子组件捕获时, //键盘不会自动收起,但我用android测试了,发现没有效果 //我使用的版本:RectNative 0.45 refreshControl={ <RefreshControl refreshing={this.state.isRefresh} onRefresh={this._onRefresh.bind(this)} title={'load...'} tintColor={'#ff0000'} colors={['#ff0000', '#00ff00','#0000ff']} progressBackgroundColor={'#ffff00'} /> } > <ScrollView horizontal={true} contentContainerStyle={styles.inScrollView} showsHorizontalScrollIndicator={false} //不显示滑动条 > <TextInput placeholder={'测试软键盘'} style={{width: totalWidth,height: 0.5 * totalHeight,backgroundColor: '#fff1ae'}}/> <View style={{width: totalWidth,backgroundColor: 'blue'}}/> </ScrollView> <ScrollView horizontal={true} contentContainerStyle={styles.inScrollView}> <TextInput placeholder={'测试软键盘'} style={{width: totalWidth,backgroundColor: 'blue'}}/> </ScrollView> <ScrollView horizontal={true} contentContainerStyle={styles.inScrollView}> <View style={{width: totalWidth,backgroundColor: 'red'}}/> <View style={{width: totalWidth,backgroundColor: 'blue'}}/> </ScrollView> <ScrollView horizontal={false} contentContainerStyle={styles.inScrollView}> <View style={{width: totalWidth,backgroundColor: 'blue'}}/> </ScrollView> </ScrollView> </View> ); } } var styles = StyleSheet.create({ container: { flex: 1,height: '50%',backgroundColor: 'grey',},outScrollView: { // flex: 1,这里指定flex的话,会出现不能上下滑动,原因在这样会把 "内容高度定死了",所以最好不要设置高度/flex,让内容的高度自适应 justifyContent: 'center',backgroundColor: 'green',inScrollView: { padding: 20,backgroundColor: '#88ff73' } }); AppRegistry .registerComponent( 'Project08',() => MainPage ) ;

上面的代码创建了一个ScrollView其中嵌套了4个ScrollView,有3个是横向滑动,最后一个是纵向滑动。ps:这里竟然没有滑动冲突,我想说:“666”,这要是android原生开的话,这种布局可是比较麻烦的。

基本属性

属性 作用
contentContainerStyle 设置内层容器的样式。what?什么是内层容器?这里我的理解是,它这个ScroolView中还包装着一个View,这里View包含有我们设置的Item,大家想想,我们在android原生开发中使用呢ScrollView的时候,内层是不是一般也要嵌套一个LinearLayout用来存放子View吧
onScroll 回调方法,滑动的时候回调
scrollEventThrottle 这个之后ios有效,用来设置onScroll滑动的频率,可以节省性能,类型:number.表示1秒回调多少次
showsVerticalScrollIndicator 这个用来设置是否显示垂直滚动条,和他相似的还有showsHorizontalScrollIndicator
showsHorizontalScrollIndicator 用来设置是否显示横向滑动条
keyboardDismissMode 用来设置软件盘滑动的时候,是否隐藏的模式,none(默认值),拖拽时不隐藏软键盘 on-drag 当拖拽开始的时候隐藏软键盘 interactive 软键盘伴随拖拽操作同步地消失,并且如果往上滑动会恢复键盘。安卓设备上不支持这个选项,会表现的和none一样
keyboardShouldPersistTaps ‘never’(默认值),点击TextInput以外的子组件会使当前的软键盘收起。此时子元素不会收到点击事件。’always’,键盘不会自动收起,ScrollView也不会捕捉点击事件,但子组件可以捕获 ‘handled’,当点击事件被子组件捕获时,键盘不会自动收起。这样切换TextInput时键盘可以保持状态。多数带有TextInput的情况下你应该选择此项,但我用android机测试的时候,发现没卵用
refreshControl 用来设置下拉刷新组件,这个组件下文将介绍

ok,这些是基本属性,更多属性大家可以参考:http://reactnative.cn/docs/0.45/scrollview.html#content

RefreshControl

这个组件我上文的代码中,大家应该已经看到用法了。

refreshControl={
    <RefreshControl refreshing={this.state.isRefresh} onRefresh={this._onRefresh.bind(this)} title={'load...'} tintColor={'#ff0000'} colors={['#ff0000','#0000ff']} progressBackgroundColor={'#ffff00'} /> }

需要的属性基本我都写上了,这里我再列个表格解释一下好了。

属性 作用
refreshing bool 类型,如果设置true,则下拉刷新按钮就一直显示着,如果设置false,就不显示,只有当下拉的时候显示
onRefresh 下拉回调
title 标题
tintColor 指定刷新指示器的颜色
colors 指定至少一种颜色用来绘制刷新指示器
progressBackgroundColor 指定刷新指示器的背景色

ListView

ListView是一个可以垂直滑动的组件,一般用来显示列表数据

用法

/** * Created by blueberry on 6/9/2017. * @flow */

import React,{Component} from 'react';
import {AppRegistry,Button} from 'react-native';
import StaticContainer from './StaticContainer';

let array = [];
{
    let len = 100;
    for (let i = 0; i < len; i++) {
        array.push('测试数据' + i);
    }
}

/** * 加个log,用来测试,是否更新。 */
class LogView extends Component {
    componentDidUpdate() {
        console.log(this.props.name + 'Did update');
    }

    render() {
        return (
            <Text style={{backgroundColor: '#ffd98c'}}> 我是:{this.props.name} </Text> ); } } export default class ListViewPage extends Component { constructor() { super(); let ds = new ListView.DataSource({rowHasChanged: (r1,r2) => r1 !== r2}); this.state = { //填充数据 dataSource: ds.cloneWithRows(array),}; } render() { return ( <ListView  // 数据源 dataSource={this.state.dataSource} initialListSize={10} //初始的时候显示数量 onChangeVisibleRows={(visible, changedRows) => { // 我用android测试,没有回调.... // visible: 类型:{ sectionID: { rowID: true }} // { sectionID: { rowID: true | false }} console.log('visible:' + JSON.stringify(visible)); console.log('changedRow:' + JSON.stringify(changedRows)); }} onEndReached={() => console.log('onEndReached')}//当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足 // onEndReachedThreshold个像素的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时, // 如果数据不足一屏(比如初始值是空的),这个事件也会被触发,请自行做标记过滤。 onEndReachedThreshold={2} //调用onEndReached之前的临界值,单位是像素 pageSize={3} //每次渲染的行数 // 返回一个头部可以渲染的组件 renderHeader={() => ( <LogView name="header"/> )} //返回一个尾部可以渲染的组件 renderFooter={() => ( <StaticContainer> <LogView name="Footer"/> </StaticContainer> )} //显示每一行 renderRow={ (rowData,sectionID,rowID,highlightRow) => { return ( <Text  style={{borderBottomColor: 'grey',borderBottomWidth: 1}}> {'rowData:' + rowData + ' sectionId:' + sectionID + " rowId:" + rowID + " highlightRow:" + highlightRow} </Text> ) }}/> ); } } AppRegistry.registerComponent('Project08',() => ListViewPage);

上面的代码实现了一个用来显示100条数据的列表,它还有一个头部,和一个尾部,因为头部和尾部的数据一般都不收布局变化,所有使用了一个StaticContainer来包装它,让他不刷新。这样做可以提高效率。为了看出效果,我特意定义了一个LogView组件,用来测试。

ListView.DataSource

ListView.DataSource 主要用来为ListView提供数据,它的一般用法。上面的代码已经给出了。

let ds = new ListView.DataSource({rowHasChanged: (r1,r2) => r1 !== r2});
this.state = {
    //填充数据
    dataSource: ds.cloneWithRows(array),};

它还有另外一个方法cloneWithRowsAndSections(dataBlob,sectionIdentities,rowIdentities) 用来填充分组数据。
使用详细可以参考:http://reactnative.cn/docs/0.45/listviewdatasource.html#content

基本属性

属性 作用
dataSource 数据源,上文已经说明
initialListSize 初始的时候显示数量
onChangeVisibleRows 当可见的行的集合变化的时候调用此回调函数。visibleRows 以 { sectionID: { rowID: true }}的格式包含了所有可见行,而changedRows 以{ sectionID: { rowID: true
onEndReached 当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的),这个事件也会被触发,请自行做标记过滤。
onEndReachedThreshold 调用onEndReached之前的临界值,单位是像素。
pageSize 每次事件循环(每帧)渲染的行数。
renderFooter 页头与页脚会在每次渲染过程中都重新渲染(如果提供了这些属性)。如果它们重绘的性能开销很大,把他们包装到一个StaticContainer或者其它恰当的结构中。页脚会永远在列表的最底部,而页头会在最顶部。
renderHeader 和renderFoot的用法一样
renderRow (rowData,highlightRow) => renderable 最重要的方法,用渲染每个item,其中rowData是你定义的数据列表中的类型数据,sectionID是该行的sectionID,rowId是该行的rowID,hightlighRow是一个函数引用,我目前没有发现卵用,官网说:如果item正在被高亮,可以通过hightlightRow(null) 来重置
scrollRenderAheadDistance 当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行
renderSectionHeader (sectionData,sectionID) => renderable 如果提供了此函数,会为每个小节(section)渲染一个粘性的标题
stickySectionHeadersEnabled设置小节标题(section header)是否具有粘性
stickyHeaderIndices 一个子视图下标的数组,用于决定哪些成员会在滚动之后固定在屏幕顶端

额,还有ScrollView有的属性,ListView都有;所有horizontal、refreshControl,都可以个ListView设置。
基本属性就这些,更多属性参考:http://reactnative.cn/docs/0.45/listview.html#content

分组显示

/** * Created by blueberry on 6/9/2017. * * @flow */

import React,Text} from 'react-native';

let array: Array<Array<string>> = [];
{
    for (let i = 0; i < 10; i++) {
        let group: Array<string> = [];

        for (let j = 0; j < 10; j++) {
            group.push('分组:' + i + " item:" + j);
        }
        array['分组' + i] = group;

    }
}

export default class GroupListView extends Component {

    constructor() {
        super();

        var ds = new ListView.DataSource({
            rowHasChanged: (r1,r2) => r1 !== r2,sectionHeaderHasChanged: (pre,next) => pre !== next,});



        this.state = {
            /** * 填充数据 * @params 所有的分组数据,结构{分组1:[分组1 item1,分组1item2. ...],分组2:[分组2item1,....]} * @param sectionIdentities 每个分组的索引 */
            dataSource: ds.cloneWithRowsAndSections(array,Object.keys(array)),};
    }

    render() {
        return (
            <ListView dataSource={this.state.dataSource} renderRow={(rowData) => { return <Text>{rowData}</Text>;
            }}
                      renderSectionHeader={(sectionData,sectionId) => {
                          console.log('sectionData:' + JSON.stringify(sectionData) + ',sectionID:' + JSON.stringify(sectionId));
                          return <Text style={{backgroundColor: 'red'}}>{sectionData[0]}</Text> }} stickySectionHeadersEnabled={true} //开启之后,会有个粘性效果, stickyHeaderIndices={[1]} //一个子视图下标的数组(这个下标连section也算在内的,),用于决定哪些成员会在滚动之后固定在屏幕顶端.根 // stickySectionHeadersEnabled的效果很像 scrollRenderAheadDistance={10} //当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行。 /> ); } } AppRegistry.registerComponent('Project08',() => GroupListView);

上面代码实现了用listView分组显示item。

FlatList

FlatList是ListView的升级版,它的性能比ListView好一些,但它目前刚出来,冒死还有些坑存在。。。

用法

/** * Created by blueberry on 6/11/2017. */

import React,{Component,PureComponent} from 'react';
import {AppRegistry,TouchableOpacity} from 'react-native';

class ListItem extends PureComponent {
    _onPress = () => {
        this.props.onPressItem(this.props.id);
    }

    render() {
        let color = this.props.selected ? 'red' : 'blue';
        return (
            <TouchableOpacity  style={{height: 200,justifyContent: 'center',flex: 1,alignItems: 'center',backgroundColor: color} } onPress={this._onPress}> <Text style={{fontSize: 20,height: 100,}}>{this.props.title}</Text> <Text style={{fontSize: 18,height: 80}}>{this.props.content}</Text> </TouchableOpacity> ); } } /** * PureComponent 可以提高性能,只有在props或state发生改变时render。 */ export default class FlatListPage extends PureComponent { state = {selected: (new Map(): Map<string, boolean>)}; _onPressItem = (id: string) => { this.setState((state) => { const selected = new Map(state.selected); selected.set(id,!selected.get(id));//toggle. return {selected}; }); }; _keyExtractor = (item,index) => item.id; /** * 使用箭头函数,既保证了this指向FlatListPage,也保证了不会每次都生成一个新的函数,这样在对比prop时,就返回'没有改变' */ _renderItem = ({item}) => ( <ListItem  id={item.id} onPressItem={this._onPressItem} selected={!!this.state.selected.get(item.id)} title={item.title} content={item.content} /> ); render() { return ( <FlatList  data={this.props.data} renderItem={this._renderItem} // extraData={this.state} keyExtractor={this._keyExtractor} ItemSeparatorComponent={() => <View style={{height: 2,backgroundColor: 'black'}}/>} //分割线 ListFooterComponent={() => <View style={{height: 50,backgroundColor: 'red'}}/>} //尾部布局 ListHeaderComponent={() => <View style={{height: 50,backgroundColor: 'blue'}}/>} //头部布局 columnWrapperStyle={{height: 200,}} numColumns={2} // //getItemCount={40} //getItemLayout={(data,index) => ({length: 200,offset: 200 * index,index})} refreshing={false} onEndReachedThreshold={20} //决定距离底部20个单位的时候,回到onEndReacted,但是我这只20, // 他距离4000左右的时候就回掉了,测试版本Android reactNative Api:0.45 onEndReached={(info) => { console.log('onEndReacted:' + info.distanceFromEnd); }} /> ); } } { let array = []; for (let i = 0; i < 40; i++) { array[i] = {id: i,key: 'key' + i,title: '标题' + i,content: '内容' + i,}; } FlatListPage.defaultProps = {data: array}; } AppRegistry.registerComponent('Project08',() => FlatListPage);

常用属性

属性 作用
ItemSeparatorComponent 行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后
ListFooterComponent 设置尾部组件
ListHeaderComponent 设置头部组件
columnWrapperStyle 如果设置了多列布局(即将numColumns值设为大于1的整数),则可以额外指定此样式作用在每行容器上。
data 为了简化起见,data属性目前只支持普通数组。如果需要使用其他特殊数据结构,例如immutable数组,请直接使用更底层的VirtualizedList组件。
extraData 如果有除data以外的数据用在列表中(不论是用在renderItem还是Header或者Footer中),请在此属性中指定。同时此数据在修改时也需要先修改其引用地址(比如先复制到一个新的Object或者数组中),然后再修改其值,否则界面很可能不会刷新。
keyExtractor此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标
numColumns 多列布局只能在非水平模式下使用,即必须是horizontal={false}。此时组件内元素会从左到右从上到下按Z字形排列,类似启用了flexWrap的布局。组件内元素必须是等高的——暂时还无法支持瀑布流布局

更多属性请参考: http://reactnative.cn/docs/0.45/flatlist.html#content

上述代码定义的组件都继承了 PureComponent,这个组件的作用是,只有prop和state它才render。实现它是为了提高效率。

SectionList

是一个高性能的分组列表组件

使用

/** * Created by blueberry on 6/12/2017. */

import React,SectionList,RefreshControl} from 'react-native';

class ListItem extends PureComponent {

    render() {
        return (
            <Text style={{backgroundColor: 'red',height: 100}}>{this.props.title}</Text> ); } } let sections = []; for (let i = 0; i < 10; i++) { data = []; for (let j = 0; j < 10; j++) { data[j] = {title: '分组' + i + ',item' + j,id: j}; } sections.push({data: data,key: '分组' + i}); // 也可以使用下面方式自定义 不同section渲染不同类型的子组件 //sections.push({data: data,key: '分组' + i,renderItem: i === 2 ? ()=><ListItem title="测试"/> : undefined}); } export default class SectionListPage extends PureComponent { //为每一行生成唯一的key _keyExtractor = (item,index) => '' + item.key + index; render() { console.log(JSON.stringify(sections)); return ( <SectionList  //渲染item的组件 renderItem={({item}) => <ListItem  title={item.title} /> } //渲染sectionHeader的组件 renderSectionHeader={({section}) => <Text>{section.key}</Text> } //数据 sections={sections} //生成唯一的key keyExtractor={this._keyExtractor} ItemSeparatorComponent={() => <View style={{height: 2,backgroundColor: 'blue'}}/>} //头部布局 /> ); } } AppRegistry.registerComponent('Project08',() => SectionListPage);

基本属性

属性 作用
renderItem 和FlatList的renderItem作用一样。用来设置渲染item的组件,但SectionList,也可以section数据源中设置renderItem这个属性
renderSectionHeader 设置渲染分组小标签的组件
seciton 设置数据源

其余属性FlatList中的属性作用一样,这里就不在介绍了。

我在上述代码中有这么一行,用来设置不同的renderItem函数,读者可以去掉注释看看效果
// sections.push({data: data,key: '分组' + i,renderItem: i === 2 ? ()=><ListItem title="测试"/> : undefined});

总结

其实我们文章中我们主要提到了 6个组件:ScrollView ListView RefreshControl FlatList SectionList PureComponent;其中主要讲解了四个滑动组件
- ScrollView
它没有懒加载功能,适合少量数据显示用法比较简单。
- ListView
它用来显示列表数据,是懒加载Item,支持下拉刷新,上拉加载应该用它的onEndReached也是可以办到的。也支持分组列表显示,分组标签粘性滑动等功能性能比较好。设置数据需要结合ListView.DataSource组件。
- FlatList
可以说是ListView的升级版,性能比ListView要好,同样支持下拉刷新等功能,目前我用0.45版本,刚出来,官方说还不稳定。
- SectionList
用来显示分组列表组件,性能比较高。

上面就是本文介绍的四个滑动组件,他们都支持下拉刷新组件,(ScrollView)有的属性,其他滑动组件基本都有。
RefreshControl就是官方给出的下拉刷新组件,用来设置到滑动组件上。
PureComponent之后当state或props变了之后,才能刷新。可以提高性能

ok,介绍到这里了,其中我写的测试源码,在上文都贴出来了,大家可以测试测试。

参考

ReactNative 官网:http://facebook.github.io/react-native/releases/0.43/docs/flatlist.html
ReactNative 中文网:http://reactnative.cn/docs/0.45/sectionlist.html#content

原文链接:https://www.f2er.com/react/303788.html

猜你在找的React相关文章