/* * @Author: your name * @Date: 2022-04-20 10:05:31 * @LastEditTime: 2022-04-20 19:53:00 * @LastEditors: Please set LastEditors * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @FilePath: /YMJRN/src/common/headtableview/ScrollTabView.tsx */ import React, { Component, RefObject } from 'react'; import { View, Animated, ScrollView, Dimensions, Platform, StyleSheet, ViewStyle, TextStyle, InteractionManager, PanResponder, } from 'react-native'; import SceneComponent from './SceneComponent'; interface Props { childrenScrollY?:(y:number) => void; renderScrollHeader?: (p: any) => React.ReactElement; renderLeftView?:(p:any) => React.ReactElement; rightBgView?:(p:any) => React.ReactElement; insetValue?: number; // 状态栏的高度,也就是TabBar距离顶部状态栏的距离 headerHeight?: number; style?: ViewStyle; contentProps?: object; rightViewStyle?:object; LRViewStyle?:object; } const DEFAULT_PROPS = { insetValue: 0, contentProps: {}, }; const IS_IOS = Platform.OS === 'ios'; const dw = Dimensions.get('window').width; const dh = Dimensions.get('window').height; export default class ScrollHeadTabView extends Component { static defaultProps = DEFAULT_PROPS; constructor(props: Props) { super(props); const containerWidth = dw; const containerHeight = dh; const containerOffsetY = new Animated.Value(0); this.state = { containerOffsetY, containerWidth, containerHeight, }; this._contentScrollArr = []; this._headScrollPanY = 0; this._panResponder = PanResponder.create({ // 要求成为响应者: //this.refs[RefsConstant.HOME_DEV_LIST].setRefreshFunc(false); onStartShouldSetPanResponder: (evt, gestureState) => false, onStartShouldSetPanResponderCapture: (evt, gestureState) => false, onMoveShouldSetPanResponder: (evt, gestureState) => false, onMoveShouldSetPanResponderCapture: (evt, gestureState) => { let needMove=false; if(Math.abs(gestureState.dy)>0 ){ needMove = true; } if (this.props.locked) { needMove = false; } if (Math.abs(gestureState.dy) > Math.abs(gestureState.dx)&&needMove) { return true; } }, onPanResponderGrant: (evt, gestureState) => { // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情! console.log('onPanResponderGrant'); // gestureState.{x,y} 现在会被设置为0 if ((gestureState.dy < 0) || (gestureState.dy > 0)) { let index = 0; let scrollOffsetY = (this._contentScrollArr[index]&&this._contentScrollArr[index].scrollOffsetY)?this._contentScrollArr[index].scrollOffsetY:0; this._headScrollPanY = Math.min(Math.abs(scrollOffsetY?scrollOffsetY:0),this.props.headerHeight); } }, onPanResponderMove: (evt, gestureState) => { // 最近一次的移动距离为gestureState.move{X,Y} console.log('onPanResponderMove'); console.log('滑动参数:dx='+gestureState.dx +',dy='+gestureState.dy); // 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y} if ((gestureState.dy < 0) || (gestureState.dy > 0)) { let fromY = this._headScrollPanY||0; this.goToScroll({x:0,y:fromY-(gestureState.dy)}); } }, //是否可以释放响应者角色让给其他组件 onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderRelease: (evt, gestureState) => { // 用户放开了所有的触摸点,且此时视图已经成为了响应者。 console.log('onPanResponderRelease'); // 一般来说这意味着一个手势操作已经成功完成。 if ((gestureState.dy < 0) || (gestureState.dy > 0)) { let index = 0; let scrollOffsetY = (this._contentScrollArr[index]&&this._contentScrollArr[index].scrollOffsetY)?this._contentScrollArr[index].scrollOffsetY:0; this._headScrollPanY = Math.min(Math.abs(scrollOffsetY?scrollOffsetY:0),this.props.headerHeight); } }, onPanResponderTerminate: (evt, gestureState) => { // 另一个组件已经成为了新的响应者,所以当前手势将被取消。 console.log('onPanResponderTerminate'); }, onShouldBlockNativeResponder: (evt, gestureState) => { // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者 // 默认返回true。目前暂时只支持android。 return true; }, }); } componentDidMount() { // Fix(ViewPager version > 5.X)initialPage does not work on Android with Hermes } componentDidUpdate(prevProps) { } componentWillUnmount() { } goToScroll = (positon={x:0,y:0}) => { this.props.childrenScrollY&&this.props.childrenScrollY(positon.y); let index = 0; let scrollOffsetY = (this._contentScrollArr[index]&&this._contentScrollArr[index].scrollOffsetY)?this._contentScrollArr[index].scrollOffsetY:0; if (positon.y > this.props.headerHeight && this.scrollOffsetY < this.props.headerHeight) { this._contentScrollArr[index]&&this._contentScrollArr[index].scrollTo&&this._contentScrollArr[index].scrollTo({y:this.props.headerHeight});; } else { this._contentScrollArr[index]&&this._contentScrollArr[index].scrollTo&&this._contentScrollArr[index].scrollTo({y:positon.y});; } }; //渲染关联视图 renderLeftView = () => { const { renderScrollHeader, headerHeight ,renderLeftView} = this.props; const { containerOffsetY, containerWidth } = this.state; if (!renderLeftView) return null; return ( {renderLeftView()} ); }; //渲染关联视图 renderRightView = () => { const { containerOffsetY, containerWidth } = this.state; const { rightViewStyle,headerHeight,rightBgView} = this.props; return {rightBgView&& {rightBgView()} } {this.renderScrollableContent()} }; // 渲染可滑动头部 renderHeader = () => { const { renderScrollHeader, headerHeight } = this.props; const { containerOffsetY, containerWidth } = this.state; if (!renderScrollHeader) return null; return ( {renderScrollHeader()} ); }; _shouldRenderSceneKey = (idx, currentPageKey) => { let numOfSibling = 0; return idx < currentPageKey + numOfSibling + 1 && idx > currentPageKey - numOfSibling - 1; }; _keyExists = (sceneKeys, key) => { return sceneKeys.find((sceneKey) => key === sceneKey); }; _makeSceneKey = (child, idx) => { return child.props.tabLabel + '_' + idx; }; renderScrollableContent = () => { const scenes = this._composeScenes(); if (scenes&&Array.isArray(scenes)&&scenes.length>0) { return scenes } }; _creatSceneParams = (index) => { const { renderScrollHeader, headerHeight, insetValue } = this.props; const { containerOffsetY, containerHeight } = this.state; if (!renderScrollHeader) { return { isActive: 0 == index }; } const params = { index, isActive: 1, containerOffsetY, headerHeight,custref:(ref)=>{ this._contentScrollArr[index] = ref; },scrollTopCallback:()=>{ this._headScrollPanY = 1; }}; params.sceneHeight = headerHeight + containerHeight - insetValue; return params; }; _composeScenes = () => { return this._children().map((child, idx) => { if(idx!=0){ return React.cloneElement(child) } let key = this._makeSceneKey(child, idx); // 如果有scrollHeader,标签页必须保持update状态 const showUpdate = this.props.renderScrollHeader ? true : this._shouldRenderSceneKey(idx, 0); return ( {this._keyExists([key], key) ? ( React.cloneElement(child, this._creatSceneParams(idx)) ) : ( )} ); }); }; _handleLayout = (e) => { const { width, height } = e.nativeEvent.layout; if (width && width > 0 && Math.round(width) !== Math.round(this.state.containerWidth)) { if (IS_IOS) { const containerWidthAnimatedValue = new Animated.Value(width); // Need to call __makeNative manually to avoid a native animated bug. See // https://github.com/facebook/react-native/pull/14435 containerWidthAnimatedValue.__makeNative(); this.setState({ containerWidth: width }); } else { this.setState({ containerWidth: width }); } } if (height && height > 0 && Math.round(height) !== Math.round(this.state.containerHeight)) { this.setState({ containerHeight: height }); } }; _children = (children = this.props.children) => { return React.Children.map(children, (child) => child); }; render() { return ( {this.renderHeader()} {typeof(this.props.renderLeftView)=="function"&&this.renderLeftView()} {typeof(this.props.renderLeftView)=="function"&&this.renderRightView()} {typeof(this.props.renderLeftView)!="function"&&this.renderScrollableContent()} ); } } const styles = StyleSheet.create({ container: { position: 'relative', flex: 1, }, });