转自此处

概述

本篇在(四)的基础上继续对相机APP的功能进行增强。触摸对焦,就是在屏幕上点击某个点,相机就以此点内容进行对焦,保证此点最清晰;触摸测光,就是在屏幕上点击某个点,相机调整曝光亮度,保证此点亮度最为合适;二指手势缩放,就是通过手指在屏幕上的缩放,相机内容也随之进行缩放。上述三个功能也是目前相机APP较为常见的功能,我们接下来就进行实现。

触摸对焦

你要是仔细看过Camera.Parameters的官方文档的话,大概见过setFocusAreas()方法,就像字面意思一样,这个方法就是用来指定对焦区域的,而触摸对焦主要就是依靠这个方法实现。通过监听相机预览的触摸事件,获得手指触摸屏幕的坐标,然后通过setFocusAreas()指定这个对焦区域,最后应用到相机就好了。

坐标转换

手指触摸屏幕的坐标并不能直接应用于setFocusAreas(),因为相机会用到另一套坐标系,如下图所示(来自官方文档)

相机预览中心是(0, 0),左上角是(-1000, -1000),右下角是(1000, 1000)。其中蓝色的矩形就是一个对焦区域,相机以此区域进行对焦。这个坐标系可以让我们免于实际尺寸的困扰,还有一个好处就是这个坐标系不会受预览内容的旋转的影响,就是说只需要做一次坐标变换就好了。

CameraPreview中加入

Java
private static Rect calculateTapArea(float x, float y, float coefficient, Camera.Size previewSize) {float focusAreaSize = 300;int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();int centerX = (int) (x / previewSize.width - 1000);int centerY = (int) (y / previewSize.height - 1000);int left = clamp(centerX - areaSize / 2, -1000, 1000);int top = clamp(centerY - areaSize / 2, -1000, 1000);RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);return new Rect(Math.round(rectF.left), Math.round(rectF.top), Math.round(rectF.right), Math.round(rectF.bottom));
}private static int clamp(int x, int min, int max) {if (x > max) {return max;}if (x < min) {return min;}return x;
}

calculateTapArea()接收触摸点的坐标,返回转换后坐标介于[-1000, 1000]矩形。这是一个较为通用的代码,应该很容易看懂。

设置对焦区域

在得到转换后的矩形后就可以直接通过setFocusAreas()应用到相机了?实际没这么简单。直接这么做往往不能达到理想的效果,因为Android本身的问题以及设备的差异,在常用的对焦模式为continuous-picture下,setFocusAreas()可能会不工作。目前常用的解决办法是在setFocusAreas()同时修改相机对焦模式为macro等,待对焦完毕后,再将对焦模式修改为用户之前定义的。

CameraPreview中加入

Java
private static void handleFocus(MotionEvent event, Camera camera) {Camera.Parameters params = camera.getParameters();Camera.Size previewSize = params.getPreviewSize();Rect focusRect = calculateTapArea(event.getX(), event.getY(), 1f, previewSize);camera.cancelAutoFocus();if (params.getMaxNumFocusAreas() > 0) {List<Camera.Area> focusAreas = new ArrayList<>();focusAreas.add(new Camera.Area(focusRect, 800));params.setFocusAreas(focusAreas);} else {Log.i(TAG, "focus areas not supported");}final String currentFocusMode = params.getFocusMode();params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);camera.setParameters(params);camera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {Camera.Parameters params = camera.getParameters();params.setFocusMode(currentFocusMode);camera.setParameters(params);}});
}

前面大部分是设置曝光区域,很容易,getMaxNumFocusAreas()用来判断相机是否支持设定手动对焦点,如果不支持就不用瞎折腾了;cancelAutoFocus()是将相机的所以对焦完成后的回调函数都去掉,其实无关紧要。currentFocusMode就是保存用户设置的对焦方式,然后将对焦方式修改为macro,应用到相机,相机开始对焦。什么时候把对焦方式还原为用户设定的呢?当然是在相机对焦完成后,我们通过autoFocus()设定一个回调函数,当相机对焦完成后就会调用这个回调函数,我们就可以在回调函数里设置将对焦方式修改回用户设定的,然后应用到相机。

捕获触摸事件

SurfaceView就有onTouchEvent()触摸事件,我们只需要将其重载实现自己想要的功能就好了。

CameraPreview中加入

Java
public boolean onTouchEvent(MotionEvent event) {if (event.getPointerCount() == 1) {handleFocus(event, mCamera);}return true;
}

getPointerCount获取手指数目,当只有一个手指时触发对焦,直接调用handleFocus()就好了。

运行试试

现在APP就实现了触摸对焦了,运行试试吧。如下面两图就是分别以两本书的内容进行对焦,可以明显发现一本清晰一本模糊。

触摸测光

触摸测光与触摸对焦大同小异,一般来说我们希望在以指定点对焦的同时也以此点测光,调节亮度,只需要修改handleFocus()就好了。

handleFocus()中加入

