如何在react-native中叠加ActivityIndicator?

82

我有一个视图,其中有一些表单元素和一个按钮(TouchableHighlight)。点击该按钮时,应显示一个活动指示器作为现有视图的叠加层。活动指示器应在页面中心对齐,并且现有视图应稍微模糊以指示叠加层。我尝试了不同的选项,但无法使其正常工作。

render() {
    const { username, password } = this.state;

    return (
        <View style={styles.container}>
            <View style={styles.group}>
                <Text style={styles.text}>Username:</Text>
                <TextInput
                    style={styles.input}
                    onChangeText={this.handleUserNameChange.bind(this)}
                    value={username}
                    underlineColorAndroid="transparent"
                />
            </View>
            <View style={styles.group}>
                <Text style={styles.text}>Password:</Text>
                <TextInput
                    style={styles.input}
                    secureTextEntry={true}
                    onChangeText={this.handlePasswordChange.bind(this)}
                    value={password}
                    underlineColorAndroid="transparent"
                />
            </View>
            <TouchableHighlight
                style={styles.button}
                onPress={this.handleLogin.bind(this)}>
                <Text style={styles.buttonText}>Logon</Text>
            </TouchableHighlight>
        </View>
    );
}

现有的样式:

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'flex-start',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
        marginTop: 60
    },
    group: {
        alignItems: 'flex-start',
        padding: 10
    },
    input: {
        width: 150,
        padding: 2,
        paddingLeft: 5,
        borderColor: 'gray',
        borderWidth: 1
    },
    text: {
        padding: 0
    },
    button: {
        width: 150,
        backgroundColor: '#2299F2',
        padding: 15,
        marginTop: 20,
        borderRadius: 5
    },
    buttonText: {
        textAlign: 'center',
        color: '#fff',
        fontSize: 24
    },
});

我需要在上面的视图中添加一个ActivityIndicator,覆盖整个视图,并将其居中显示。


尝试使用React Native Loading Spinner Overlay,结果整个应用程序停止滚动。因此将其移除,一切恢复正常。 - Rashmi
10个回答

162

要让这个工作,您需要将其absolute定位,并在应该位于叠加层下方的元素之后呈现它:

  loading: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    alignItems: 'center',
    justifyContent: 'center'
  }

然后根据加载状态条件性地将其组合成 render 方法。我假设 this.handleLogin 已经设置了某种加载状态。确保它最后渲染,以便它优先显示。

...
{this.state.loading &&
    <View style={styles.loading}>
      <ActivityIndicator size='large' />
    </View>
}


16
非常感谢,我已经将背景色#F5FCFF88添加到加载样式中,以实现模糊叠加效果。 - vijayst
9
在View中添加pointerEvents="none"属性,使背景不可触摸。 - Dhrupal
它对我不起作用,它水平居中,但垂直上方,而整个底部屏幕都是空的... - Zennichimaro
1
@Dhrupal 给加载样式添加更高的zIndex。例如:zIndex: 1000 - Muaz
pointerEvents="box-none" 对我有效。 - Mert
显示剩余3条评论

31

这里是使用create react native app的完整示例。

import React from 'react';
import {StyleSheet, ActivityIndicator, View} from "react-native";

export default class Example extends React.Component {

    constructor(props) {
        super(props);

        this.state = {}

    render() {
        return (
            <View
                style={{flex: 1}}
            >
                  //Add other content here
                  {this.state.loading &&
                  <View style={styles.loading}>
                      <ActivityIndicator/>
                  </View>
                  }
                </View>
            );
        }
    }

    showLoading() {
       this.setState({loading: true})
    }

    hideLoading() {
       this.setState({loading: false})
    }

