【Android】自己定义相机的实现(支持连续拍照、前后摄像头切换、连续对焦)

摘要:
我写了一个我自己相机的演示。支持连续拍摄和相机切换。因为我从未接触过相关的编程。在Android上拍照的一般过程中,需要调用相机类Android.hardware.camera。您需要使用android.view.SurfaceView来显示每个帧的预览。在surfaceCreated中打开相机,获取相机对象,并将其设置为在surfaceview上预览;在surfaceChanged中设置相机参数;在表面损坏时释放相机。这样,一个基本上可用的、具有预览功能的自定义相机就准备好了。当设置相机参数时,保持这种一致性。在曲面视图的纵横比保持不变的情况下。

~转载请注明http://blog.csdn.net/u013015161/article/details/46921257

介绍

这几天。写了一个自己定义照相机的demo。支持连续拍照和摄像头切换。因为自己曾经没接触过相关的编程。也算是一个学习的过程,在这里做一下记录,同一时候也分享出来,并附上源代码和project。
效果如图:
这里写图片描写叙述
左上角switch切换摄像头,右边snapbutton进行拍照。

一般流程

Android进行拍照,须要调用摄像头类android.hardware.Camera。

而要进行预览。则须要用android.view.SurfaceView对每一帧的预览图进行显示。
实现自己定义相机一般流程为:
1、用addCallback给SurfaceView设置Callback接口对象,实现当中三个回调方法:surfaceCreated、surfaceChanged、surfaceDestroyed。
在surfaceCreated中打开摄像头,获得Camera对象,并设置其在surfaceview上预览;
在surfaceChanged中设置摄像头的參数;
在surfaceDestroyed释放摄像头。否则会导致退出之后其它应用无法调用摄像头,包含系统相机。
2、点击拍照button时。调用Camera对象的takePicture方法,其第三个參数为PictureCallback接口对象,当中的onPictureTaken回调方法參数中有一个byte数组。存储了拍摄到的图片数据,在方法中保存到本地就可以。
这样,一个基本可用、带预览的自己定义相机就做好了。但这样还远远不够。因为会出现各种各样的问题。

主要问题

预览变形

这个是最头疼的问题。

首先要知道3个宽高比:摄像头分辨率(PictureSize)宽高比、预览分辨率(PreviewSize)宽高比以及用作预览的SurfaceView的宽高比。

假设要让预览不变形,这三个宽高比须要保持一致。这样的一致性的保持在设置摄像头參数时进行。代码例如以下:

public void setCameraAndDisplay(int width, int height)
    {
        Camera.Parameters parameters = camera.getParameters();
        /*获取摄像头支持的PictureSize列表*/
        List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
        /*从列表中选取合适的分辨率*/
        Size picSize = CameraUtils.getProperSize(pictureSizeList, ((float)width)/height);
        if(null != picSize)
        {
            parameters.setPictureSize(picSize.width, picSize.height);
        }
        else
        {
            picSize = parameters.getPictureSize();
        }
        /*获取摄像头支持的PreviewSize列表*/
        List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
        Size preSize = CameraUtils.getProperSize(previewSizeList, ((float)width)/height);
        if(null != preSize)
        {Log.v("TestCameraActivityTag", preSize.width + "," + preSize.height);
            parameters.setPreviewSize(preSize.width, preSize.height);
        }

        /*依据选出的PictureSize又一次设置SurfaceView大小*/
        float w = picSize.width;
        float h = picSize.height;
        surfaceView.setLayoutParams(new RelativeLayout.LayoutParams( (int)(height*(w/h)), height)); 

        parameters.setJpegQuality(100); // 设置照片质量  
        if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
        {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        }



        camera.cancelAutoFocus();//仅仅有加上了这一句,才会自己主动对焦。  
        camera.setDisplayOrientation(0);
        camera.setParameters(parameters);
    }

方法里传进去的參数为SurfaceView如今的宽高。在保证surfaceview宽高比不变的情况下(比方为了保证全屏预览)。分别去寻找符合条件的PictureSize和PreviewSize。假设找不到,就返回默认宽高比(我设置为4:3)的PictureSize和PreviewSize, 同一时候为保证3个宽高比一致,surfaceView的宽高比也要重设。
提供寻找合适的PictureSize和PreviewSize方法的类例如以下:

public class CameraUtils {

    public static Size getProperSize(List<Size> sizeList, float displayRatio)
    {
        //先对传进来的size列表进行排序
        Collections.sort(sizeList, new SizeComparator());

        Size result = null;
        for(Size size: sizeList)
        {
            float curRatio =  ((float)size.width) / size.height;
            if(curRatio - displayRatio == 0)
            {
                result = size;
            }
        }
        if(null == result)
        {
            for(Size size: sizeList)
            {
                float curRatio =  ((float)size.width) / size.height;
                if(curRatio == 3f/4)
                {
                    result = size;
                }
            }
        }
        return result;
    }

    static class SizeComparator implements Comparator<Size>
    {

        @Override
        public int compare(Size lhs, Size rhs) {
            // TODO Auto-generated method stub
            Size size1 = lhs;
            Size size2 = rhs;
            if(size1.width < size2.width 
                    || size1.width == size2.width && size1.height < size2.height)
            {
                return -1;
            }
            else if(!(size1.width == size2.width && size1.height == size2.height))
            {
                return 1;
            }
            return 0;
        }

    }
}

