700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Android自定义相机—Camera篇

Android自定义相机—Camera篇

时间:2022-10-25 14:35:05

相关推荐

Android自定义相机—Camera篇

前言

这里主要简单介绍如何使用Camera+SurfaceView自定义相机拍照,如果是Camera2或者是TextureView的可以前往主页,后面会陆续推出系列文章。

自定义相机很多人都已经介绍得非常清楚了,这里个人一方面针对自己的实践起一个记录作用,另一方面也分享一下自己自定义相机的过程和总结。

相关Demo在文末有提供,有需要可自行前往获取~

Android使用相机的几种方式

直接调用原生相机拍照

优势:兼容性最好劣势:无法支持一些自定义场景的使用

自定义相机拍照

优势:可以根据需求实现一些原生相机无法支持的功能劣势:兼容性相对较差

调用原生相机

val nativeIntent = Intent()var uri: Uriif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {uri = FileProvider.getUriForFile(this@MainActivity,"com.n.customcamera.fileProvider",imageFile)nativeIntent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)} else {uri = Uri.fromFile(imageFile)}​nativeIntent.action = MediaStore.ACTION_IMAGE_CAPTUREnativeIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri)startActivityForResult(nativeIntent, Constants.OPEN_NATIVE_CAMERA_CODE)

注:上述需要注意两个地方:

1、uri的指定Android7.0系统以上需要进行provider配置

2、imageFile则是你需要拍照完成存储的照片文件,可以根据自己的需要自定义

自定义相机

先上效果图:

自定义相机我这里分为以下几步:

系统权限配置沉浸式配置相机布局相机预览设置相机拍照

系统权限配置

// 相机权限<uses-permission android:name="android.permission.CAMERA" />// 文件读写权限<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />// 相机自动对焦配置<uses-feature android:name="android.hardware.camera.autofocus" />

以上就可以满足自定义相机所需的全部权限了,只是需要注意相机权限、文件读写权限都需要动态获取。

沉浸式配置

沉浸式的配置是为了更好的获取到相机预览尺寸,从而避免相机预览画面变形。而沉浸式不同系统版本也存在差异化配置,这里为了简单我直接采用开源项目immersionbar进行,仓库地址:/gyf-dev/Imm… ,具体用法也很简单,只需要在自定义相机的页面oncreate中进行初始化即可实现沉浸式效果。

ImmersionBar.with(this).init()

相机布局

这里就是最简单的布局了,一个SurfaceView用于预览相机画面、一个取消按钮、一个拍照按钮:

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MyCameraActivity">​<SurfaceViewandroid:id="@+id/surface_view"android:layout_width="match_parent"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />​<LinearLayoutandroid:id="@+id/view_bottom_operate"android:layout_width="match_parent"android:layout_height="80dp"android:background="#FFF"android:gravity="center"android:orientation="horizontal"android:paddingStart="10dp"android:paddingEnd="10dp"android:visibility="visible"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent">​<androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/btn_cancle"android:layout_width="0dp"android:layout_height="50dp"android:layout_weight="1"android:gravity="center"android:text="取消"android:textColor="#000"android:src="@mipmap/icon_cancle"android:textSize="18sp" />​<androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/btn_take_picture"android:layout_width="0dp"android:layout_height="50dp"android:layout_weight="1"android:gravity="center"android:text="点击拍照"android:textColor="#000"android:src="@mipmap/icon_take_picture"android:textSize="18sp" />​<androidx.appcompat.widget.AppCompatImageViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text=""android:textColor="#000"android:textSize="18sp" /></LinearLayout>​</androidx.constraintlayout.widget.ConstraintLayout>

相机预览设置

既然是相机预览,这里就一定要先获取相机以及获取相机预览尺寸,最后才是将相机预览画面通过SurfaceView显示出来。

获取相机

由于从Android2.3开始就已经支持了多摄像头了,因此获取相机可以直接通过Camera.open(id)获取,而这个id可以根据Camera.getNumberOfCameras()获取。另外由于相机预览画面默认是横屏的,如果你是做的竖屏相机,需要旋转90度。

