1.0.5 • Published 4 years ago

react-native-viewpager-list v1.0.5

Weekly downloads
2
License
MIT
Repository
github
Last release
4 years ago

安装

yarn add react-native-viewpager-list

(当前仅支持 android,安装完成后无需任何配置)

使用

import {ViewPager, ViewPagerList} from 'react-native-viewpager-list';


<ViewPager {...props}>
    <View />
    <View />
    <View />
</ViewPager>


<ViewPagerList {...props}/>

支持 ViewPagerViewPagerList 两种方式;前者较为方便,有多少个子 view 就有多少个 page;后者主要使用在有很多 page 的情况,比如类似抖音那样,无限下滑。

通用 props

<ViewPagerList
   disableSwipe={false}  //禁用滑动(仍可通过API滑动,但用户不可操作)
   disableWave={false} //是否禁用 滑动到边缘时继续拖拽的水波纹效果
   currentIndex={0}  //当前显示 page
   horizontal={false}  //是否为横向(默认为纵向)
   loop={false}      //是否循环展示
   offscreenPageLimit={0}  //离屏(预加载) page 个数, 
                           //即当前 page 的前后提前渲染的 page 个数
                           //对于 ViewPagerList 创建后不可不可修改, ViewPager 无此限制

   autoplay={false}  //是否自动播放
   autoplayTimeout={2500}  //自动播放时间间隔
   autoplayDirection={false} //自动播放是否仅沿着一个方向(即播放到结尾后停止)

   transformer="|card|zoomOut|depth"   //页面过渡效果,默认为空
   scale={0.85}     //过渡效果: 渐隐的最小缩放比例
   alpha={0.75}     //过渡效果: 渐隐的最小透明度 (depth效果不支持)
   padding={int}  // card 过渡效果: 一屏显示多个, padding 设置 主 page 两端间距
   maring={int}   // margin 设置两端(前后) page 的显示尺寸
   
   //当前显示 page 的背景组件
   //利用该属性可实现类似抖音效果, 背景组件可设置为一个 Video 组件
   //这也可避免滑动过程中更新 子view 导致视频组件不断的销毁与重建
   //当然也可在其他性能敏感的场景使用
   //该参数仅在首次有效, 创建后更改无效
   getBackground={ReactComponent|Function} 

    // 下拉刷新, 与 ScrollView 使用方式基本一致, 仅支持垂直视图
    refreshControl={
        <RefreshControl
            tintColor=""
            onRefresh={this._onRefresh}
        />
    }

    //page状态发生改变,在滑动开始/滑动松开/滑动完成等节点触发
    onPageScrollStateChanged={({state}) => {}}  
    //滑动过程的回调
    onPageScroll={({position, offset, offsetPixels}) => {}}
    //页面切换后触发(此时是刚松开手指, 即将惯性滑动到 position 页面)
    onPageSelected={({position}) => {}}
    //页面切换完成后触发
    onPageChanged={({position}) => {}}
/>

refreshControl 说明

与 ScrollView 不同之处在于,不需要设置 refreshing 属性,若不使用 RN 自带的 refreshControl 刷新器,需保证自行使用的刷新器至少要支持 refreshingenabledonRefresh 三个属性(若不支持的话,可考虑套一层 wrapper)。回调函数会有一个函数参数,完成数据更新后,只需要调用一下即可,使用方法如下

_onRefresh = (resolve) => {
    setTimeout(() => {
        resolve()
    }, 3000);
}

通用 API

以下 api 属于共用接口,ViewPagerViewPagerList 都支持

getCount

// 获取当前页面数量
int count = this.refs.pager.getCount();

setCurrentIndex

// 滑动到指定页面, smooth=true, 使用过渡效果, 反之不使用
this.refs.pager.setCurrentIndex(index, smooth);

getCurrentIndex

// 获取当前正在显示的 page index
int index = this.refs.pager.getCurrentIndex()

disable

// 禁用或启用当前 viewpager, 禁用后仍可通过 api 切换页面
// 但用户不可拖拽操作了, 与直接修改 props.disableSwipe 效果相同
// 不过性能更好一点
this.refs.pager.disable(disabled)

beginFakeDrag / fakeDragBy / endFakeDrag

// 模拟手势, 暂时没想到使用场景, 但 viewpager2 提供了, 索性也暴露给 js
// 需注意: beginFakeDrag 和 endFakeDrag 必须成对出现, 否则会闪退
const pager = this.refs.pager;
pager.beginFakeDrag();
pager.fakeDragBy(30);
pager.endFakeDrag();

ViewPagerList 专用

ViewPagerList 的 page 不支持直接使用子 view,而是使用类似 FlatList 的方式,由 数据 和 渲染函数动态创建 page,对于数据较多的情况,可大幅提升性能。

<ViewPagerList
    initData={Array}  //绑定数据
    renderItem={Function}  //page渲染函数

    ref="pager"
/>

renderItem

// 创建第 index 个 page 的 view
// item 为 initData 中下标为 index 的数据
renderItem = (item, index) => {
    return <View>{item.balbla}</View>
}

修改数据可以直接设置 props.initData,但 ViewPagerList 的设计初衷是出于性能考虑,所以不建议直接设置,而是根据实际情况使用以下 API 来操作数据

const pager = this.refs.pager;

// 更新列表数据
// 以下四个函数可额外指定一个 selected 参数
// 在更新完数据后, 将 viewpager 定位到指定页面
// 可缺省, 即不改变当前的 selected page
//---------------------------------------------------

// 追加一批数据
pager.push(Array, selected)

// 在 index 位置插入 data (index 本身也会被替换)
// index 可缺省, 默认为 0, 即插入到开头
pager.insert(Array, index, selected)

// 从 index 位置开始移除 length 个(含 index)
// length 可缺省, 默认为 1
pager.remove(Array, index, selected)

// 一次性更新所有数据, 可放心使用, 会自动按需更新
pager.update(Array, selected)


// 获取/更新 指定 page 的数据
//---------------------------------------------------

// 获取所有 page 数据
Array list = pager.getItem();

// 获取下标 index 的 page 数据
Object item = pager.getItem(index);

// 更新指定 page 的数据
pager.updateItem(index, data);

// 强制更新指定 page 的视图(即使其 data 未变动), 会触发 renderItem
pager.renderItem(index);


// 针对当前正在显示 page 的操作
//---------------------------------------------------

// 获取当前 page 的数据
// 等价于 pager.getItem(this.getCurrentIndex())
Object item = pager.getCurrentItem();

// 更新当前 page 的数据
// 等价于 pager.updateItem(this.getCurrentIndex(), data)
pager.updateCurrentItem(data);

API 使用注意事项

// getItem / getCurrentItem 获取到的数据为原始数据, 并非拷贝的数据
const item = this.refs.pager.getCurrentItem();

// 不会触发 renderItem 重新渲染 page, 但在 page 回收后再次重新创建时,
// 会触发 renderItem 重新渲染, 回收时机由 offscreenPageLimit 决定
item.foo = "foo";

// 同上, 此时仍不会触发, 相当于设置了 item=item, 无法触发 RN 的 props diff 
item.foo = "foo";
this.refs.pager.updateItem(
    this.refs.pager.getCurrentIndex(),
    item
);

// 以下方式可立即更新
this.refs.pager.updateItem(
    this.refs.pager.getCurrentIndex(),
    {...item, foo:"foo"}
);

// 这样设计的原因是为了应对以下场景
// 1. 已通过其他方式更新完了 page, 仅需改变数据以便下次正确渲染
// 2. 没更新 page, 通过改变数据触发 renderItem 立即重新渲染

额外说明

Page 内部也有可能也有 touchmove 相关的组件, 所以 viewpager 默认并未阻止该类型事件,但这会给以下使用场景造成困扰

import React from 'react';
import {View, TouchableOpacity} from 'react-native';
import {ViewPager} from 'react-native-viewpager-list';

class App extends React.Component {
  render(){
    return <ViewPager>
        <View>
            <TouchableOpacity onPress={() => {}}/>
        </View>
    </ViewPager>
  }  
}

按住 TouchableOpacity 拖拽切换 ViewPager 到下一个页面,此时会触发 onPress 事件,这并不符合期望;实际所需要的是点击 TouchableOpacity 且不发生页面切换,才响应 onPress 事件,可通过以下方式解决。

class App extends React.Component {
  
  _pageState = 0;
  _onPageStateChange = ({state}) => {
      this._pageState = state;
  }  

  // _pageState !== 0 意味着发生了拖拽, 此时阻止子视图响应 touch 事件
  _onMoveShouldSetResponderCapture = () => {
      return this._pageState !== 0;
  }
 
  render(){
    return <ViewPager
        onPageScrollStateChanged={this._onPageStateChange}
        onMoveShouldSetResponderCapture={this._onMoveShouldSetResponderCapture}
    >
        <View>
            <TouchableOpacity onPress={() => {}}/>
        </View>
    </ViewPager>
  }  
}
1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago