You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中显示ActivityIndicator时如何锁定全屏幕触摸?

解决React Native中ActivityIndicator显示时下方组件可触摸的问题

嘿,这个问题我之前开发React Native项目时也踩过坑!用Modal虽然能解决,但确实有点“大材小用”的感觉,给你分享几个更轻量优雅的方案,亲测好用:

方法一:利用TouchableWithoutFeedback禁用内容触摸

核心思路是用TouchableWithoutFeedback包裹整个页面内容,通过状态控制它的disabled属性,同时在上方叠加一个半透明遮罩层和ActivityIndicator。这样加载时,内容区域会被禁用触摸,遮罩层也能从视觉上提示用户当前处于加载状态。

示例代码:

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

function HomeScreen() {
  const [isLoading, setIsLoading] = useState(false);

  // 模拟加载操作
  const handleLoad = () => {
    setIsLoading(true);
    setTimeout(() => setIsLoading(false), 3000);
  };

  return (
    <View style={styles.container}>
      {/* 包裹所有可交互内容 */}
      <TouchableWithoutFeedback disabled={isLoading}>
        <View style={styles.content}>
          {/* 你的页面交互组件,比如按钮、列表等 */}
          <View style={styles.button} onPress={handleLoad}>
            {/* 按钮内容 */}
          </View>
        </View>
      </TouchableWithoutFeedback>

      {/* 加载状态遮罩 */}
      {isLoading && (
        <View style={styles.loadingOverlay}>
          <ActivityIndicator size="large" color="#2196F3" />
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    position: 'relative', // 让遮罩层可以绝对定位
  },
  content: {
    flex: 1,
    padding: 16,
  },
  loadingOverlay: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(255,255,255,0.8)', // 半透明背景增强视觉提示
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 100, // 确保遮罩层在最上层
  },
  button: {
    padding: 12,
    backgroundColor: '#2196F3',
    borderRadius: 8,
    alignItems: 'center',
  },
});

方法二:使用pointerEvents属性控制触摸响应

React Native的View组件有一个pointerEvents属性,通过它可以直接控制视图是否响应触摸事件。这个方案代码更简洁,不需要额外的Touchable组件:

示例代码:

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

function HomeScreen() {
  const [isLoading, setIsLoading] = useState(false);

  const handleLoad = () => {
    setIsLoading(true);
    setTimeout(() => setIsLoading(false), 3000);
  };

  return (
    <View style={styles.container}>
      {/* 页面内容:加载时禁用所有触摸 */}
      <View 
        style={styles.content} 
        pointerEvents={isLoading ? 'none' : 'auto'}
      >
        <View style={styles.button} onPress={handleLoad}>
          {/* 按钮内容 */}
        </View>
      </View>

      {/* 加载遮罩:确保它能挡住触摸事件 */}
      {isLoading && (
        <View style={styles.loadingOverlay} pointerEvents="auto">
          <ActivityIndicator size="large" color="#2196F3" />
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    position: 'relative',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  loadingOverlay: {
    position: 'absolute',
    inset: 0, // 等价于top/left/right/bottom: 0
    backgroundColor: 'rgba(255,255,255,0.8)',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 100,
  },
});

当isLoading为true时,内容区域的pointerEvents设为none,意味着它和所有子组件都不会响应触摸;遮罩层设为auto,确保它能接收触摸(其实这里只是用来挡住下面的内容,不需要处理触摸事件)。

方法三:封装可复用的LoadingOverlay组件

如果多个页面都需要这个功能,建议封装成独立组件,提升代码复用性:

// components/LoadingOverlay.js
import { View, ActivityIndicator, StyleSheet } from 'react-native';

export default function LoadingOverlay({ visible, color = '#2196F3', size = 'large' }) {
  if (!visible) return null;

  return (
    <View style={styles.overlay}>
      <ActivityIndicator size={size} color={color} />
    </View>
  );
}

const styles = StyleSheet.create({
  overlay: {
    position: 'absolute',
    inset: 0,
    backgroundColor: 'rgba(255,255,255,0.8)',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 1000,
    pointerEvents: 'auto',
  },
});

然后在页面中使用:

import { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import LoadingOverlay from './components/LoadingOverlay';

function HomeScreen() {
  const [isLoading, setIsLoading] = useState(false);

  const handleLoad = () => {
    setIsLoading(true);
    setTimeout(() => setIsLoading(false), 3000);
  };

  return (
    <View style={styles.container}>
      <View 
        style={styles.content} 
        pointerEvents={isLoading ? 'none' : 'auto'}
      >
        <View style={styles.button} onPress={handleLoad}>
          {/* 按钮内容 */}
        </View>
      </View>
      <LoadingOverlay visible={isLoading} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    position: 'relative',
  },
  content: {
    flex: 1,
    padding: 16,
  },
});

这些方案都比Modal更轻量,因为Modal会触发页面层级的切换,而这些方法只是在当前页面内处理触摸和视觉遮罩,体验更流畅。个人最推荐方法二,代码简洁且效果稳定!

内容的提问来源于stack exchange,提问作者SmoggeR_js

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:47:19