/*** 获取打开相机** @return*/private Camera getCustomCamera() {if (null == mCamera) {//Camera.open()方法说明:2.3以后支持多摄像头,所以开启前可以通过getNumberOfCameras先获取摄像头数目,// 再通过 getCameraInfo得到需要开启的摄像头id,然后传入Open函数开启摄像头,// 假如摄像头开启成功则返回一个Camera对象try {// 这里我直接默认使用后置摄像头mCamera = Camera.open(0);//预览画面默认是横屏的,需要旋转90度mCamera.setDisplayOrientation(90);} catch (Exception e) {}}return mCamera;}

获取最佳预览尺寸

这一步如果不动态调整surfaceView的大小其实很难兼容所有设备,不过好在目前大多数相机都是可以通过获取屏幕分辨率以及相机支持的分辨率,通过计算获取到一个最佳的预览分辨率的。

1、获取相机支持的所有预览分辨率

// 获取相机支持的所有预览尺寸,一般是从大到小倒序排列的mCamera?.parameters.supportedPictureSizes

2、计算合适的分辨率进行预览

其实这里的核心就是获取一个和SurfaceView同等分辨率比例的一个最大分辨率尺寸,这样在预览和拍照的时候相片才不会变形,由于我是将SurfaceView的尺寸设置为屏幕的大小(这也是做沉浸式的原因),所以只需要获取和屏幕一样比例的最大预览尺寸即可。以下是我的具体方式:

/*** 根据屏幕尺寸以及相机支持的分辨率获取最佳预览分辨率** @param parameters 相机参数*/private fun getPreviewSize(parameters: Camera.Parameters) {// 选择合适的图片尺寸,必须是手机支持的尺寸val sizeList = parameters.supportedPictureSizes// 如果sizeList只有一个我们也没有必要做什么了,因为就他一个别无选择if (sizeList.size > 1) {for (size: Camera.Size in sizeList) {Log.i("分辨率>>>", "Width=" + size.height + "__Height=" + size.width)// 获取当前分辨率的最大公约数val sizeGY = getGY(size.height, size.width)// 校验是否和屏幕的分辨率比例一致,如果一致则是最大if (screenWidth / screenGY == size.height / sizeGY && screenHeight / screenGY == size.width / sizeGY) {previewWidth = size.heightpreviewHeight = size.widthreturn}}} else {previewWidth = sizeList[0].heightpreviewHeight = sizeList[0].width}Log.i("分辨率", "previewWidth=" + previewWidth + "__previewHeight=" + previewHeight)}

/*** 获取两个数的公约数** @param a 数字1* @param b 数字2* * @return 公约数*/private fun getGY(a: Int, b: Int): Int {var localA = avar localB = bwhile (localA % localB != 0) {val temp = localA % localBlocalA = localBlocalB = temp}return localB}

进行相机预览

预览的核心分为以下几步:

实现surfaceViewHolder的各项回调将surfaceViewHolder设置为Camera的显示控件开启相机预览

mSurfaceHolder = binding.surfaceView.holdermSurfaceHolder.addCallback(object : SurfaceHolder.Callback {override fun surfaceCreated(holder: SurfaceHolder) {mCamera = getCustomCamera()}​override fun surfaceChanged(holder: SurfaceHolder, p1: Int, p2: Int, p3: Int) {mCamera?.let {val parameters = it.parametersgetPreviewSize(parameters)parameters.setPictureSize(previewWidth, previewHeight)// 设置自动对焦模式parameters.focusMode = Camera.Parameters.FOCUS_MODE_AUTOparameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTUREit.parameters = parameterstry {it.setPreviewDisplay(mSurfaceHolder)} catch (e: Exception) {e.printStackTrace()Toast.makeText(this@MyCameraActivity, e.message, Toast.LENGTH_SHORT).show()}it.startPreview()}}​override fun surfaceDestroyed(p0: SurfaceHolder) {if (null != mCamera) {mSurfaceHolder.removeCallback(this);mCamera?.setPreviewCallback(null);//停止预览mCamera?.stopPreview()mCamera?.lock()//释放相机资源mCamera?.release()mCamera = null}}})

拍照

拍照只需要调用Camera的takePicture方法即可:

binding.btnTakePicture.setOnClickListener {mCamera?.takePicture({}, null) { bytearray, camera ->// bytearray 就是照片数据}}}

注意事项:如果你在获取相机的时候对相机预览画面做了旋转,这里获取到照片数据需要将它旋转回来。

可以使用如下方法旋转图片:

/*** 旋转图片** @param bitmap 目标图片* @param rotation 旋转角度* @Return 旋转后的图片*/public static Bitmap getRotatedBitmap(Bitmap bitmap, int rotation) {Matrix matrix = new Matrix();matrix.postRotate(rotation);return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, false);}

至此,关于使用Camera进行自定义相机开发就介绍到这里,有兴趣的可以去下载源码查看。 源码地址:/No.N/custom…

如果有任何问题欢迎随时评论或者私信交流,如果我的分享能够帮助到你也希望能给我点赞关注~

如果有任何问题欢迎随时评论或者私信交流,如果我的分享能够帮助到你也希望能给我点赞关注~

作者:一念三千_

原文链接:/post/7185335137603682341

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

二、源码解析合集

三、开源框架合集

欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。