简体   繁体   中英

How can I have nested horizontal ScrollViews in React Native?

I saw that React Native offers nested vertical scroll-views from React Native Nested ScrollView Can`t Scroll on Android Device

The problem is that it doesn't seem to work on nested Horizontal ScrollViews on Android. See this code for an example:

https://snack.expo.io/@harrytravelchime/broken-horizontal-scroll

import React from 'react';
import _ from 'lodash';
import { View, ScrollView, StyleSheet, Text, SafeAreaView } from 'react-native';

export default class App extends React.PureComponent {
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <ScrollView
          style={{ height: '100%', width: '100%' }}
          horizontal
          nestedScrollEnabled
        >
          <View style={{ flexDirection: 'row' }}>
            <ScrollView
              style={{ width: 200, height: '100%' }}
              horizontal
              nestedScrollEnabled
            >
              <View style={{ flexDirection: 'row' }}>
                {_.times(200, n => (
                  <View key={1000 + n} style={{ marginRight: 10 }}>
                    <Text>{1000 + n}</Text>
                  </View>
                ))}
              </View>
            </ScrollView>
            {_.times(200, n => (
              <View key={n} style={{ marginRight: 10 }}>
                <Text>{n}</Text>
              </View>
            ))}
          </View>
        </ScrollView>
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'stretch',
    paddingVertical: 50,
  },
});

On the other hand, the same code except with vertical scrolling totally works:https://snack.expo.io/@harrytravelchime/working-vertical-scroll

Is there a way to make nested horizontal scrolling work?

One solution that I've come up with is to have a TouchableWithoutFeedback that tracks any touch by the user within the inner ScrollView. As soon as it detects a touch, it disables scrolling on the outer ScrollView, which would cause the inner ScrollView to receive events.

The main changes from the above code are:

  1. Added state with outerScrollViewScrollEnabled
  2. When the inner ScrollView is touched via the TouchableWithoutFeedback , change that state
  3. Make the outer ScrollView's scrollEnabled depend on that
import React from "react";
import _ from "lodash";
import {
  View,
  ScrollView,
  StyleSheet,
  Text,
  SafeAreaView,
  TouchableWithoutFeedback
} from "react-native";

interface State {
  outerScrollViewScrollEnabled: boolean;
}

export default class App extends React.PureComponent {
  state = { outerScrollViewScrollEnabled: true };

  handleInnerPressIn = () => this.setState({ outerScrollViewScrollEnabled: false });
  handleInnerPressOut = () => this.setState({ outerScrollViewScrollEnabled: true });

  render() {
    const { outerScrollViewScrollEnabled } = this.state;

    return (
      <SafeAreaView style={styles.container}>
        <ScrollView
          style={{ height: "100%", width: "100%" }}
          horizontal
          scrollEnabled={outerScrollViewScrollEnabled}
        >
          <View style={{ flexDirection: "row" }}>
            <ScrollView style={{ width: 200, height: "100%" }} horizontal>
              <TouchableWithoutFeedback
                onPressIn={this.handleInnerPressIn}
                onPressOut={this.handleInnerPressOut}
              >
                <View style={{ flexDirection: "row" }}>
                  {_.times(200, n => (
                    <View key={1000 + n} style={{ marginRight: 10 }}>
                      <Text>{1000 + n}</Text>
                    </View>
                  ))}
                </View>
              </TouchableWithoutFeedback>
            </ScrollView>
            {_.times(200, n => (
              <View key={n} style={{ marginRight: 10 }}>
                <Text>{n}</Text>
              </View>
            ))}
          </View>
        </ScrollView>
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: "column",
    justifyContent: "center",
    alignItems: "stretch",
    paddingVertical: 50
  }
});

There is one more easier solution if you are using react-native-gesture-handler

import { ScrollView } from 'react-native'
import { ScrollView as GestureHandlerScrollView } from 'react-native-gesture-handler'

<ScrollView horizontal>
    <GestureHandlerScrollView horizontal />
</ScrollVIew>

I got this answer from react-native github issue

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM