Android 从零开发一个简易的相机App

您所在的位置:网站首页 安卓app开发中的界面是怎么实现的 Android 从零开发一个简易的相机App

Android 从零开发一个简易的相机App

2024-07-09 14:41:54| 来源: 网络整理| 查看: 265

本文介绍了实现一个简易Android相机App过程中,遇到的一些问题,对Camera API的选型、通知相册更新、跳转相册、左右滑动界面切换拍照/录像,相机切换时候的高斯模糊虚化效果、相机切换的3D效果做了说明。

1. 技术选型

Android调用相机可以使用Camera1、Camera2和CameraX

1.1 Camera1

Camera1的API相对复杂,且Google在Android 5.0的时候,就已经停止维护了。 但由于种种原因,有时候不得不使用Camera1的API。 如果必须要使用,建议参照 Camera1Java 这个Github库,写的还挺详细的。 同时,还有Camera1的官方文档 : Camera1 API

1.2 Camera2

Android5.0以上支持Camera2的API,如果使用Camera2,可以看我的博客 : 十分钟实现 Android Camera2 相机预览 十分钟实现 Android Camera2 相机拍照 十分钟实现 Android Camera2 视频录制 还有官方的文档 : Android Developers | Camera2 overview

也可以直接使用Github上的一个封装库 CameraView,使用起来比较简单, 它支持使用Camera1或Camera2作为引擎,进行图片的拍摄和视频的捕捉。

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CameraView camera = findViewById(R.id.camera); camera.setLifecycleOwner(this); }

具体详见 官方文档 CameraView官方文档

Android5.0以上支持Camera2,但 Android 5.0 及更高版本的设备可能并不支持所有相机 API2 功能。 不是所有Android设备都支持完整的Camera2功能, 现在都2022了, Camera2出来都有8年左右了, Android车机上还有在使用低版本HAL的, 就会导致Camera2一些高级功能都没法使用。详见 Android Camera2 综述

1.3 Camera X

CameraX 是 Jetpack 的新增库。基于Camera2开发,向上提供更简洁的API接口,向下处理了各种厂商机型的兼容性问题,有助于在众多设备上打造一致的开发者体验。

Camera X 用起来也很简单

val preview = Preview.Builder().build() val viewFinder: PreviewView = findViewById(R.id.previewView) // The use case is bound to an Android Lifecycle with the following code val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // PreviewView creates a surface provider and is the recommended provider preview.setSurfaceProvider(viewFinder.getSurfaceProvider())

具体详见我的另一篇博客 Android 使用CameraX实现预览/拍照/录制视频/图片分析/对焦/切换摄像头等操作

这里,我选用了CameraX来进行相机的开发。

1.4 扩展知识 : Android Camera HAL

HAL(Hardware Abstraction Layer),即Android 的Camera硬件抽象层。 HAL 位于相机驱动程序和更高级别的 Android 框架之间,它定义了必须实现的接口,以便应用可以正确地操作相机硬件。 HAL 可定义一个标准接口以供硬件供应商实现,可让Android忽略较低级别的驱动程序实现。HAL实现通常会内置在共享库模块(.so)中。

Android Developers | HAL介绍

接下来来介绍下开发简易相机App的时候,遇到的问题

2. 通知相册更新

当我们拍摄了一张图片之后,如果不通知系统更新相册,那么在相册中是找不到这张图片的。 所以当我们拍摄了图片后,必须要通知系统,让相册更新这张图片。

首先,新建一个FileUtils类,将需要保存的图片存储在该路径下

object FileUtils { val FILENAME = "yyyy-MM-dd-HH-mm-ss-SSS" val PHOTO_EXTENSION = ".jpg" /** Helper function used to create a timestamped file */ fun createFile(baseFolder: File, format: String, extension: String) = File( baseFolder, SimpleDateFormat(format, Locale.US) .format(System.currentTimeMillis()) + extension ) /** Use external media if it is available, our app's file directory otherwise */ fun getOutputDirectory(context: Context): File { val appContext = context.applicationContext val mediaDir = context.externalMediaDirs.firstOrNull()?.let { File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() } } return if (mediaDir != null && mediaDir.exists()) mediaDir else appContext.filesDir } fun getMoviesDirectory(context: Context): File { var externalDirectory = Environment.getExternalStorageDirectory() return File(externalDirectory, "Movies") } }

然后保存图片后

//这里是异步线程 File outputDirectory = FileUtils.INSTANCE.getOutputDirectory(context); File myCaptureFile = FileUtils.INSTANCE.createFile(outputDirectory, FileUtils.INSTANCE.getFILENAME(), FileUtils.INSTANCE.getPHOTO_EXTENSION()); //写入文件 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile)); bm.compress(Bitmap.CompressFormat.JPEG, 100, bos); bos.flush(); bos.close();

最后,通知系统更新相册

//把图片保存后声明这个广播事件通知系统相册有新图片到来 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri uri = Uri.fromFile(myCaptureFile); Log.d(TAG, "Photo capture succeeded:" + myCaptureFile.getPath()); intent.setData(uri); context.sendBroadcast(intent);

需要注意的是,这里没有启用分区存储,如果要适配分区存储的话,请看这几篇文章 Android 10 分区存储完全解析 Android Developer : 访问共享存储空间中的媒体文件 支持 Android 12,全版本保存图片到相册方案

3. 跳到相册

相机里还有一个跳转到全部相册的功能 首先,是去网上找到了一个跳转到相册的方法

3.1 使用Intent.ACTION_PICK val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) if (isVideo){ intent.type = "video/*" }else{ intent.type = "image/*" } startActivity(intent)

详见 android如何调系统用相册并处理返回

但是这其实是一个选择图片的Intent,并不是跳转到真的相册。 后来想到,可以通过隐式Intent跳转到系统相册的App,这里以华为的相册为例

3.2 反编译获得隐式intent 3.2.1 查找包名并导出

首先,我们需要先查找到华为相册的包名,导出华为相册app

// 第一步 : 查看包名 adb shell am monitor //第二步 : 查看该包名的存放路径 adb shell pm path com.huawei.photos //第三步 : 导出到电脑上 adb pull 路径地址

具体详见 用adb导出某个app

3.2.2 使用dex2jar进行反编译

我们先解压apk,获取到dex文件,然后使用dex2jar进行反编译 需要注意的是,需要把dex版本修改为036,高版本已不支持反编译了。

d2j-dex2jar classes.dex

反编译成功后,我们使用jd-gui进行打开 同时,通过apktool我们可以反编译得到AndroidManifest.xml 查看Manifest可以得知,相册app的入口是GalleryMain

在这里插入图片描述 可以看到,当tab为1的时候,会切换到albums这个tab,这个就是我们想要的。

最终的itnent为

if (RomUtils.isHuawei()) { val intent = Intent() intent.setClassName("com.huawei.photos", "com.huawei.gallery.app.GalleryMain") intent.putExtra("tab", 1) startActivity(intent) } 4. 左右滑动界面切换拍照/录像

一般相机左右滑动之后可以切换拍照/录像功能,我们对根view进行touchEvent监听即可

private var mPosX = 0F private var mPosY = 0F private var mCurPosX = 0F private var mCurPosY = 0F binding.rootView.setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { mPosX = event.x mPosY = event.y } MotionEvent.ACTION_MOVE -> { mCurPosX = event.x mCurPosY = event.y } MotionEvent.ACTION_UP -> if (mCurPosX - mPosX > 0 && Math.abs(mCurPosX - mPosX) > 120 ) { //往左滑 } else if (mCurPosX - mPosX 120 ) { //往右滑 } } true } 5. 相机切换时候虚化,实现高斯模糊效果

我这里使用到了AndroidUtilCode里的工具类ImageUtils,使用ImageUtils.fastBlur来进行高斯模糊效果的处理

val originBitmap = binding.previewView.bitmap val blurBitmap = ImageUtils.fastBlur(originBitmap, 0.25F, 25F) binding.imgBlur.setImageBitmap(blurBitmap)

这里需要注意的有两点

用户点击切换后,需要同步先处理好高斯模糊的效果,再进行摄像头的切换,这个处理时间大概在200ms,对于用户几乎是无感知的CameraX 默认使用的implementationMode为performance,我是改为compatible取到的图像角度才是正常的,关于implementationMode可以看 CameraX 实现预览文档

效果如下 在这里插入图片描述

6. 相机切换 3D翻转效果

市面上主流的相机,切换前后摄像头的时候,会有3D翻转的效果。 网上找到了这篇文章 手把手教你实现Android开发中的3D卡片翻转效果!,自己按照文中步骤实现了一下

Rotate3dAnimation.kt

/** * An animation that rotates the view on the Y axis between two specified angles. * This animation also adds a translation on the Z axis (depth) to improve the effect. */ public class Rotate3dAnimation extends Animation { private final float mFromDegrees; private final float mToDegrees; private final float mCenterX; private final float mCenterY; private final float mDepthZ; private final boolean mReverse; private Camera mCamera; /** * Creates a new 3D rotation on the Y axis. The rotation is defined by its * start angle and its end angle. Both angles are in degrees. The rotation * is performed around a center point on the 2D space, definied by a pair * of X and Y coordinates, called centerX and centerY. When the animation * starts, a translation on the Z axis (depth) is performed. The length * of the translation can be specified, as well as whether the translation * should be reversed in time. * * @param fromDegrees the start angle of the 3D rotation //起始角度 * @param toDegrees the end angle of the 3D rotation //结束角度 * @param centerX the X center of the 3D rotation //x中轴线 * @param centerY the Y center of the 3D rotation //y中轴线 * @param reverse true if the translation should be reversed, false otherwise//是否反转 */ public Rotate3dAnimation(float fromDegrees, float toDegrees, float centerX, float centerY, float depthZ, boolean reverse) { mFromDegrees = fromDegrees; mToDegrees = toDegrees; mCenterX = centerX; mCenterY = centerY; mDepthZ = depthZ;//Z轴移动的距离,这个来影响视觉效果,可以解决flip animation那个给人看似放大的效果 mReverse = reverse; } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); mCamera = new Camera(); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final float fromDegrees = mFromDegrees; float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); final float centerX = mCenterX; final float centerY = mCenterY; final Camera camera = mCamera; final Matrix matrix = t.getMatrix(); Log.i("interpolatedTime", interpolatedTime+""); camera.save(); if (mReverse) { camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime); } else { camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime)); } camera.rotateX(degrees); camera.getMatrix(matrix); camera.restore(); matrix.preTranslate(-centerX, -centerY); matrix.postTranslate(centerX, centerY); } }

Rotate3dManager.kt

class Rotate3dManager(val photo1: View) { private var centerX = 0 private var centerY = 0 private val depthZ = 400 private val duration = 300 private var closeAnimation: Rotate3dAnimation? = null /** * 卡牌文本介绍关闭效果:旋转角度与打开时逆行即可 */ private fun initCloseAnim() { closeAnimation = Rotate3dAnimation( 360F, 270F, centerX.toFloat(), centerY.toFloat(), depthZ.toFloat(), true ) closeAnimation!!.setDuration(duration.toLong()) closeAnimation!!.setFillAfter(true) closeAnimation!!.setInterpolator(AccelerateInterpolator()) closeAnimation!!.setAnimationListener(object : Animation.AnimationListener { override fun onAnimationStart(animation: Animation) { } override fun onAnimationRepeat(animation: Animation) {} override fun onAnimationEnd(animation: Animation) { val rotateAnimation = Rotate3dAnimation( 90F, 0F, centerX.toFloat(), centerY.toFloat(), depthZ.toFloat(), false ) rotateAnimation.duration = duration.toLong() rotateAnimation.fillAfter = true rotateAnimation.interpolator = DecelerateInterpolator() photo1!!.startAnimation(rotateAnimation) } }) } fun operate() { if (photo1.width operate() } return } //以旋转对象的中心点为旋转中心点,这里主要不要再onCreate方法中获取,因为视图初始绘制时,获取的宽高为0 centerX = photo1.width / 2 centerY = photo1.height / 2 if (closeAnimation == null) { initCloseAnim() } //用作判断当前点击事件发生时动画是否正在执行 if (closeAnimation!!.hasStarted() && !closeAnimation!!.hasEnded()) { return } photo1!!.startAnimation(closeAnimation) } }

然后,对想要翻转的View进行使用即可

val rotate3dManager = Rotate3dManager(targetView) rotate3dManager.operate()

效果如下 在这里插入图片描述

github地址 : DialogFlipTest

转载:https://blog.csdn.net/EthanCo/article/details/126260794



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