最近项目用到了相机拍照的功能,于是想着封装好一些通用性较好的相机调用,从百度和谷歌上查找出来的资料真的印证了“天下文章一大抄”的道理,而且它们实现的拍照功能大都存在缺陷,如聚焦问题、重复拍照问题、照片存储问题、横竖屏转换问题。一大堆的问题,而且程序的扩展性和可重用性实在不敢恭维,排版级其混乱。
最后无奈,打开API文档camera相机类,从最基础的学起,然后自己进行改进,从这里也告诉我们一个道理,API文档才是学习起点,因为它会告诉你整个实现的原理和原因,能够对整个框架有一个整体的了解,看完API文档看其他的就有事半功倍的效果,吐槽完毕,下面来正式实现。
一.实现流程
这幅图是从API文档(最好是看英文版的)整理出来的,从这副图上面我们可以看出,主要是有6步,其中难点是创建相机预览类。
二.权限声明
这个不讲了,直接加入声明权限代码,不明白的可以网上查查看
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
三 检查相机和获取相机实例
新建CameraCheck类,主要有2个方法,代码如下
public class CameraCheck {
public static boolean CheckCamera(Context mContext) {
if (mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
return true;
} else {
Toast.makeText(mContext, "相机不存在!", Toast.LENGTH_SHORT).show();
return false;
}
}
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(Context mContext) {
Camera c = null;
if (CheckCamera(mContext)) {
try {
c = Camera.open();
} catch (Exception e) {
c=null;
}
}
return c; // returns null if camera is unavailable
}
}
第一个方法用来检查相机是否存在,这个方法是来自API文档,使用方法
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)
hasSystemFeature(String name)方法返回设备是否支持name功能的真假值;
通过方法getCameraInstance返回相机的实例,通过调用该方法,mContext能够获得该相机资源,只有获得了该相机资源才能够对相机进行操作。
四.创建相机预览类(重点)
我们在拍照之前需要对取景进行预览,这里我们需要使用SurfaceView控件,关于SurfaceView控件我们先简单的了解一下(别急,磨刀不误砍柴工)。
SurfaceView是View的子类,所以它拥有View的一切方法和属性,这一点我们从命名上面就可以看出来,比如绘制方法、大小等属性;它比View多了一个Surface的东西,Surface是专门用来绘制的类,而SurfaceView可以控制surface绘制的大小、位置等等;
可能有人会问,那为什么要专门这样一个类来绘制呢?不是有OnDraw()方法吗?相比于OnDraw()方法它有很多优势,如下总结:
(1)在频繁更新UI线程的情况下,可以使用封装好的surface来频繁的更新,因为surface可以使用后台线程对UI界面进行绘制,而OnDraw()等绘制方法很难做到(除非你频繁的调用handler来更新主界面,这得多麻烦啊!);
(2)SurfaceView可以用来绘制2D或者3D图形,绘制一些动态曲线等,它显示的速度会比一般的快很多,因为他是通过硬件加速的方式来绘制的。
(3)它可以用来接受硬件的数据来绘制图像。
所以,通过以上几点我们可以知道,用它来接受相机的预览是理所当然的。那么它的使用方法是怎么样的呢?下创建一个surfaceView的继承类一般需要实现如下几个方法:
(1)surfaceCreated(SurfaceHolderholder):在该类创建的时候调用,这里一般需要实现一些初始化的工作,SurfaceHodler用来设定surface的大小位置等等;
(2)surfaceChanged(SurfaceHolderholder, int format, int width,int height)在surface大小发生改变时候调用,这里实现图形的绘制;
(3)surfaceDestroyed(SurfaceHolderholder)在surface销毁时候调用,这里一般对资源进行释放&