Java
Rect meteringRect = calculateTapArea(event.getX(), event.getY(), 1.5f, previewSize);if (params.getMaxNumMeteringAreas() > 0) {List<Camera.Area> meteringAreas = new ArrayList<>();meteringAreas.add(new Camera.Area(meteringRect, 800));params.setMeteringAreas(meteringAreas);
} else {Log.i(TAG, "metering areas not supported");
}

getMaxNumMeteringAreas()用来判断相机是否支持设定手动测光点,如果不支持就不用瞎折腾了。在DEMO中handleFocus()的名字变为handleFocusMetering()了,因为现在不止能够进行对焦了嘛。

运行试试

现在触摸屏幕会同时完成对焦和测光,运行试试吧。

二指手势缩放

这个听起来很难,但实际很容易。首先消除一个误解,当对相机进行缩放时,无论手指是在屏幕哪个地方缩放,实际都是以预览的中心进行缩放,因为缩放时相机的角度是没有变的。所以我们只需要知道用户两只手指是在放大还是缩小,然后通过setZoom()指定缩放程度,应用到相机就好了。

手指间距

注意不同于触摸对焦,现在我们只需要知道手指是合拢还是张开,不需要知道手指的具体位置。怎么知道手指是合拢还是张开?可惜Android并没有提供这个方法,只会告诉我们有两个手指,还告诉手指的坐标;我们可以记下手指之间的间距,如果在手指移动时间距变大,那就是张开,否则就是合拢。

首先是计算手指间距,在CameraPreview中加入

Java
private static float getFingerSpacing(MotionEvent event) {float x = event.getX(0) - event.getX(1);float y = event.getY(0) - event.getY(1);return (float) Math.sqrt(x * x + y * y);
}

MotionEvent中获取两个手指的坐标(提前保证一定有两个手指),然后计算距离,很简单。

设置缩放

判断手指合拢还是张开稍后再说,现在来看在知道是合拢还是张开后,怎么设置缩放。

对于相机来说,缩放程度是介于[0, getMaxZoom()]之间的,不缩放时值为0,具体数值通过setZoom()设置,应用到相机就能看到效果了。所以只需要在每次触发设置缩放时,根据是缩小还是放大,将缩放值减1或加1,并应用到相机。对于一次缩放手势,会多次触发设置缩放,这样就形成了一个连续的缩放过程,看起来就像过渡效果了。

CameraPreview中加入

Java
private void handleZoom(boolean isZoomIn, Camera camera) {Camera.Parameters params = camera.getParameters();if (params.isZoomSupported()) {int maxZoom = params.getMaxZoom();int zoom = params.getZoom();if (isZoomIn && zoom < maxZoom) {zoom++;} else if (zoom > 0) {zoom--;}params.setZoom(zoom);camera.setParameters(params);} else {Log.i(TAG, "zoom not supported");}
}

isZoomSupported()判断相机是否支持缩放,不支持就不用瞎折腾了。getMaxZoom()获取最大缩放值,最小值为0不用获取;getZoom()获取当前缩放值,如果是放大,且当前缩放值不超过最大值,则将当前缩放值加1;如果是缩小,且当前缩放值不小于0,则将当前缩放值减1。最后应用到相机,就完成了整个过程。

捕获二指缩放

先看代码,在CameraPreview中加入

Java
private float oldDist = 1f;

并将onTouchEvent()修改为

Java
public boolean onTouchEvent(MotionEvent event) {if (event.getPointerCount() == 1) {handleFocus(event, mCamera);} else {switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_POINTER_DOWN:oldDist = getFingerSpacing(event);break;case MotionEvent.ACTION_MOVE:float newDist = getFingerSpacing(event);if (newDist > oldDist) {handleZoom(true, mCamera);} else if (newDist < oldDist) {handleZoom(false, mCamera);}oldDist = newDist;break;}}return true;}

我们只看两指手势的部分。event.getAction() & MotionEvent.ACTION_MASK获取手势类别;ACTION_POINTER_DOWN即为两只手指触摸到屏幕,此时我们通过两只手指的坐标得到手指间距,记录到成员变量oldDist中;ACTION_MOVE即为手指在屏幕上移动,对应两只手指正在缩放,缩放过程中每次手指移动都会触发。此时记录新的手指间距为newDist,并与oldDist比较,确定缩放类型,调用handleZoom()进行缩放;相机缩放完成后,将oldDist赋值为newDist,作为下一次触发ACTION_MOVE的基准,这样完成缩放。

运行试试

现在在屏幕上用手指进行缩放,就会使相机预览缩放了,运行试试吧。如下面两图就是缩放前和缩放后

一点唠叨

上面我们实现了触摸对焦,触摸测光,二指手势缩放,看起来比较简单但也还是有许多细节问题值得深入探究。本篇美中不足的就是没有给触摸和手势加上动画,比如触摸时应该在屏幕上显示一个矩形指示,缩放时应该在屏幕上显示一个进度条指示缩放程度;鉴于加上这些内容需要更多代码和一些技巧,本篇没有实现,望自行查找(参考部分有个链接涉及到这个问题)。另外对于这些功能的实现也可以有不同的策略,我只是提出我认为最合适的方法,可能不是最好的。

