React Native中使用 react-native-scrollable-tab-view嵌套在ScrollView里,导致 子内容 在安卓上无法显示

问题:

0.9.0 或 0.8.0 版本的react-native-scrollable-tab-view如果外层使用了一个ScrollView,在Android端上是无法显示的,iOS上显示无问题。

类似代码如下:

<ScrollView style={{flex: }}>
<ItemMoment navigation={this.props.navigation} item={item} showComment={false}/> <View style={{height: 0.5, color: "#ccc", marginTop: , marginBottom: }}/> <ScrollableTabView
tabBarPosition='top'
initialPage={} //默认为第一页
locked={false} //表示手指是否能拖动视图,默认为false(表示可以拖动)。设为true的话,我们只能点击Tab来切换视图。
renderTabBar={() => <ScrollableTabBar/>}
tabBarUnderlineColor={'red'}
scrollWithoutAnimation={true} tabBarBackgroundColor='#fff'
tabBarActiveTextColor='#2c2c2c'
tabBarInactiveTextColor='#666'
> <CommentTab navigation={this.props.navigation} style={styles.textTab} tabLabel='评论(129)'/>
<CommentTab navigation={this.props.navigation} style={styles.textTab} tabLabel='转发(209)'/>
<CommentTab navigation={this.props.navigation} style={styles.textTab} tabLabel='赞(17)'/> </ScrollableTabView> </ScrollView>

解决办法:

查看源码后发现Android端使用的是AnimatedViewPagerAndroid,和iOS的实现有区别,可以把react-native-scrollable-tab-view/index.js中所有Android的判断给去掉,全部使用iOS的实现方式,即可解决此问题。

例如,注释掉 node_modules/react-native-scrollable-tab-view/index.js 完后的代码如下:

