【React-native】 实现页面中间部分吸顶效果

在FlatList 和ScrollView 中有一个stickyHeaderIndices 可以轻松实现吸顶效果。

<ScrollView
  	showsVerticalScrollIndicator={false}
 	style={styles.container}
 	ListHeaderComponent={...}
  	stickyHeaderIndices={[0]}//第一个子元素即头部组件,上滑时吸顶 	
/>

由于头部组件是一个整体,无法单独使组件内的元素吸顶,不满足我的需求。
在这里插入图片描述

实现方案1–来源于网上

实现的图中第二部分吸顶功能的核心代码

import * as React from 'react';
import { StyleSheet, Animated } from "react-native";

/**
 * 滑动吸顶效果组件
 * @export
 * @class StickyHeader
 */
export default class StickyHeader extends React.Component{

  static defaultProps = {
    stickyHeaderY: -1,
    stickyScrollY: new Animated.Value(0)
  }
  
  constructor(props) {
    super(props);
    this.state = {
      stickyLayoutY: 0,
    };
  }
  // 兼容代码,防止没有传头部高度
  _onLayout = (event) => {
    this.setState({
      stickyLayoutY: event.nativeEvent.layout.y,
    });
  }

  render() {
    const { stickyHeaderY, stickyScrollY, children, style } = this.props
    const { stickyLayoutY } = this.state
    let y = stickyHeaderY != -1 ? stickyHeaderY : stickyLayoutY;
    const translateY = stickyScrollY.interpolate({
      inputRange: [-1, 0, y, y + 1],
      outputRange: [0, 0, 0, 1],
    });
    return (
      <Animated.View
        onLayout= { this._onLayout }
        style = {
          [
            style,
            styles.container,
            { transform: [{ translateY }] }
          ]}
      >

      { children }

      </Animated.View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    zIndex: 100
  },
});

页面里实际用法如下:

// 在页面constructor里声明state
this.state = {
  scrollY: new Animated.Value(0),
  headHeight:-1
};


<Animated.ScrollView 
  style={{ flex: 1 }}
  onScroll={
    Animated.event(
      [{
        nativeEvent: { contentOffset: { y: this.state.scrollY } } // 记录滑动距离
      }],
      { useNativeDriver: true }) // 使用原生动画驱动
  }
  scrollEventThrottle={1}
>

  <View onLayout={(e) => {
    let { height } = e.nativeEvent.layout;
    this.setState({ headHeight: height }); // 给头部高度赋值
  }}>
    // 里面放入第一部分组件
  </View>
  
  <StickyHeader
    stickyHeaderY={this.state.headHeight} // 把头部高度传入
    stickyScrollY={this.state.scrollY}  // 把滑动距离传入
  >
    // 里面放入第二部分组件
  </StickyHeader>
  
  // 这是第三部分的列表组件
  <FlatList
    data={this.state.dataSource}
    renderItem={({item}) => this._createListItem(item)}
  />
  
</Animated.ScrollView>

但是将这FlatList/SectionList中的任何一个放在 ScrollView 中,它们将无法计算当前窗口的大小,而是会尝试渲染所有内容,这可能会导致性能问题。

实现方案2–个人想法

使用SectionList分组列表,ListHeaderComponent头部组件放置不需吸顶但需要跟着上滑的部分,renderSectionHeader放需要吸顶的部分组件,且包含空列表时需展示的组件,renderSectionFooter根据是请求下一页还是没有更多了进行显示。

<SectionList
          ref={(c) => {
            this.longlist = c
          }}
          style={[comStyles.box, { height: '100%' }]}
          sections={[{ data: list }]}
          renderItem={({ item, index }) => this.renderItem(item)}
          onEndReached={() => {
            if (page * 5 < total) {
              this.setState({ isNext: true })
              this.getList('next')
            }
          }}
          onRefresh={() => this.init(true)}
          refreshing={refreshing}
          stickySectionHeadersEnabled={true} //安卓需手动设置
          onEndReachedThreshold={0.01}
          ListHeaderComponent={this.renderSwiper()}
          renderSectionHeader={() => {
            return (
              <>
                <Tabs
                  tabs={cateList}
                  isScroll
                  selTab={cateId}
                  onChange={(item) => this.setState({ cateId: item.value })}
                  itemStyle={{ marginRight: scaleSize(20) }}
                  style={{ backgroundColor: '#f5f5f5' }}
                />
                {!list?.length && <DefaultPage style={{ height: 200 }} />}
              </>
            )
          }}
          renderSectionFooter={() => {
            return isNext ? <ActivityIndicator size='small' color='#000' /> : page * 5 < total || !total ? <View /> : <Text style={[comStyles.fontGrayS, comStyles.textCenter, { padding: 10 }]}>没有更多了~</Text>
          }}
        />

注意:onEndReached需要判断是否进行请求,否则会造成循环。


方案一缺点:一次渲染所有数据,造成性能问题

方案二缺点:对于复杂的数据,如不同tab显示不同列表,且不同操作,后期维护较为麻烦。

有更好的方法欢迎提出,谢谢!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-雾里-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值