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
相关产品推荐
相关产品推荐

