React Native: 使用ScrollView时垂直居中问题

104

我正在使用React Native,当我引入ScrollView后,无法保持元素的垂直居中。为了演示这一点,我使用React Native Playground创建了3个应用程序。所有示例都有两个页面,并且可以通过点击蓝色按钮进行切换。

示例1:

在第2页上,此示例显示垂直居中的块。这是因为容器应用了以下样式:

flex: 1,
flexDirection: 'column',
justifyContent: 'center',

https://rnplay.org/apps/lb5wSQ

然而,当你切换到第二页时就会出现问题,因为整个页面内容无法适应,所以我们需要一个ScrollView

示例2

在这个示例中,我将所有内容都包含在一个ScrollView中。这样可以让第二页滚动,但是第一页的垂直居中就丢失了。

https://rnplay.org/apps/DCgOgA

示例3

为了尝试恢复第一页的垂直居中,我在ScrollViewcontentContainerStyle上应用了flex: 1。这修复了第一页的垂直居中,但我不能再滚动到第二页的底部。

https://rnplay.org/apps/LSgbog

我该如何解决这个问题,以使得第一页的元素垂直居中,同时在第二页仍然能够完全滚动?


你能找到解决方案吗? - Zach
1
@Zach 不,但我已经很久没有看过这个了。这还是一个问题吗? - Johnny Oshika
@JohnnyOshika,您能接受我的答案吗?它已经帮助了很多人。 - Jake Coxon
1
@JakeCoxon 已完成,谢谢! - Johnny Oshika
我的回答涵盖了你的问题。 - undefined
7个回答

349
我使用 flexGrow 替代了 flex 来解决这个问题。 这使内容容器拉伸以填充 ScrollView,但不限制最大高度,因此当内容超出 ScrollView 的边界时仍然可以正确滚动。

12
最终,你让我明白了flexGrow的用处。 - Dr. Younes Henni
小小的解释让我非常明白,我之前卡了好几个小时。 - Delali
3
如下回答所述,实际上仍需要 <ScrollView contentContainerStyle={{flexGrow: 1, justifyContent: 'center'}}> - Petr Bela
1
这将是最简单易用的: <ScrollView contentContainerStyle={{ alignItems: 'center' }}> - John
谢谢!当ScrollView水平滚动(horizontal选项设置为true)时,此flexGrow方法也适用。 - Dwigt

66
这是我的方法。
使用一个外部容器,设置 flex : 1,然后滚动视图需要使用 contentContainerStyle,而不是 style,设置 {flexGrow : 1, justifyContent : 'center'}
<View style={styles.mainContainer}>
  <ScrollView
    contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
    <View style={styles.scrollViewContainer}>
      {/* Put your stuff here, and it will be centered vertically. */}
    </View>
  </ScrollView>
</View>

样式:

const styles = StyleSheet.create({
  mainContainer: { flex: 1 },
  scrollView: { height: Dimensions.get('window').height },
  scrollViewContainer: {},
});

当我尝试将scrollView: { height: Dimensions.get('window').height }这段样式注释掉时,它根本没有任何效果。 这也是应该的,因为它在主要代码中没有被使用。 - undefined

7

现在有一个新的解决方案(不幸的是目前只适用于iOS) - 我们可以在ScrollView上使用属性。具体信息请参考centerContent


3
这仅适用于iOS,有安卓的选项吗? - Devansh sadhotra

6
为了使ScrollView本身垂直居中,您需要有这样的结构:
<View style={{flex: 1}}>
  <View>
   <ScrollView .../>
  </View>
</View>

否则,如果您想要将 ScrollView 的内容本身居中,那么需要使用以下代码:
<ScrollView contentContainerStyle={{flexGrow : 1, justifyContent : 'center'}} ...>

4

编辑:现在你可以使用<ScrollView contentContainerStyle={{flexGrow: 1}}>

对于不支持flexGrow的旧版本React Native,请使用下面的解决方案。


我发现可靠地实现iOS和Android上的此目的的唯一方法是将内部视图的minHeight设置为ScrollView的大小。例如:

<ScrollView
    style={{flex: 1}}
    contentContainerStyle={{minHeight: this.state.minHeight}}
    onLayout={event => this.setState({minHeight: event.nativeEvent.layout.height})}
>

注意:为了简单起见,我已经将上面的样式和函数内联了,但您可能希望使用常量引用来存储不变的值,并记忆变化的样式以防止不必要的重新渲染。
const {minHeight} = this.state;
// Manually memorise changing style or use something like reselect...
if (this.lastMinHeight != minHeight) {
    this.lastMinHeight = minHeight;
    this.contentContainerStyle = {minHeight: minHeight}
}
<ScrollView
    style={styles.scrollViewStyle}
    contentContainerStyle={this.contentContainerStyle}
    onLayout={this.onScrollViewLayout}
>

1
为什么不在ScrollView中只包含第二页?
return (
    <ScrollView >
        <View style={styles.lipsum}>
            {this.renderButton()}
            <Text style={styles.lipsumText}>{lipsumText}</Text>
        </View>
    </ScrollView>
  );

只需修改您的第一个示例,它就能完美运行 :)


1
我想这是一个选项,也是一个很好的建议,但在我的实际用例中,顶部始终会出现一个标题(带有标志、标语等),它也需要成为ScrollView的一部分。将ScrollView移动到子视图意味着标题不再滚动,或者我必须在所有子视图中复制标题。 - Johnny Oshika

0
我只是使用
paddingTop: Dimensions.get("window").height / 4,

在所需的视图内,例如:
<ScrollView>
   <View
   style={{
   paddingTop: Dimensions.get("window").height / 4,
   }}
   >
   <PaymentEmptyList />
   </View>
</ScrollView>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接