React Native  带进度条横向滚动 本篇参照  

react native android 应用内apk 版本升级进度监听_Text

 

 

我是用 React Hooks写的 这里贴出要注意的地方:

(1)重新计算 marLeftAnimated 时,监听ScrollView的滚动事件。如果滚动了把一个自定义量的值改变,只要这个值改变了,就说明滚动了,滚动就重新计算 marLeftAnimated

react native android 应用内apk 版本升级进度监听_ci_02

 

 

react native android 应用内apk 版本升级进度监听_Text_03

 

 

react native android 应用内apk 版本升级进度监听_表数据_04

 

 防止别人的路径删了。我这里把我的代码放上来。

index.tsx

// 社区Tab下 协会Tab页
import React, { ReactNode, useEffect, useState } from 'react';
import { ImageSourcePropType } from 'react-native';
import { connect } from 'react-redux';
import { View, Colors, Text, Image } from 'react-native-ui-lib';
import { DispatchPro, RootState } from '../../../store';
import { FlatList, LineSpace, Touchable, WidthSpace } from '../../../components';
import { handleAssociationList } from './config';
import { IndicatorScrollView } from '../components/indicatorScrollView';
import { deviceWidth, navigate } from '../../../utils';
import { IDaynamicItem } from '../../../models/community/ICommunity.module';
import DaynamicItem from '../components/daynamicItem';
import configs from '../../../configs';

type IAssociation = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;
const Association = (props: IAssociation) => {
  const { fetchAssociationList, fetchAssociationDaynamicList, associationList } = props;
  const columnLimit = 5;  //option列数量
  const rowLimit = 2;  //option行数量
  const [daynamicList, setDaynamicList] = useState<IDaynamicItem[]>([]); // 动态列表数据
  const [currentPageNo, setCurrentPageNo] = useState(0); // 动态列表分页
  // 页面初始化时,请求数据
  const initRequest = () => {
    fetchAssociationList({
      params: {
      },
      apiName: 'fetchAssociationList',
    });
  };
  useEffect(() => {
    initRequest();
  }, []);
  // 请求动态列表数据,距离底部还有0.2 时触发 注意此参数是一个比值而非像素单位。比如,0.5 表示距离内容最底部的距离为当前列表可见长度的一半时触发。
  const fetchDataFn = React.useMemo(() => new Promise((resolve) => {
    fetchAssociationDaynamicList
      && fetchAssociationDaynamicList({
        params: { pageNo: currentPageNo + 1, pageSize: 10, pageTypeId: '2', associationId: '0' },
        apiName: 'fetchDaynamicList',
      }).then(res => {
        setDaynamicList([...daynamicList, ...(res?.shareInfoList || [])]);
        resolve({ pageNo: currentPageNo + 1, totalCount: res?.totalCount, results: [...daynamicList, ...(res?.shareInfoList || [])] });
      });
  }), [currentPageNo]);

  const pageToNextFn = () => {
    setCurrentPageNo(currentPageNo + 1);
  };

  // 点击单个协会,跳转至协会详情页
  const onPress = (id: string, title: string) => {
    navigate('Webview', {
      url: `${configs.reactUrl}/associationDetail?associationId=${id}`,
      title,
    });
  };



  // 横向滚动协会里面的内容 option
  const renderOption = () => {
    const size = (deviceWidth - 20) / columnLimit;  // 每个option的宽度
    const optionTotalArr: ReactNode[] = [];  //存放所有option样式的数组
    //根据行数,声明用于存放每一行渲染内容的数组
    for (let i = 0; i < rowLimit; i++) optionTotalArr.push([]);
    handleAssociationList(associationList).map((item, index) => {
      let rowIndex = 0;  //行标识
      if (index < columnLimit * rowLimit) {
        //没超出一屏数量时,根据列数更新行标识
        rowIndex = parseInt(String(index / columnLimit));
      } else {
        //当超出一屏数量时,根据行数更新行标识
        rowIndex = index % rowLimit;
      }
      (optionTotalArr[rowIndex] as ReactNode[]).push(
        <Touchable key={index} onPress={() => onPress(item.linkParams, item.text)} activeOpacity={0.7} style={{ marginTop: 21, justifyContent: 'center', alignItems: 'center' }} >
          <View style={{ width: size, alignItems: 'center', justifyContent: 'center' }}>
            <Image style={{ width: 36, height: 36, borderRadius: 18 }} source={item.image as ImageSourcePropType} />
            <LineSpace height={6} />
            <Text l21>{item.text}</Text>
          </View>
        </Touchable>,
      );
    });

    return (
      <View
        style={{ flex: 1, justifyContent: 'center', paddingHorizontal: 10 }}
      >
        {
          optionTotalArr.map((item: ReactNode, index: number) => {
            return <View key={index} style={{ flexDirection: 'row' }}>{item}</View>;
          })
        }
      </View>
    );
  };
  return (
    <FlatList
      fetchDataFn={fetchDataFn}
      pageToNextFn={pageToNextFn}
      triggerPageNo={currentPageNo}
      keyExtractor={(item: IDaynamicItem) => item.shareId}
      renderItem={({ item }: { item: IDaynamicItem }) => {
        return <View>
          <LineSpace height={15} />
          <View row>
            <WidthSpace width={15} />
            <DaynamicItem data={item} />
          </View>
        </View>;
      }}
      ListHeaderComponent={<>
        <View style={{ alignItems: 'center' }} >
          <IndicatorScrollView
            containerStyle={{ flex: 1, width: (deviceWidth - 20) / columnLimit }}
            indicatorBgStyle={{ marginBottom: 10, borderRadius: 2, width: 30, height: 4, backgroundColor: Colors.greyDD }}
            indicatorStyle={{ borderRadius: 2, height: 4, backgroundColor: Colors.primaryColor }}
          >
            {renderOption()}
          </IndicatorScrollView>
        </View >
      </>}
    />
  );
};

