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

使用expo-camera时预览顶部黑屏,拍摄后照片显示正常

问题解决:Expo Camera预览顶部黑屏,拍照后显示正常

问题概述

使用 Expo 52.0.38 + expo-camera 16.0.10,摄像头预览时屏幕顶部黑屏、仅底部可见,但拍摄后的照片可正常显示,降级expo-camera版本后问题未解决。

可能原因

  • 全屏布局未适配设备安全区(状态栏、底部导航栏),导致预览区域被挤压
  • CameraView未设置合适的resizeMode,预览画面无法正确适配屏幕尺寸
  • 废弃的expo-permissions依赖引发兼容性问题
  • 组件层级样式冲突(如overlay的flex布局影响预览渲染)

解决方案

1. 适配设备安全区

使用expo-constants获取设备安全区参数,调整CameraView的布局,避免被系统栏遮挡:

import { SafeAreaInsets } from 'expo-constants';

// 修改styles中的camera样式
const styles = StyleSheet.create({
  // ...其他样式
  camera: {
    flex: 1,
    marginTop: SafeAreaInsets.top,
    marginBottom: SafeAreaInsets.bottom,
  },
});

2. 设置CameraView的resizeMode

显式指定resizeMode="cover",确保预览画面自适应屏幕:

<CameraView
  style={styles.camera}
  facing={type}
  key={type}
  ref={cameraRef}
  resizeMode="cover" // 添加此行
/>

3. 移除废弃的expo-permissions依赖

Expo 52中expo-permissions已被弃用,你的代码已正确调用各模块内置的权限请求方法,直接删除该依赖即可:

npm uninstall expo-permissions
# 或
yarn remove expo-permissions

4. 动态适配屏幕尺寸

使用useWindowDimensions获取实时屏幕尺寸,确保CameraView占满可用空间:

import { useWindowDimensions } from 'react-native';

export default function CameraScreen() {
  const { width, height } = useWindowDimensions();
  
  // ...其他逻辑
  
  return (
    // ...其他代码
    <CameraView
      style={{ ...styles.camera, width, height }} // 动态设置尺寸
      facing={type}
      key={type}
      ref={cameraRef}
      resizeMode="cover"
    >
      // ...子组件
    </CameraView>
  );
}

5. 调整Overlay布局

将overlay改为绝对定位,避免覆盖CameraView的渲染逻辑:

// 修改overlay样式
overlay: {
  position: 'absolute',
  top: 0,
  left: 0,
  right: 0,
  bottom: 0,
  justifyContent: 'center',
  alignItems: 'center',
},

修改后的完整示例代码

import { useState, useEffect, useRef } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, useWindowDimensions } from 'react-native';
import { CameraView, Camera } from 'expo-camera';
import { MediaLibrary } from 'expo-media-library';
import { SafeAreaInsets } from 'expo-constants';
import FontAwesome from '@expo/vector-icons/FontAwesome';

export default function CameraScreen() {
  const [hasPermission, setHasPermission] = useState<boolean | null>(null);
  const [type, setType] = useState<'front' | 'back'>('back');
  const [isProcessing, setIsProcessing] = useState(false);
  const cameraRef = useRef<CameraView>(null);
  const { width, height } = useWindowDimensions();

  useEffect(() => {
    (async () => {
      const { status: cameraStatus } = await Camera.requestCameraPermissionsAsync();
      const { status: mediaStatus } = await MediaLibrary.requestPermissionsAsync();
      setHasPermission(cameraStatus === 'granted' && mediaStatus === 'granted');
    })();
  }, []);

  const takePicture = async () => {
    if (cameraRef.current) {
      setIsProcessing(true);
      try {
        const photo = await cameraRef.current.takePictureAsync();
        // 此处添加照片处理逻辑
      } catch (err) {
        console.error('拍照失败:', err);
      } finally {
        setIsProcessing(false);
      }
    }
  };

  return (
    <View style={styles.container}>
      {hasPermission === null ? (
        <View style={styles.container}>
          <Text>请求摄像头权限...</Text>
        </View>
      ) : hasPermission === false ? (
        <View style={styles.container}>
          <Text>无摄像头访问权限</Text>
        </View>
      ) : (
        <CameraView
          style={{ ...styles.camera, width, height }}
          facing={type}
          key={type}
          ref={cameraRef}
          resizeMode="cover"
        >
          <View style={styles.overlay}>
            <View style={styles.guideFrame} />
          </View>

          <View style={styles.buttonContainer}>
            <TouchableOpacity
              style={styles.flipButton}
              onPress={() => {
                setType(type === 'back' ? 'front' : 'back');
              }}
            >
              <FontAwesome name="refresh" size={20} color="white" />
            </TouchableOpacity>

            <TouchableOpacity
              style={isProcessing ? styles.captureButtonDisabled : styles.captureButton}
              onPress={takePicture}
              disabled={isProcessing}
            >
              {isProcessing ? (
                <Text style={styles.captureText}>处理中...</Text>
              ) : (
                <View style={styles.captureInner} />
              )}
            </TouchableOpacity>
          </View>
        </CameraView>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  camera: {
    flex: 1,
    marginTop: SafeAreaInsets.top,
    marginBottom: SafeAreaInsets.bottom,
  },
  overlay: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
  },
  guideFrame: {
    width: 250,
    height: 250,
    borderWidth: 2,
    borderColor: 'rgba(255, 255, 255, 0.7)',
    borderRadius: 8,
  },
  buttonContainer: {
    position: 'absolute',
    bottom: SafeAreaInsets.bottom + 40,
    left: 0,
    right: 0,
    flexDirection: 'row',
    backgroundColor: 'transparent',
    justifyContent: 'space-around',
    alignItems: 'center',
  },
  flipButton: {
    padding: 15,
  },
  captureButton: {
    width: 60,
    height: 60,
    borderRadius: 30,
    borderWidth: 2,
    borderColor: 'white',
    justifyContent: 'center',
    alignItems: 'center',
  },
  captureButtonDisabled: {
    width: 60,
    height: 60,
    borderRadius: 30,
    borderWidth: 2,
    borderColor: '#999',
    justifyContent: 'center',
    alignItems: 'center',
  },
  captureInner: {
    width: 50,
    height: 50,
    borderRadius: 25,
    backgroundColor: 'white',
  },
  captureText: {
    color: 'white',
    fontSize: 12,
  },
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:57:01