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

Android Studio自定义相机预览无显示仅可见拍摄按钮问题求解

问题描述

我正在Android Studio中开发自定义相机功能,目前对该技术较为陌生,已完成的开发步骤如下:

  • 已在manifest-file中完成相机使用权限的清单声明;
  • 已完成相机实例访问;
  • 已创建预览类CameraPreview;
  • 已在布局中放置预览容器。

但打开应用后只能看到「Capture」拍摄按钮,无相机预览画面。
运行环境:

  • 小米9T,Android版本:10
  • 最小SDK:API 21(Android 5.0 Lollipop)
    无法使用Kotlin(CameraX)方案。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.customcamera">
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera2.full" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.CustomCamera">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

CameraPreview.java

package com.example.customcamera;

import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;

/** 基础相机预览类 */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    public static final String TAG = "TAG";

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;
        // 注册SurfaceHolder.Callback,监听底层surface的创建和销毁事件
        mHolder = getHolder();
        mHolder.addCallback(this);
        // 已废弃的设置,但Android 3.0以下版本需要
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // Surface已创建,告知相机绘制预览的位置
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "设置相机预览失败: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // 空实现,在Activity中处理相机预览资源释放
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // 如果预览可调整或旋转,在此处处理相关事件
        // 调整尺寸或格式前请先停止预览
        if (mHolder.getSurface() == null){
            // 预览surface不存在
            return;
        }
        // 调整前停止预览
        try {
            mCamera.stopPreview();
        } catch (Exception e){
            // 忽略:尝试停止不存在的预览
        }
        // 在此处设置预览尺寸、调整大小、旋转或重设格式等操作
        // 使用新设置启动预览
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (Exception e){
            Log.d(TAG, "启动相机预览失败: " + e.getMessage());
        }
    }
}

MainActivity.java

package com.example.customcamera;

import androidx.appcompat.app.AppCompatActivity;
import android.hardware.Camera;
import android.os.Bundle;
import android.widget.FrameLayout;

public class MainActivity extends AppCompatActivity {
    private Camera mCamera;
    private CameraPreview mPreview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 创建相机实例
        mCamera = getCameraInstance();
        // 创建预览视图,并添加到Activity的容器中
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }

    /** 获取Camera对象实例的安全方法 */
    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(); // 尝试获取相机实例,参数可传0到1的摄像头ID
        }catch (Exception e){
            // 相机不可用(被占用或不存在)
        }
        return c; // 相机不可用时返回null
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"/>
    <Button
        android:id="@+id/button_capture"
        android:text="Capture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
</LinearLayout>

故障原因

你使用的设备运行Android 10,属于API 23及以上版本,相机权限属于危险权限,仅在Manifest中声明无法获取权限,必须通过动态权限申请让用户主动授权才能访问相机硬件。你没有做动态权限申请逻辑,所以相机实例实际获取失败,预览自然无法渲染。

解决方案

临时验证方案

应用运行后,进入系统应用设置→找到对应应用→开启相机权限,重启应用即可正常显示预览。

正式修复方案

在MainActivity中增加动态权限申请逻辑,修改后的示例代码如下:

package com.example.customcamera;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Bundle;
import android.widget.FrameLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private Camera mCamera;
    private CameraPreview mPreview;
    private static final int REQUEST_CAMERA_PERMISSION = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 先检查是否已经获取相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            // 未授权,发起权限申请
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
        } else {
            // 已授权,初始化相机
            initCamera();
        }
    }

    // 处理权限申请结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限申请通过,初始化相机
                initCamera();
            } else {
                // 权限被拒绝,提示用户必须授权才能使用相机功能
                Toast.makeText(this, "需要相机权限才能使用拍摄功能", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 把原来的相机初始化逻辑抽成单独方法
    private void initCamera() {
        mCamera = getCameraInstance();
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }

    /** 获取Camera对象实例的安全方法 */
    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(); // 尝试获取相机实例,参数可传0到1的摄像头ID
        }catch (Exception e){
            // 相机不可用(被占用或不存在)
        }
        return c; // 相机不可用时返回null
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 14:57:03