    const styles = StyleSheet.create({
        loading: {
            position: 'absolute',
            left: 0,
            right: 0,
            top: 0,
            bottom: 0,
            opacity: 0.5,
            backgroundColor: 'black',
            justifyContent: 'center',
            alignItems: 'center'
        }
    })

这个可以工作,但是如何启用/禁用背景颜色呢? - nixn
@niksn,我为你添加了showLoading和hideLoading函数。 - ykay says Reinstate Monica
1
谢谢兄弟,这对我帮助很大。 - Manoj Bhardwaj

7
您可以使用StyleSheet.absoluteFill来缩短代码。 将其添加到您的渲染中即可。

<View style={styles.container}>
  //... other code here
  {this.state.loading && <View
      style={{
        ...StyleSheet.absoluteFill,
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <ActivityIndicator />
  </View>}
</View>

改进:

您还可以创建一个加载组件:

Loading.js

import React from 'react';
import {View, ActivityIndicator, StyleSheet} from 'react-native';

export const Loading = ({theme = 'white', size = 'large'}) => {
  const color = theme === 'white' ? '#00bdcd' : '#fff';
  return (
    <View
      style={{
        ...StyleSheet.absoluteFill,
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <ActivityIndicator size={size} color={color} />
    </View>
  );
};

然后您可以在任何想要使用它的地方使用它

<View style={styles.container}>
   //... other code here
   // remember to import Loading component
   {this.state.loading && <Loading />}
</View>


优秀而优雅的解决方案。 - IdoS

6

您可以使用活动指示器组件来构建漂亮的覆盖层,同时像Sanaur建议的那样利用模态功能。

例如,您可以使用以下功能组件。您可以通过将其绑定到屏幕中的状态的 show 属性来控制其可见性。

这是一个您可以根据需要调整的示例。

const ModalActivityIndicator = props => {
  const {
    show = false,
    color = "black",
    backgroundColor = "white",
    dimLights = 0.6,
    loadingMessage = "Doing stuff ..."
  } = props;
  return (
    <Modal transparent={true} animationType="none" visible={show}>
      <View
        style={{
          flex: 1,
          alignItems: "center",
          justifyContent: "center",
          backgroundColor: `rgba(0,0,0,${dimLights})`
        }}
      >
        <View
          style={{
            padding: 13,
            backgroundColor: `${backgroundColor}`,
            borderRadius: 13
          }}
        >
          <ActivityIndicator animating={show} color={color} size="large" />
          <Text style={{ color: `${color}` }}>{loadingMessage}</Text>
        </View>
      </View>
    </Modal>
  );
};

在您的屏幕上,在呈现的返回值中,只需将其作为子元素添加即可(请忽略其他代码,我将其放在那里以提供上下文)。

return (
<TouchableWithoutFeedback
  onPress={() => {
    Keyboard.dismiss();
  }}
>
  <View style={{ padding: 13, flex: 1}}>
    <ModalActivityIndicator show={screenIsWaiting} />
    <View
      style={{

这里的screenIsWaiting仅仅是一个状态,例如:

  const [screenIsWaiting, setScreenIsWaiting] = useState(false);

为了测试它,您可以在任何地方添加一个按钮,
  <Button
    title="TestButton"
    onPress={async () => {
      setScreenIsWaiting(true);
      await sleep(5000);
      setScreenIsWaiting(false);
      ...
    }}
  />

其中 sleep 是一个函数,其定义如下

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

我在另一篇StackOverflow帖子上发现了sleep()的想法。

当然,你也可以定义

<ModalActivityIndicator show={screenIsWaiting} ... />

只需在您的应用程序根组件中引入一次,并通过全局状态容器(如redux)触发其显示和属性。


4

步骤 1:创建旋转器组件:

export const OverlaySpinner = () => {
 return (
    <View style={styles.spinnerView}>
      <ActivityIndicator size="large" color="#0000ff" />
    </View>
  );   
 };

步骤 2:

创建旋转器视图的样式(使用zIndex非常重要,以确保视图在屏幕上覆盖所有内容):

spinnerView: {
    position: "absolute",
    zIndex: 1,
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    alignItems: "center",
    justifyContent: "center",
    backgroundColor: "#F5FCFF88",
  },

第三步:

创建旋转组件的初始状态:

const [showSpinner, setshowSpinner] = useState(true);

第四步:

使用组件,别忘了导入它(提交时别忘了隐藏键盘)

{showSpinner && <OverlaySpinner />}

4

对于此项需求,有一个相关的库react-native-loading-spinner-overlay可供使用。 你可以直接使用以下命令进行安装:

npm install react-native-loading-spinner-overlay --save

你可以使用此方法将内容导入到项目中:

import Spinner from 'react-native-loading-spinner-overlay';

以下是使用方法

<Spinner
  //visibility of Overlay Loading Spinner
  visible={this.state.loading}
  //Text with the Spinner 
  textContent={'Loading...'}
  //Text style of the Spinner Text
  textStyle={styles.spinnerTextStyle}
/>

enter image description here enter image description here


这个库在全屏叠加和禁用用户交互方面似乎工作正常。 - Jay Mayu

2

考虑到加载,添加视图

    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    alignItems: 'center',
    justifyContent: 'center'

2

我建议使用模态框来叠加活动指示器。以下是一个例子:

<Modal
transparent={true}
animationType={'none'}
visible={loading}
onRequestClose={() => {console.log('close modal')}}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator
animating={loading} />
</View>
</View>
</Modal>

0
这是一个针对使用 NativeBase 设计系统的函数组件的代码示例。我在其中使用了另一个 hook,即 useColorScheme,用于检测暗模式。如果有人想要实现类似功能,可以参考这个示例。
import { Flex, Spinner } from "native-base"
import React from "react"
import useColorScheme from "../../hooks/useColorScheme"

export default function Loading() {
  const colorScheme = useColorScheme()

  return (
    <Flex
      position="absolute"
      alignItems="center"
      justifyContent="center"
      top={0}
      left={0}
      right={0}
      bottom={0}
      backgroundColor={colorScheme === "dark" ? "coolBlack.500" : "white"}
    >
      <Spinner size="lg" color={colorScheme === "dark" ? "white" : "coolBlack.500"} />
    </Flex>
  )
}

0

在活动指示器的视图中设置

位置:'absolute', 左:0, 右:0, 上:0, 下:0, alignItems:'center', justifyContent:'center'


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