DEMO

本文实现的相机APP源码都放在GitHub上,如果需要请点击zhantong/AndroidCamera-TouchToFocusMeteringZoom。

参考

  • Camera | Android Developers
  • Camera.Parameters | Android Developers
  • Camera.Area | Android Developers
  • View | Android Developers
  • MotionEvent | Android Developers
  • Android SurfaceView not responding to touch events - Stack Overflow
  • Android setFocusArea and Auto Focus - Stack Overflow
  • Android Camera preview zoom using double finger touch - Stack Overflow
  • Android imageView Zoom-in and Zoom-Out - Stack Overflow
  • android camera - Draw Rectangle on SurfaceView - Stack Overflow

Android相机开发: 触摸对焦,触摸测光,二指手势缩放相关推荐

  1. android触屏对焦_Android相机开发(五): 触摸对焦,触摸测光,二指手势缩放

    Android Camera Develop: touch to focus, touch to metering, double finger touch to zoom 概述 本篇在(四)的基础上 ...

  2. android 相机编程,Android相机开发系列

    Android Camera Develop Series 简介 Android相机开发系列文章循序渐进,教你从一个没有任何功能的相机APP开始,逐步完善实现一般相机APP的各种功能,甚至还能拿来做图 ...

  3. Android游戏开发之单点触摸与多点触摸

    Android游戏开发之单点触摸与多点触摸的响应方式 游戏开发中的触摸事件 在游戏开发中监听屏幕触摸事件须要在View中重写父类onTouchEvent方法,在重写的方法中拦截用户触摸屏幕的一些信息, ...

  4. Android相机开发和遇到的坑

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/53350551 文章出自:薛瑄的博客 在Android相机开发实际开发过程中遇到了不 ...

  5. Android相机开发

    文章目录 Android相机开发 申请权限 创建一个可以预览的界面 1.创建一个新工程 2.在新创建的工程中activity中布局文件 3.创建一个相机预览的view 继承SurfaceView 4. ...

  6. Android相机开发那些坑

    最近我负责开发了一个跟Android相机有关的需求,新功能允许用户使用手机摄像头,快速拍摄特定尺寸(1:1或3:4)的照片,并支持在拍摄出的照片上做贴纸相关的操作.由于之前没有接触过Android相机 ...

  7. camera (19)---Android 相机开发的基本流程

    [Android 相机]Android 相机开发的基本流程 https://blog.csdn.net/bluewindtalker/article/details/54563910 相机开发现在有2 ...

  8. Android相机开发详解(一)

    Android相机开发详解(一) 请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客) Android相机 ...

  9. 《Android Studio开发实战》学习(二)- 聊天室

    <Android Studio开发实战>学习(二)- 聊天室 背景 聊天室布局文件的编写 聊天室代码文件的编写 运行结果 背景 在前一篇文章 1中实现了使用Android Studio开发 ...

最新文章

  1. python软件在下载库文件_python – 并行下载多个文件的库或工具
  2. Linux 操作系统原理 — 多处理器架构
  3. 【转】深入浅出REST
  4. hbase shelljava 获取多版本的值
  5. 远程唤醒_Python黑科技:在家远程遥控公司电脑,python+微信一键连接!
  6. XCTF-高手进阶区:unserialize3
  7. android 半边圆角背景,Android UI(一)Layout 背景局部Shape圆角设计
  8. mybatis 级联查询兑现_MyBatis之自查询使用递归实现 N级联动效果(两种实现方式)...
  9. HDU 3333 Turing Tree(树状数组/主席树)
  10. 数据分析师熬夜整理:最全「零售业」数据指标和使用技巧
  11. 我38岁,从外企技术高管到失业在家,只因为做错了这件事
  12. Maven刷新后jdk变成jre
  13. Linux中的ps命令
  14. 多摩川绝对值编码器STM32F103通信源码 通信实现源码及硬件实现方案,用于伺服行业开发者开发编码器接口,对于使用STM32开发电流环的人员具有参考价值
  15. html 列表实现展开和收起,实现列表展开收起效果
  16. 笔记本电脑键盘被锁如何解锁
  17. 使用context:component-scan base-package= /context:component-scan报500错误
  18. 10·24|程序员节!
  19. 服务器多系统ssd寿命检测,检测 SSD 剩余寿命 寿终正寝之前还能挽回数据
  20. 洛谷 P1162 填涂颜色 C++ 深搜 染色法

热门文章

  1. VLC对视频流做翻转、旋转
  2. Qt和海康威视混合编程
  3. 霍兰德人格分析雷达图的两个坑
  4. Google与k8s
  5. pppoe拨号.bat
  6. 在英特尔独立显卡上训练ResNet PyTorch模型
  7. python用保留字while实现无限循环_Python中无限循环需要什么条件
  8. html图片水平镜像翻转
  9. cve-2008-4609一把梭
  10. java 判断域密码到期提醒_域帐号密码过期邮件提醒