因为不同的手机返回的支持分辨率的排序不一样(我手中一款联想从小到大排序,而还有一部nexus 4从大到小排序),所以须要先对列表统一进行从小到大排序。这样,方法返回的就是符合条件宽高比的最大分辨率,能够保证照片的清晰度。

照片方向错误

解决方法是监听手机方向的改变。监听到方向发生变化,就调用Camera.Parameters 对象(Camera对象调用getParameters()方法获得)的setRotation方法又一次设置成像方向。


监听的方法是。在Activity设置一个实现了OrientationEventListener接口 的对象。并调用其enable()方法激活。

IOrientationEventListener iOriListener;
... ...
iOriListener.enalbe();

实现OrientationEventListener接口的类:

public class IOrientationEventListener extends OrientationEventListener{

        public IOrientationEventListener(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
        }


        @Override
        public void onOrientationChanged(int orientation) {
            // TODO Auto-generated method stub
            if(ORIENTATION_UNKNOWN == orientation)
            {
                return;
            }
            CameraInfo info = new CameraInfo();
            Camera.getCameraInfo(camera_id, info);
            orientation = (orientation + 45) / 90 * 90;
            int rotation = 0;
            if(info.facing == CameraInfo.CAMERA_FACING_FRONT)
            {
                rotation = (info.orientation - orientation + 360) % 360;
            }
            else
            {
                rotation = (info.orientation + orientation) % 360;
            }
            if(null != camera)
            {
                Camera.Parameters parameters = camera.getParameters();
                parameters.setRotation(rotation);
                camera.setParameters(parameters);
            }

        }

    }

当中设置rotation的公式由google官方提供。

预览方向错误

因为我设置了相机的Activity的方向为ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,即横屏模式,所以没有遇到这个问题。
只是上一篇我转载的博文里,有提到相关问题的解决。相关代码摘录出来。供大家參考。

下面代码放在surfaceview的callback中surfaceChanged方法里执行:

if(this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE)

{

//假设是竖屏

parameters.set(“orientation”, “portrait”);

//在2.2以上能够使用

//camera.setDisplayOrientation(90);

}

else

{

parameters.set(“orientation”, “landscape”);

//在2.2以上能够使用

//camera.setDisplayOrientation(0);

}

连续对焦

通过连续对焦,能够拍出清晰的照片。代码在上面的代码段里出现过

/*先推断是否支持,否则可能报错*/
        if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
        {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        }
        camera.cancelAutoFocus();//仅仅有加上了这一句,才会自己主动对焦。  

照相机Activity的代码就不全贴了,更详细的能够參考demoproject。

project下载

点此下载自己定义相机demoproject

如过上面那句话点击无效 ,说明资源还在审核。。。

能够去百度云盘下载:
http://pan.baidu.com/s/1c077rDA

因为本人手中測试机型仅仅有两台,执行了上述demo的朋友方便的话能够 告知下执行效果。

免责声明:文章转载自《【Android】自己定义相机的实现(支持连续拍照、前后摄像头切换、连续对焦)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Sping+ActiveMQ整合SQL Server 排名函数下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android 利用SurfaceView进行图形绘制

SurfaceView使用介绍 SurfaceView是View的一个特殊子类,它的目的是另外提供一个线程进行绘制操作。 要使用SurfaceView进行绘制,步骤如下: 1.用SurfaceView进行绘制,首先要创建一个类,继承SurfaceView,同时这个类应该实现SurfaceHolder.Callback接口。 这个接口中的三个回...

软件工程项目之摄影App(总结)

软件工程项目之摄影App 心得体会: dyh:这次的项目很难做,本来想在里面添加动画效果的,但是找了很多例子都没看明白,能力还是不足够把,还有一个就是数据库在安卓课程里面刚刚涉及到,所以也还没能做出数据储存;网络储存什么的就更加不会了,只能实现简单的跳转,储存方面只会使用intent传递数据。安卓这个学期才学,看上去很简单,但是实则真的不是很简单呢,特别...

Android7.0,剪裁后提示“无法保存经过裁剪的图片”

今天在适配一下 7.0的拍照和选择照片,裁剪,发现拍照可以,选择图片也可以, 但是就是裁剪的时候不行,会弹出提示:无法保存经过裁剪的图片 后来才发现,在设置裁剪要保存的intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);的时候, 这个outUri是要使用Uri.fromFile(file)生成的,而不是使用Fil...

iOS 开发之照片框架详解(3)

http://kayosite.com/ios-development-and-detail-of-photo-framework-part-three.html 三. 常用方法的封装 虽然 PhotoKit 的功能强大很多,但基于兼容 iOS 8.0 以下版本的考虑,暂时可能仍无法抛弃 ALAssetLibrary,这时候一个比较好的方案是基于 ALAss...

判断页面是横屏还是竖屏

在h5页面中必须要在html头部加上这个 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/> 理解: 1)、content中的width指的...

Android 禁止屏幕旋转 &amp; 旋转屏幕时保持Activity内容 转

1.在应用中固定屏幕方向。 在AndroidManifest.xml的activity中加入: android:screenOrientation=”landscape” 属性即可(landscape是横向,portrait是纵向)。 OK 2.随屏幕旋转时,不重新调用onCreate。 当将手机屏幕旋转时,系统会被强制重置启动onCreate...