const mapStateToProps = ({
  community: { associationList, associationDaynamicList, associationDaynamicLoading },
}: RootState) => ({ associationList, associationDaynamicList, associationDaynamicLoading });
const mapDispatchToProps = ({
  community: { fetchAssociationList, fetchAssociationDaynamicList },
}: DispatchPro) => ({
  fetchAssociationList,
  fetchAssociationDaynamicList,
});

export default connect(mapStateToProps, mapDispatchToProps)(Association);

 

 

 IndicatorScrollView .tsx

import React, { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react';
import { View, ScrollView, Animated } from 'react-native';
import { Colors } from 'react-native-ui-lib';
import { LineSpace } from '../../../../components';
import { deviceWidth } from '../../../../utils';
type IProps = {
  children: ReactNode; // 展示的内容
  containerStyle: CSSProperties; // 容器样式
  indicatorBgStyle: CSSProperties; //滚动条框样式
  indicatorStyle: CSSProperties; //滚动条样式
};
// 协会 横向滚动,根据自己定义的列数和行数,确定展示的数据
export const IndicatorScrollView = (props: IProps) => {
  const defaultProps = {
    containerStyle: { width: deviceWidth, backgroundColor: Colors.white },
    style: {},
    indicatorBgStyle: {
      marginBottom: 10,
      borderRadius: 2,
      width: 30,
      height: 4,
      backgroundColor: Colors.greyDD,
    },
    indicatorStyle: {
      borderRadius: 2,
      height: 4,
      backgroundColor: Colors.primaryColor,
    },
  };
  //滑动偏移量
  const scrollOffset = new Animated.Value(0);

  //显示滑动进度部分条的长度
  const [barWidth, setBarWidth] = useState(defaultProps.indicatorBgStyle.width / 2);
  const [childWidth, setChildWidth] = useState(defaultProps.containerStyle.width); //ScrollView子布局宽度
  const [scrollMark, setScrollMark] = useState(0); //设置协会横向滚动的标识,值变了说明滚动了,滚动了就重新计算marLeftAnimated
  const marLeftAnimated: React.MutableRefObject<any> = useRef(); //蓝色滚动条距离左边的位置、
  // 横向滚动触发
  const animatedEvent = Animated.event([
    {
      nativeEvent: {
        contentOffset: { x: scrollOffset },
      },
    },
  ]);
  useEffect(() => {
    //内容可滑动距离
    const scrollDistance = childWidth - defaultProps.containerStyle.width;
    if (scrollDistance > 0) {
      const _barWidth =
        (defaultProps.indicatorBgStyle.width * defaultProps.containerStyle.width) / childWidth;
      setBarWidth(_barWidth);
      //显示滑动进度部分的距左距离
      const leftDistance = defaultProps.indicatorBgStyle.width - _barWidth;
      const newscrollOffset = scrollOffset;
      marLeftAnimated.current = newscrollOffset.interpolate({
        inputRange: [0, scrollDistance], //输入值区间为内容可滑动距离
        outputRange: [0, leftDistance], //映射输出区间为进度部分可改变距离
        extrapolate: 'clamp', // 绑定动画值到指定插值范围
      });
    }
  }, [scrollMark, childWidth]);
  return (
    <View style={{ flex: 1, ...defaultProps.containerStyle }}>
      <ScrollView
        horizontal={true} //横向
        alwaysBounceVertical={false}
        alwaysBounceHorizontal={false}
        showsHorizontalScrollIndicator={false} //自定义滑动进度条,所以这里设置不显示
        scrollEventThrottle={0.1} //滑动监听调用频率
        onScroll={(e) => { animatedEvent(e); setScrollMark(scrollMark + 1); }} //滑动监听事件,用来映射动画值
        scrollEnabled={childWidth - defaultProps.containerStyle.width > 0 ? true : false}
        onContentSizeChange={(width) => {
          if (childWidth != width) {
            setChildWidth(width);
          }
        }}
      >
        {props.children ?? <View style={{ flexDirection: 'row' }}>{props.children}</View>}
      </ScrollView>
      {childWidth - defaultProps.containerStyle.width > 0 ? (
        <>
          <LineSpace height={15} />
          <View style={[{ alignSelf: 'center' }, defaultProps.indicatorBgStyle]}>
            <Animated.View
              style={{
                position: 'absolute',
                width: barWidth,
                top: 0,
                left: marLeftAnimated.current,
                ...defaultProps.indicatorStyle,
              }}
            />
          </View>
          <LineSpace height={14} />
        </>
      ) : null}
    </View>
  );
};