const React = require('react');
const { Component } = React;
const { ViewPropTypes } = ReactNative = require('react-native');
const createReactClass = require('create-react-class');
const PropTypes = require('prop-types');
const {
Dimensions,
View,
Animated,
ScrollView,
Platform,
StyleSheet,
ViewPagerAndroid,
InteractionManager,
} = ReactNative;
const TimerMixin = require('react-timer-mixin'); const SceneComponent = require('./SceneComponent');
const DefaultTabBar = require('./DefaultTabBar');
const ScrollableTabBar = require('./ScrollableTabBar'); const AnimatedViewPagerAndroid = Platform.OS === 'android' ?
Animated.createAnimatedComponent(ViewPagerAndroid) :
undefined; const ScrollableTabView = createReactClass({
mixins: [TimerMixin, ],
statics: {
DefaultTabBar,
ScrollableTabBar,
},
scrollOnMountCalled: false, propTypes: {
tabBarPosition: PropTypes.oneOf(['top', 'bottom', 'overlayTop', 'overlayBottom', ]),
initialPage: PropTypes.number,
page: PropTypes.number,
onChangeTab: PropTypes.func,
onScroll: PropTypes.func,
renderTabBar: PropTypes.any,
style: ViewPropTypes.style,
contentProps: PropTypes.object,
scrollWithoutAnimation: PropTypes.bool,
locked: PropTypes.bool,
prerenderingSiblingsNumber: PropTypes.number,
}, getDefaultProps() {
return {
tabBarPosition: 'top',
initialPage: ,
page: -,
onChangeTab: () => {},
onScroll: () => {},
contentProps: {},
scrollWithoutAnimation: false,
locked: false,
prerenderingSiblingsNumber: ,
};
}, getInitialState() {
const containerWidth = Dimensions.get('window').width;
let scrollValue;
let scrollXIOS;
let positionAndroid;
let offsetAndroid; if (Platform.OS === 'ios') {
scrollXIOS = new Animated.Value(this.props.initialPage * containerWidth);
const containerWidthAnimatedValue = new Animated.Value(containerWidth);
// Need to call __makeNative manually to avoid a native animated bug. See
// https://github.com/facebook/react-native/pull/14435
containerWidthAnimatedValue.__makeNative();
scrollValue = Animated.divide(scrollXIOS, containerWidthAnimatedValue); const callListeners = this._polyfillAnimatedValue(scrollValue);
scrollXIOS.addListener(
({ value, }) => callListeners(value / this.state.containerWidth)
);
} else {
positionAndroid = new Animated.Value(this.props.initialPage);
offsetAndroid = new Animated.Value();
scrollValue = Animated.add(positionAndroid, offsetAndroid); const callListeners = this._polyfillAnimatedValue(scrollValue);
let positionAndroidValue = this.props.initialPage;
let offsetAndroidValue = ;
positionAndroid.addListener(({ value, }) => {
positionAndroidValue = value;
callListeners(positionAndroidValue + offsetAndroidValue);
});
offsetAndroid.addListener(({ value, }) => {
offsetAndroidValue = value;
callListeners(positionAndroidValue + offsetAndroidValue);
});
} return {
currentPage: this.props.initialPage,
scrollValue,
scrollXIOS,
positionAndroid,
offsetAndroid,
containerWidth,
sceneKeys: this.newSceneKeys({ currentPage: this.props.initialPage, }),
};
}, componentWillReceiveProps(props) {
if (props.children !== this.props.children) {
this.updateSceneKeys({ page: this.state.currentPage, children: props.children, });
} if (props.page >= && props.page !== this.state.currentPage) {
this.goToPage(props.page);
}
}, componentWillUnmount() {
if (Platform.OS === 'ios') {
this.state.scrollXIOS.removeAllListeners();
} else {
this.state.positionAndroid.removeAllListeners();
this.state.offsetAndroid.removeAllListeners();
}
}, goToPage(pageNumber) {
if (Platform.OS === 'ios') {
const offset = pageNumber * this.state.containerWidth;
if (this.scrollView) {
this.scrollView.getNode().scrollTo({x: offset, y: , animated: !this.props.scrollWithoutAnimation, });
}
} else {
if (this.scrollView) {
if (this.props.scrollWithoutAnimation) {
this.scrollView.getNode().setPageWithoutAnimation(pageNumber);
} else {
this.scrollView.getNode().setPage(pageNumber);
}
}
} const currentPage = this.state.currentPage;
this.updateSceneKeys({
page: pageNumber,
callback: this._onChangeTab.bind(this, currentPage, pageNumber),
});
}, renderTabBar(props) {
if (this.props.renderTabBar === false) {
return null;
} else if (this.props.renderTabBar) {
return React.cloneElement(this.props.renderTabBar(props), props);
} else {
return <DefaultTabBar {...props} />;
}
}, updateSceneKeys({ page, children = this.props.children, callback = () => {}, }) {
let newKeys = this.newSceneKeys({ previousKeys: this.state.sceneKeys, currentPage: page, children, });
this.setState({currentPage: page, sceneKeys: newKeys, }, callback);
}, newSceneKeys({ previousKeys = [], currentPage = , children = this.props.children, }) {
let newKeys = [];
this._children(children).forEach((child, idx) => {
let key = this._makeSceneKey(child, idx);
if (this._keyExists(previousKeys, key) ||
this._shouldRenderSceneKey(idx, currentPage)) {
newKeys.push(key);
}
});
return newKeys;
}, // Animated.add and Animated.divide do not currently support listeners so
// we have to polyfill it here since a lot of code depends on being able
// to add a listener to `scrollValue`. See https://github.com/facebook/react-native/pull/12620.
_polyfillAnimatedValue(animatedValue) { const listeners = new Set();
const addListener = (listener) => {
listeners.add(listener);
}; const removeListener = (listener) => {
listeners.delete(listener);
}; const removeAllListeners = () => {
listeners.clear();
}; animatedValue.addListener = addListener;
animatedValue.removeListener = removeListener;
animatedValue.removeAllListeners = removeAllListeners; return (value) => listeners.forEach(listener => listener({ value, }));
}, _shouldRenderSceneKey(idx, currentPageKey) {
let numOfSibling = this.props.prerenderingSiblingsNumber;
return (idx < (currentPageKey + numOfSibling + ) &&
idx > (currentPageKey - numOfSibling - ));
}, _keyExists(sceneKeys, key) {
return sceneKeys.find((sceneKey) => key === sceneKey);
}, _makeSceneKey(child, idx) {
return child.props.tabLabel + '_' + idx;
}, renderScrollableContent() {
// if (Platform.OS === 'ios') {
const scenes = this._composeScenes();
return <Animated.ScrollView
horizontal
pagingEnabled
automaticallyAdjustContentInsets={false}
contentOffset={{ x: this.props.initialPage * this.state.containerWidth, }}
ref={(scrollView) => { this.scrollView = scrollView; }}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: this.state.scrollXIOS, }, }, }, ],
{ useNativeDriver: true, listener: this._onScroll, }
)}
onMomentumScrollBegin={this._onMomentumScrollBeginAndEnd}
onMomentumScrollEnd={this._onMomentumScrollBeginAndEnd}
scrollEventThrottle={}
scrollsToTop={false}
showsHorizontalScrollIndicator={false}
scrollEnabled={!this.props.locked}
directionalLockEnabled
alwaysBounceVertical={false}
keyboardDismissMode="on-drag"
{...this.props.contentProps}
>
{scenes}
</Animated.ScrollView>;
// } else {
// const scenes = this._composeScenes();
// return <AnimatedViewPagerAndroid
// key={this._children().length}
// style={styles.scrollableContentAndroid}
// initialPage={this.props.initialPage}
// onPageSelected={this._updateSelectedPage}
// keyboardDismissMode="on-drag"
// scrollEnabled={!this.props.locked}
// onPageScroll={Animated.event(
// [{
// nativeEvent: {
// position: this.state.positionAndroid,
// offset: this.state.offsetAndroid,
// },
// }, ],
// {
// useNativeDriver: true,
// listener: this._onScroll,
// },
// )}
// ref={(scrollView) => { this.scrollView = scrollView; }}
// {...this.props.contentProps}
// >
// {scenes}
// </AnimatedViewPagerAndroid>;
// }
}, _composeScenes() {
return this._children().map((child, idx) => {
let key = this._makeSceneKey(child, idx);
return <SceneComponent
key={child.key}
shouldUpdated={this._shouldRenderSceneKey(idx, this.state.currentPage)}
style={{width: this.state.containerWidth, }}
>
{this._keyExists(this.state.sceneKeys, key) ? child : <View tabLabel={child.props.tabLabel}/>}
</SceneComponent>;
});
}, _onMomentumScrollBeginAndEnd(e) {
const offsetX = e.nativeEvent.contentOffset.x;
const page = Math.round(offsetX / this.state.containerWidth);
if (this.state.currentPage !== page) {
this._updateSelectedPage(page);
}
}, _updateSelectedPage(nextPage) {
let localNextPage = nextPage;
if (typeof localNextPage === 'object') {
localNextPage = nextPage.nativeEvent.position;
} const currentPage = this.state.currentPage;
this.updateSceneKeys({
page: localNextPage,
callback: this._onChangeTab.bind(this, currentPage, localNextPage),
});
}, _onChangeTab(prevPage, currentPage) {
this.props.onChangeTab({
i: currentPage,
ref: this._children()[currentPage],
from: prevPage,
});
}, _onScroll(e) {
if (Platform.OS === 'ios') {
const offsetX = e.nativeEvent.contentOffset.x;
if (offsetX === && !this.scrollOnMountCalled) {
this.scrollOnMountCalled = true;
} else {
this.props.onScroll(offsetX / this.state.containerWidth);
}
} else {
const { position, offset, } = e.nativeEvent;
this.props.onScroll(position + offset);
}
}, _handleLayout(e) {
const { width, } = e.nativeEvent.layout; if (!width || width <= || Math.round(width) === Math.round(this.state.containerWidth)) {
return;
} if (Platform.OS === '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();
scrollValue = Animated.divide(this.state.scrollXIOS, containerWidthAnimatedValue);
this.setState({ containerWidth: width, scrollValue, });
} else {
this.setState({ containerWidth: width, });
}
this.requestAnimationFrame(() => {
this.goToPage(this.state.currentPage);
});
}, _children(children = this.props.children) {
return React.Children.map(children, (child) => child);
}, render() {
let overlayTabs = (this.props.tabBarPosition === 'overlayTop' || this.props.tabBarPosition === 'overlayBottom');
let tabBarProps = {
goToPage: this.goToPage,
tabs: this._children().map((child) => child.props.tabLabel),
activeTab: this.state.currentPage,
scrollValue: this.state.scrollValue,
containerWidth: this.state.containerWidth,
}; if (this.props.tabBarBackgroundColor) {
tabBarProps.backgroundColor = this.props.tabBarBackgroundColor;
}
if (this.props.tabBarActiveTextColor) {
tabBarProps.activeTextColor = this.props.tabBarActiveTextColor;
}
if (this.props.tabBarInactiveTextColor) {
tabBarProps.inactiveTextColor = this.props.tabBarInactiveTextColor;
}
if (this.props.tabBarTextStyle) {
tabBarProps.textStyle = this.props.tabBarTextStyle;
}
if (this.props.tabBarUnderlineStyle) {
tabBarProps.underlineStyle = this.props.tabBarUnderlineStyle;
}
if (overlayTabs) {
tabBarProps.style = {
position: 'absolute',
left: ,
right: ,
[this.props.tabBarPosition === 'overlayTop' ? 'top' : 'bottom']: ,
};
} return <View style={[styles.container, this.props.style, ]} onLayout={this._handleLayout}>
{this.props.tabBarPosition === 'top' && this.renderTabBar(tabBarProps)}
{this.renderScrollableContent()}
{(this.props.tabBarPosition === 'bottom' || overlayTabs) && this.renderTabBar(tabBarProps)}
</View>;
},
}); module.exports = ScrollableTabView; const styles = StyleSheet.create({
container: {
flex: ,
},
scrollableContentAndroid: {
flex: ,
},
});

