仅Android端React Native FlatList内可触摸组件点击无响应问题求助
问题描述
我正尝试实现类似视频中展示的FlatList效果,页面元素包含TextInput、Button等可触摸组件。
当前功能在iOS上运行完全正常,但在Android端出现异常:虽然通过FlingGesture可以触发FlatList滚动,但所有可触摸组件都无法响应触摸操作,点击TextInput时不会唤起软键盘。
项目中使用了NativeBase和React Navigation,可供问题排查参考。
相关代码
<Content> <FlingGestureHandler key='up' direction={Directions.UP} onHandlerStateChange={ev => { if (ev.nativeEvent.state === State.END) { //setActiveIndex() if (activeIndex === data.length -1) { return } setActiveIndex(activeIndex + 1) } }} > <FlingGestureHandler key='down' direction={Directions.DOWN} onHandlerStateChange={ev => {if (ev.nativeEvent.state === State.END) { //setActiveIndex() if (activeIndex === 0) { return } setActiveIndex(activeIndex - 1) } }} > <FlatList scrollEnabled={false} removeClippedSubviews={false} refreshControl={<RefreshControl refreshing={games.loadingGames} onRefresh={onRefresh} />} contentContainerStyle={{ flex: 5, justifyContent: 'flex-start', padding: SPACING * 2 }} style={{minHeight: Dimensions.get('screen').height-240}} data={games.sort((a,b) => { return (b.status === a.status) ? new Date(a.startDateTime) - new Date(b.startDateTime) : new Date(b.startDateTime) - new Date(a.startDateTime) })} CellRendererComponent={({item, index, children, style, ...props}) => { // console.log(`props`, props) const newStyle = [ style, { zIndex: games.length - index } ] return ( <View style={newStyle} index={index} {...props}> {children} </View> ) }} keyExtractor={(item) => item.gameId.toString()} renderItem={({item, index}) => { const inputRange = [index -1, index, index + 1] const translateY = scrollXAnimated.interpolate({ inputRange, outputRange: [50, 0, -100] }) const scale = scrollXAnimated.interpolate({ inputRange, outputRange: [.8, 1, 1.3] }) const opacity = scrollXAnimated.interpolate({ inputRange, outputRange: [1 - 1/VISIBLE_ITEMS, 1, 0] }) const game = item return index >= activeIndex && index < activeIndex+2 && ( <GameItem style={{ minHeight: ITEM_HEIGHT, minWidth: ITEM_WIDTH, opacity, transform: [ { translateY, }, { scale }, ], }} onLayout={getItemDimensions} game={game} selectGame={selectGame} onChangeText={onChangeScore} onChangeStars={onChangeStars} onSubmit={submitPrediction} /> ) }} /> </FlingGestureHandler> </FlingGestureHandler> </Content>
我已尝试多种解决方案,但仍未解决该问题,特此求助。
解决方案
这个问题是Android端React Native Gesture Handler的手势优先级冲突导致的:外层FlingGestureHandler默认会拦截所有触摸事件,普通点击操作会被误判为手势的起始阶段,不会下发给子组件的可触摸元素。你可以按以下步骤依次排查修复:
- 增加手势识别阈值,过滤点击操作
给两个FlingGestureHandler添加最小滑动距离和最小速度限制,只有满足滑动条件才会触发手势,普通点击会直接透传给子组件:
// 上下两个FlingGestureHandler都添加以下属性 minDist={50} // 滑动距离超过50dp才判定为fling,点击的移动距离远小于该值 minVelocityY={500} // 只识别速度足够的上下滑动,普通点击达不到该速度 cancelsTouchesInView={false} // 手势识别成功后也不取消已经下发给子组件的触摸事件
- 合并手势处理逻辑,减少嵌套层级
你当前嵌套了两层FlingGestureHandler分别处理上下滑动,完全可以合并成一个handler处理两个方向的逻辑,减少嵌套层级降低冲突概率:
<FlingGestureHandler direction={Directions.UP | Directions.DOWN} onHandlerStateChange={ev => { if (ev.nativeEvent.state === State.END) { if (ev.nativeEvent.direction === Directions.UP) { if (activeIndex === data.length -1) return setActiveIndex(activeIndex + 1) } else if (ev.nativeEvent.direction === Directions.DOWN) { if (activeIndex === 0) return setActiveIndex(activeIndex - 1) } } }} // 上面的阈值属性也加在这里 > {/* 直接放FlatList,不需要嵌套第二层handler */} </FlingGestureHandler>
- 替换可触摸组件为Gesture Handler版本
Android上RN原生的可触摸组件和Gesture Handler的兼容性较差,把GameItem内部用到的TextInput、Button、Touchable系列组件,替换成react-native-gesture-handler库导出的对应组件即可。如果用的是NativeBase的组件,可以给Input等可交互组件添加pointerEvents="auto"属性强制响应触摸。 - 排查其他容器的手势冲突
- 确认NativeBase的Content组件没有开启滚动,添加
scrollEnabled={false}属性,避免和自定义fling手势冲突 - 确认当前React Navigation页面关闭了侧滑返回手势,在页面配置中添加
gestureEnabled: false即可
内容的提问来源于stack exchange,提问作者cmaronchick
相关产品推荐
相关产品推荐