其中,注释掉的代码 如图显示即可!

附注:

笔者在实践中发现,虽然这种修改确实可以让子内容显示,但同时又产生了另外一个bug,将导致 Tab 点击就报错了。

所以,笔者最终的设计,还是 取消  ScrollView  中嵌套 ScrollableTabView

本博客地址: wukong1688

本文原文地址:https://www.cnblogs.com/wukong1688/p/10904017.html

转载请著名出处!谢谢~~

 
 

[RN] React Native中使用 react-native-scrollable-tab-view嵌套在ScrollView里,导致 子内容 在安卓上无法显示的更多相关文章

  1. [RN] React Native 中使用 stickyHeaderIndices 实现 ScrollView 的吸顶效果

    React Native中,ScrollView组件可以使用 stickyHeaderIndices 轻松实现 sticky 效果. 例如下面代码中: <ScrollView showsVert ...

  2. react native中使用echarts

    开发平台:mac pro node版本:v8.11.2 npm版本:6.4.1 react-native版本:0.57.8 native-echarts版本:^0.5.0 目标平台:android端收 ...

  3. 《React Native 精解与实战》书籍连载「React Native 中的生命周期」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  4. React Native中的远程调试是不可靠的

    一.原因 当您发现rn app在关闭远程调试后,一些功能无法正常工作时,这很可能是由于设备上的JavaScript执行环境与远程调试器之间的细微差别造成的. 例如,日期问题,Date构造函数似乎接受C ...

  5. React Native中组件的props和state

    一.组件的属性(props)和状态(state) 1.属性(props) 它是组件的不可变属性(组件自己不可以自己修改props). 组件自身定义了一组props作为对外提供的接口,展示一个组件时只需 ...

  6. react native 之 在现有的iOS工程中集成react native

    在现有的iOS工程中集成react native, 或者说将react native引入到iOS 项目,是RN和iOS混合开发的必经之路 参考官网教程:https://reactnative.cn/d ...

  7. react native 中webview内的点击事件传到外部原生调用

    先说一下我使用webview的时候遇到的一个功能需求 是这样的,上图中的这个页面是用h5做的,但是由于点击"我的优惠劵"是需要跳转到我原生的页面,也就是说我需要获得这个h5提供的点 ...

  8. React Native 中 CSS 的使用

    首先声明,此文原作者为黎 跃春 React Native中CSS 内联样式 对象样式 使用Stylesheet.Create 样式拼接 导出样式对象 下面的代码是index.ios.js中的代码: / ...

  9. iOS原生项目中集成React Native

    1.本文的前提条件是,电脑上已经安装了CocoaPods,React Native相关环境. 2.使用Xcode新建一个工程.EmbedRNMeituan [图1] 3.使用CocoaPods安装Re ...

随机推荐

  1. Elastic Stack 7.5.0白金版永不过期

    适用版本:7.4.0~7.5.0 警告:本文章仅限于学习,非商业用途. 目录结构 # 先创建相关目录,具体结构如下: /opt |-- bulid # 编译目录 | |- src |-- instal ...

  2. Linux学习笔记之文件读取过程

    0x00 概述 对于Linux系统来说,一切的数据都起源于磁盘中存储的文件.Linux文件系统的结构及其在磁盘中是如何存储的?操作系统是怎样找到这些文件进行读取的?这一章主要围绕这几个问题进行介绍(以 ...

  3. Spring怎么管理事务?

    我们一般通过aop管理事务,就是把代码看成一个纵向有序的,然后通过aop管理事务,就好比增删改的时候需要开启一个事务,我们给他配置一个required,required就是有事务就执行事务,没有就给他 ...

  4. .Net Core 程序集管理说明(加载)

    .NET CORE 的程序集加载管理和以前的 .NET 发生了很大的变化, 在 .NET CORE 里, 程序集的加载, 依赖了 xx.deps.json 文件, deps.json 文件里,定义了程 ...

  5. css3做ipone当时的滑动解锁闪亮条

    现在一般的登录 注册 什么  的页面,都是会做个滑动验证.一般都是像IPONE早期那个 滑动开屏的效果 ,这个效果现在可以用CSS3来实现. 主要用到几个属性 background 背景使用渐变属性, ...

  6. android中fragment卡顿的原因

    首页的ViewPager有十几个Fragment,在快速切换的时候,容易产生卡顿现象. 二.分析当ViewPager切换到当前的Fragment时,Fragment会加载布局并显示内容,如果用户这时快 ...

  7. GitHub Vue项目推荐|Vue+Element实现的电商后台管理系统功能丰富

    GitHub Vue项目推荐|mall-admin-web是一个电商后台管理系统的前端项目基于Vue+Element实现 主要包括商品管理.订单管理.会员管理.促销管理.运营管理.内容管理.统计报表. ...

  8. 在 Vim 中,删除 ^@ 符号的几种方法

    在 Vim 中,^@ 表示 ASCII 码中的 NULL 字符,编码为 0x00,占用一个字节. 删除方法 方法1,采用 <CTRL-V><CTRL-J> 或 <CTRL ...

  9. Linux CentOS7 安装FTP服务器

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_39680564/article/de ...

  10. layui扩展组件sliderVerify 实现滑块验证

    首先在要使用的静态文件代码中引入‘./sliderVerify/sliderVerify.js‘ 先看看效果 示例代码 <!DOCTYPE html> <html> <h ...