精华内容
下载资源
问答
  • Android自定义相机开发

    2017-11-02 11:07:17
    Android自定义相机开发 入门第一个接手的项目,如今重新拾起来整理一下。 1.添加相关权限 uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> uses-permission android:name=...

    Android自定义相机开发

    入门第一个接手的项目,如今重新拾起来整理一下。


    1.添加相关权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CAMERA"/>

    2.拍照原理

    (1)拍照流程

    1. 检测camera的存在并访问camera
      通过open(int)方法获取camera的实例,int为camera的id
      使用getParameters()获取相机当前的设置,包括预览尺寸,拍照尺寸等等参数
      如果修改了相关设置,调用setParameters(Camera.Parameters)将更改的信息重新生效
    2. 继承SurfaceView并添加SurfaceHolder接口以显示预览画面
      有需要的话使用setDisplayOrientation(int)来改变预览画面的方向
      使用setPreviewDisplay(SurfaceHolder)传递一个完整初始化的SurfaceHolder,没有surface,就没法启动预览画面
    3. 为预览画面添加你需要的布局和控件
      在拍照之前先调用startPreview()来更新预览画面
    4. 增加对拍照事件的监听
      调用takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)进行拍照,在回调函数中获得照片对象并做处理
    5. 使用拍照功能并保存照片
      预览画面会在拍照后关闭,如果还需要拍照,记得先startPreview()
      用stopPreview()来关闭预览画面
    6. 最后要释放camera
      camera使用之后一定要调用release()释放掉

    (2)

    3.参数解析

    4.结尾

    展开全文
  • 实现功能: 自定义相机开发:支持前置,后置摄像头切换,可以自动聚焦,保存图片和显示图片
  • 胡说八道 正儿八经 1. 了解下什么是EGL? 2. EGL和OpenGl ES的关系 3. EGL绘图的基本步骤 4. GlSurfaceView源码中分析EGL 总结 ... 如果要使用OpenGl来自定义相机,这个还是要...这其实一点都不奇怪,因为Android中的G...

    胡说八道

    如果要使用OpenGl来自定义相机,这个还是要了解下的。可能大多数开发者使用过OpengGL但是不知道EGL是什么?EGL的作用是什么?这其实一点都不奇怪,因为Android中的GlSurfaceView已经将EGL环境都给配置好了,你一直在使用,只是不知道他的存在罢了。很多人可能在使用OpenGl ES渲染数据的时候都带着一个疑问,渲染的数据到底到哪里去了?没看到画布,Android中的自定义view不都是有画布的吗?这篇文章就是为了解决这一系列问题而服务的。当然,如果你对自定义相机,音视频编码开发感兴趣,可以了解下我的Chat点这里

    正儿八经

    通过本文你可以了解到如下内容:1. 什么是EGL?. 2. EGL和OpenGl ES的关系 3. 使用EGL绘图的基本步骤。4. GlSurfaceView源码中分析EGL 5. 总结

    1. 了解下什么是EGL?

    EGL是什么?EGL是渲染API(如OpenGL, OpenGL ES, OpenVG)和本地窗口系统之间的接口。它处理图形上下文管理,表面/缓冲区创建,绑定和渲染同步,并使用其他Khronos API实现高性能,加速,混合模式2D和3D渲染OpenGL / OpenGL ES渲染客户端API OpenVG渲染客户端API原生平台窗口系统。这个稍微了解下就OK,你只要知道他是一个用来给OpenGl ES提供绘制界面的接口就可以了。

    EGL的作用:
    1. 与设备的原生窗口系统通信。
    2. 查询绘图表面的可用类型和配置。
    3. 创建绘图表面。
    4. 在OpenGL ES 和其他图形渲染API之间同步渲染。
    5. 管理纹理贴图等渲染资源。

    这里关于EGL的介绍就讲这么多,如果你有兴趣的话你可以继续到这里去see yi seeUnderstanding Android EGL

    2. EGL和OpenGl ES的关系

    从上面的讲解我们基本上可以知道,EGL是为OpenGl提供绘制表面的。对的,这就是OpenGl ES数据渲染画布所在了。想必到这里大家也清楚了渲染数据去处的问题了。EGL还有什么用呢?EGL可以理解为OpenGl ES ES和设备之间的桥梁。对,完全可以这么理解。

    3. EGL绘图的基本步骤

    先上图,再说话。
    简单讲解下各部分的作用:
    1. Display(EGLDisplay) 是对实际显示设备的抽象。
    2. Surface(EGLSurface)是对用来存储图像的内存区FrameBuffer 的抽象,包括Color Buffer,Stencil Buffer,Depth Buffer。
    3. Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息。

    EGL的基本使用步骤:
    1. 首先我们需要知道绘制内容的目标在哪里,EGLDisplayer是一个封装系统屏幕的数据类型,通常通过eglGetDisplay方法来返回EGLDisplay作为OpenGl ES的渲染目标,eglGetDisplay()

     if ( (mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)) == EGL14.EGL_NO_DISPLAY) {
                    throw new RuntimeException("unable to get EGL14 display");
                }
    1. 初始化显示设备,第一参数代表Major版本,第二个代表Minor版本。如果不关心版本号,传0或者null就可以了。初始化与 EGLDisplay 之间的连接:eglInitialize()
                if (!EGL14.eglInitialize(mEGLDisplay, 0, 0)) {
                    throw new RuntimeException("unable to initialize EGL14");
                }
    1. 下面我们进行配置选项,使用eglChooseConfig()方法,Android平台的配置代码如下:
    int[] attribList = {
                        EGL14.EGL_RED_SIZE, 8,
                        EGL14.EGL_GREEN_SIZE, 8,
                        EGL14.EGL_BLUE_SIZE, 8,
                        EGL14.EGL_ALPHA_SIZE, 8,
                        EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                        EGL_RECORDABLE_ANDROID, 1,
                        EGL14.EGL_NONE
                };
                EGLConfig[] configs = new EGLConfig[1];
                int[] numConfigs = new int[1];
                EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
                        numConfigs, 0);
    1. 接下来我们需要创建OpenGl的上下文环境 EGLContext 实例,这里值得留意的是,OpenGl的任何一条指令都是必须在自己的OpenGl上下文环境中运行,我们可以通过eglCreateContext()方法来构建上下文环境:
        int[] attrib_list = {
                        EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                        EGL14.EGL_NONE
                };
                mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
                        attrib_list, 0);

    eglCreateContext中的第三个参数可以传入一个EGLContext类型的变量,改变量的意义是可以与正在创建的上下文环境共享OpenGl资源,包括纹理ID,FrameBuffer以及其他Buffer资源。如果没有的话可以填写Null.
    5. 通过上面四步,获取OpenGl 上下文之后,说明EGL和OpenGl ES端的环境已经搭建完毕,也就是说OpengGl的输出我们可以获取到了。下面的步骤我们讲如何将EGl和设备屏幕连接起来。如果连接呢?当然,这时候我们就要使用EGLSurface了,我们通过EGL库提供eglCreateWindowSurface可以创建一个实际可以显示的surface.当然,如果需要离线的surface,我们可以通过eglCreatePbufferSurface创建。eglCreateWindowSurface()

         private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
          int[] surfaceAttribs = {
                        EGL14.EGL_NONE
                };
                mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
                        surfaceAttribs, 0);
    1. 通过上面的步骤,EGL的准备工作做好了,一方面我们为OpenGl ES渲染提供了目标及上下文环境,可以接收到OpenGl ES渲染出来的纹理,另一方面我们连接好了设备显示屏(这里指SurfaceView或者TextureView),接下来我们讲解如何在创建好的EGL环境下工作的。首先我们有一点必须要明确,OpenGl ES 的渲染必须新开一个线程,并为该线程绑定显示设备及上下文环境(Context)。因为前面有说过OpenGl指令必须要在其上下文环境中才能执行。所以我们首先要通过 eglMakeCurrent()方法来绑定该线程的显示设备及上下文。
    EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
    1. 当我们绑定完成之后,我们就可以进行RenderLoop循环了。这里简单说一下,EGL的工作模式是双缓冲模式,其内部有两个FrameBuffer(帧缓冲区,可以理解为一个图像存储区域),当EGL将一个FrameBuffer显示到屏幕上的时候,另一个FrameBuffer就在后台等待OpenGl ES进行渲染输出。知道调用了eglSwapBuffers这条指令的时候,才会把前台的FrameBuffers和后台的FrameBuffer进行交换,这样界面呈现的就是OpenGl ES刚刚渲染的结构了。
    mInputSurface.swapBuffers();
    1. 当然,在所有的操作都执行完之后,我们要销毁资源。特别注意,销毁资源必须在当前线程中进行,不然会报错滴。首先我们销毁显示设备(EGLSurface),然后销毁上下文(EGLContext),停止并释放线程,最后终止与EGLDisplay之间的链接,
     EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
                    EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
                    EGL14.eglReleaseThread();
                    EGL14.eglTerminate(mEGLDisplay);

    4. GlSurfaceView源码中分析EGL

    上面我们有提到过Android GlSurfaceView中已经帮忙配置好了EGL,下面我们来看下EGL在GLSurfaceView中的具体实现过程:我们平时使用GlSurfaceView怎么使用的呢?xml中布置,然后setRenderer(this),然后调用下setRenderMode()就OK了。特比简单。但是GlSurfaceView内部原理是什么呢?我们现在来一探究竟:

    特别声明:下面所有代码皆为GlSurfaView中的源码。

      public void setRenderer(Renderer renderer) {
            checkRenderThreadState();
            if (mEGLConfigChooser == null) {
                mEGLConfigChooser = new SimpleEGLConfigChooser(true);
            }
            if (mEGLContextFactory == null) {
                mEGLContextFactory = new DefaultContextFactory();
            }
            if (mEGLWindowSurfaceFactory == null) {
                mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
            }
            mRenderer = renderer;//为当前的View设置一个渲染器
            mGLThread = new GLThread(mThisWeakRef);
            //创建线程并开启
            mGLThread.start();
        }

    setRenderer函数主要干了2件事,一个是给View设置一个渲染器对象二是创建并开启渲染线程。线程start后执行guardedRun,run函数一个while(true)循环,渲染数据。

     private void guardedRun() throws InterruptedException {
                mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);//特别注意,我们将从这里开始延伸,
                ……
                 if (createEglSurface) {
                            if (LOG_SURFACE) {
                                Log.w("GLThread", "egl createSurface");
                            }
                            if (mEglHelper.createSurface()) {//创建 EGLSurface 实例
                                synchronized(sGLThreadManager) {
                                    mFinishedCreatingEglSurface = true;
                                    sGLThreadManager.notifyAll();
                                }
                            } else {
                                synchronized(sGLThreadManager) {
                                    mFinishedCreatingEglSurface = true;
                                    mSurfaceIsBad = true;
                                    sGLThreadManager.notifyAll();
                                }
                                continue;
                            }
                            createEglSurface = false;
                        }
                ……
                 if (createEglContext) {
                            if (LOG_RENDERER) {
                                Log.w("GLThread", "onSurfaceCreated");
                            }
                            GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                            if (view != null) {
                                try {
                                    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");
                                    view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);//看到这里没?1111111111111111111111111
                                } finally {
                                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                                }
                            }
                            createEglContext = false;
                        }
    
                        if (sizeChanged) {
                            if (LOG_RENDERER) {
                                Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
                            }
                            GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                            if (view != null) {
                                try {
                                    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged");
                                    view.mRenderer.onSurfaceChanged(gl, w, h);//看到这里没?22222222222222222
                                } finally {
                                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                                }
                            }
                            sizeChanged = false;
                        }
    
                        if (LOG_RENDERER_DRAW_FRAME) {
                            Log.w("GLThread", "onDrawFrame tid=" + getId());
                        }
                        {
                            GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                            if (view != null) {
                                try {
                                    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
                                    view.mRenderer.onDrawFrame(gl);//看到这里没?333333333333333333333333
                                } finally {
                                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                                }
                            }
                        }
                        ……
     }

    到这里为止,是不是感觉特别熟悉?我们已经看到了onSurfaceCreated(),onSurfaceChanged(),onDrawFrame()的回调了。在这些方法之前有个EglHelper类的创建,接下来进入EGL的配置环节。请系好安全带:

    public void start(){
            ……
         mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);//1. 获取 EGL Display 对象
            ……
            //2. 初始化与 EGLDisplay 之间的连接
             if(!mEgl.eglInitialize(mEglDisplay, version)) {
                    throw new RuntimeException("eglInitialize failed");
                }
             ……
               mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);//3. 获取 EGLConfig 对象
                /*
                    * Create an EGL context. We want to do this as rarely as we can, because an
                    * EGL context is a somewhat heavy object.
                    */
                    mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);//4 创建 EGLContext 实例
                   ……
     }

    好了,后面的请按照上面EGL的绘制步骤并对照下GlSurfaceView的源码。老衲就不在这里啰嗦了。

    总结

    总结下,看完上面应该对开篇提到的问题有了比较明确的认识了,我相信你心中已经有答案了。总结点啥呢?还是说下感慨吧,学习编程的最好方式还是看源码吧,没有什么比阅读源码学习更有效了。今天重新看GlSurfaceView又有了与以往不同的理解。最后,如果你在自定义相机的学习上面有什么问题,可以给我留言,一块交流学习。微信公公众号:aserbao。还有最后打下广告:如果你在学习Android 自定义相机的开发,可以了解下我的自定义相机的ChatAndroid 零基础开发相机

    如果觉得文章写得好,麻烦赏个赞呗,如果你觉得写的不好,欢迎留言批评指正,方便我及时复查修改。
    这里写图片描述

    展开全文
  • Java版水果管理系统源码 ...做过相机的同学都知道,相机开发一般分为五个步骤: 检测相机资源,如果存在相机资源,就请求访问相机资源,否则就结束 创建预览界面,一般是继承SurfaceView并且实现Surface
  • Android自定义相机
  • android 自定义相机

    2015-11-20 14:33:27
    项目开发中遇到三星s4相机拍照获取不到照片,自己整理了自定义相机,希望对需要的童鞋有帮助.开发工具是android studio,用eclipse的童鞋需要自己新建项目把代码拷贝出来.
  • Android 自定义相机及分析源码 使用Android 系统相机的方法: 要想让应用有相机的action,咱们就必须在清单文件中做一些声明,好让系统知道,如下 <action android:name=android.intent.action.IMAGE_CAPTURE> ...
  • android自定义相机拍照

    千次阅读 2017-10-17 09:32:29
    android自定义相机拍照

      Android中开发相机的两种方式:

        Android系统提供了两种使用手机相机资源实现拍摄功能的方法,一种是直接通过Intent调用系统相机组件,这种方法快速方便,适用于直接获得照片的场景,如上传相册,微博、朋友圈发照片等。另一种是使用相机API来定制自定义符合自己需求的相机,这种方法适用于需要定制相机界面或者开发特殊相机功能的场景,如需要对照片做裁剪、滤镜处理,添加贴纸,表情,地点标签等。(我在使用的时候发现Camera已经被摒弃了,API 21中出现了camera2这个类来代替camera类,但是笔者的测试手机还是andorid 4.4,所以还是用Camera)

            这里我就简单介绍一下我是怎么实现自定义相机的,虽然界面效果有点low,这里主要介绍一下功能,首先我们需要配置相机权限,由于我这里是将拍照的图片存储在文件夹里面的所以还需要读写文件的权限(权限如下)


    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-feature android:name="android.hardware.camera" />
        那么接下来,使用相机我们总需要一个能够看到图像的地方吧,这里Google叫我们使用SurfaceView这个类,那么SurfaceView这个类 是什么呢,首先这个类是继承View的,可以在将图像绘制在屏幕上并显示给用户。其实能够显示的原因是SurfaceView中包含一个Surface对 象,Surface是SurfaceView的可见部分.这里就不详细介绍了.
        对于自定义相机我们需要考虑传感器 以及相机的分辨率 预览图片的分辨率 在这里我暂时定义了三个传感器(加速度 ,旋转矢量,陀螺仪 )部分代码如下:
    public class SensorUtil {
    
    
       //定义3种传感器的数组,依次为加速度>旋转矢量>陀螺仪
       private static int[] mSensorTypes = {Sensor.TYPE_ACCELEROMETER
             , Sensor.TYPE_ROTATION_VECTOR, Sensor.TYPE_GYROSCOPE};
    
       //是否使用传感器或传感器是否可以使用
       private static boolean mIsStart = false;
       // 定义长度为3的数组保存传感器上一次记录的数据,0,1,2分别对应x,y,z轴数据
       // 初始化记录数据
       private static float[] mLastValues = {0f, 0f, 0f};
       //定义阈值变化的范围
       //定义加速度限定的值
       public final static float LIMIT_ACCELEROMETER = 1.0f;
       //定义旋转矢量限定值
       public final static float LIMIT_ROTATION = 0.8f;
       //定义陀螺仪限定值
       public final static float LIMIT_GYROSCOPE = 0.8f;
    
       /**
        * @return void 返回类型
        * @throws
        * @Title: restartSensor
        * @param sensorManager 传感器管理器
        * @param listener  A {@link SensorEventListener SensorEventListener} object
        * @author    */
       public static  void startSensor(SensorManager sensorManager, SensorEventListener listener) {
    
          // 获取当前机器支持的最优的传感器
          Sensor sensor =getBestSensor(sensorManager);
          // 表示未获取到所需的传感器
           if (null == sensor) {
             Log.d("Sensor", "系统不存在所需的传感器,开启定时器聚焦模式");
           }else{
             mIsStart=true;
             /**
              * * 注册传感器监听事件
              * * this,表示SensorEventListener
              * * sensor,表示对应的传感器
              * * SensorManager.SENSOR_DELAY_NORMAL,表示传感器的刷新频率
              * */
             sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);
              Log.i("Sensor", "注册传感器");
          }
       }
       /**
        * 得到所需的最优的传感器,如果没有下面3个即返回null
        * 权重比较:旋转矢量>陀螺仪>加速度
        * @Title: getBestSensor
        * @Description: 
        * @author
        * @date
        * @param sensorMag,系统的传感器管理器
        * @return Sensor
        */
       public static Sensor getBestSensor(SensorManager sensorMag){
          mIsStart=false;
          //遍历需要用到的3种传感器
          for (int i=0; i<mSensorTypes.length; i++){
             Sensor sensor = sensorMag.getDefaultSensor(mSensorTypes[i]);
             //获取到存在的传感器返回
             if (sensor!= null){
                return sensor;
             }
          }
          //为找到符合条件的传感器
          return null;
       }
       /**
        * 返回是否注册了传感器
        * @Title: isSensor
        * @Description:
        * @author
        * @date
        * @return boolean
        */
       public static boolean isStart(){return mIsStart;}
       public static void setIsStart(boolean isStart){mIsStart=isStart;}
       /**
        * 比较传感器数据变化是否在可接受的范围内
        * @Title: isOverRange
        * @Description: 
        * @author
        * @date
        * @param event,当前传回数据的传感器事件
        * @return
        * @return boolean
        * @throws
        */
       public static boolean isOverRange(SensorEvent event){
          boolean ok = false;
          //根据不同的传感器进行不同的判断
          switch (event.sensor.getType()){
          //旋转矢量传感器
          case Sensor.TYPE_ROTATION_VECTOR:
             ok = compareRotaion(event.values);
             break;
          //陀螺仪传感器
          case Sensor.TYPE_GYROSCOPE:
             ok = compareGyroscope(event.values);
             break;
          //加速度传感器
          case Sensor.TYPE_ACCELEROMETER:
             ok = compareAccelerometer(event.values);
             break;
          default:
             break;
          }
          // 保存当前的值用于比对
          if (ok) {
             mLastValues[0] = event.values[0];
             mLastValues[1] = event.values[1];
             mLastValues[2] = event.values[2];
          }
          return ok;
       }
       
       /**
        * 旋转矢量比较
        * @Title: compareRotaion
        * @Description: 
        * @author
        * @date
        * @param values,当前的数据
        * @return
        * @return boolean
        * @throws
        */
       private static boolean compareRotaion( float[] values){
    
            //比较两次变化的差异值
            float deltaX = Math.abs(values[0] - mLastValues[0]);
            float deltaY = Math.abs(values[1] - mLastValues[1]);
            float deltaZ = Math.abs(values[2] - mLastValues[2]);
            //根据差异值判断是否超过范围
          if (deltaX > SensorUtil.LIMIT_ROTATION
                || deltaY > SensorUtil.LIMIT_ROTATION
                || deltaZ > SensorUtil.LIMIT_ROTATION){
             Log.i("haha", ">>>>>overRange");
             return true;
          }
          return false;
       }
       
       /**
        * 陀螺仪比较
        * @Title: compareGyroscope
        * @Description: 
        * @author
        * @date
        * @param values,当前数据
        * @return
        * @return boolean
        * @throws
        */
       private static boolean compareGyroscope( float[] values){
          //比较两次变化的差异值
            float delateX = Math.abs(values[0] - mLastValues[0]);
            float delateY = Math.abs(values[1] - mLastValues[1]);
            float delateZ = Math.abs(values[2] - mLastValues[2]);
            //根据差异值判断是否在阈值范围类
          if (delateX > SensorUtil.LIMIT_GYROSCOPE
                || delateY > SensorUtil.LIMIT_GYROSCOPE
                || delateZ > SensorUtil.LIMIT_GYROSCOPE){
             return true;
          }
          return false;
       }
       
       /**
        * 加速度比较
        * @Title: compareGyroscope
        * @Description: 
        * @author
        * @date
        * @param values,当前数据
        * @return
        * @return boolean
        * @throws
        */
       private static boolean compareAccelerometer(float[] values){
          //比较两次变化的差异值
            float delateX = Math.abs(values[0] - mLastValues[0]);
            float delateY = Math.abs(values[1] - mLastValues[1]);
            float delateZ = Math.abs(values[2] - mLastValues[2]);
            //通过差异值判断是否在阈值内
          if (delateX > SensorUtil.LIMIT_ACCELEROMETER
                || delateY > SensorUtil.LIMIT_ACCELEROMETER
                || delateZ > SensorUtil.LIMIT_ACCELEROMETER){
    
             return true;
          }
          return false;
       }
    
    }
     自定义相机拍照快门声音(部分代码如下):

      public class  SoundUtils {
        //定义左右声道的音量大小
        public final static float LEFT_VOLUME = 1.0f;
        public final static float RIGHT_VOLUME = 1.0f;
        /**
         *
         * @Title: playerScanOkWav
         * @Description: R.raw.scan_ok
         * @author
         * @date 2
         * @param @param context    设定文件
         * @param type 0:扫描;1:拍照
         * @return void    返回类型
         * @throws
         */
        public final static void playerScanOkWav(Context context, int type){
            int sound =R.raw.scan_ok;
            if(type == 1){
                sound = R.raw.camera;
            }
            MediaPlayer mediaPlayer = MediaPlayer.create(context,sound);
            mediaPlayer.setVolume(LEFT_VOLUME, RIGHT_VOLUME);
            mediaPlayer.start();
        }
    }

     点击拍照按钮时进入预览图像界面需要 打开相机调用Camera.open()的方法:

    /**
    *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description  打开相机
     */
    public Camera getCameraInstance() {
        Camera c = null;
        try {
            c = Camera.open();
            // 打开相机异常
        } catch (Exception e) {
    
        }
        return c;
    }
    当我们拍照完成结束界面时需要启动关闭相机的功能:

    /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description关闭相机
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (flashlight) {
            if (mCamera != null) {
                Parameters params = mCamera.getParameters();
                // 关闭闪光灯
                params.setFlashMode(Parameters.FLASH_MODE_OFF);
            }
            flashlightBtn.setBackgroundResource(R.drawable.flashlightclose);
            flashlight = false;
        }
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        // 判断是否开启传感器监听并注销监听
        SensorCancellation();
    }
    传感器改变事件:

    /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description 传感器精度改变事件
     */
    @Override
    public void onAccuracyChanged(Sensor arg0, int arg1) {
    
    
    }
     /**
         *@date 创建时间 2017/4/15
         *@author
         *@company
         *@namezhongshuiping
         *@Description 传感器改变事件
         */
        @Override
        public void onSensorChanged(SensorEvent event) {
    
            // 判断相机是否准备好并且手机移动超过一定的范围
            if (mCamera != null && SensorUtil.isStart() && SensorUtil.isOverRange(event)&&!bIsFocusing) {
                // 调用自动聚焦回调
                bIsFocus = false;
                bIsFocusing = true;
                finder_view.bFocused = false;
    //            watermark.setVisibility(View.VISIBLE);
                finder_view.invalidate();
                //Log.i(TAG, "==================================onSensorChanged bIsFocus = false");
    
                mCamera.cancelAutoFocus();// 只有加上了这一句,才会自动对焦
                mCamera.autoFocus(autoFocusCB);
            }
    
    
        }
    /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description  传感器注销事件
     */
    private void SensorCancellation(){
        if (SensorUtil.isStart()) {
            SensorUtil.setIsStart(false);
            sensorMag.unregisterListener(PhotographActivity.this);
        }
    }
       匹配图片分辨率这里我循环查找该手机所以快高分辨率比由高往低查找直到找到16/9的比例的分辨率由于我这里的对焦框是固定的图片对焦框比例是按照16/9的比例绘制的:


    /**
     * 设定的屏幕的比例不是图片的比例
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description 匹配分辨率
     */
    private Size getOptimalPreviewSize(List<Size> sizes, double targetRatio) {
        if (sizes == null)
            return null;
        Size optimalSize = null;
        Collections.sort(sizes, new Comparator<Size>() {
            @Override
            public int compare(Size lhs, Size rhs) {
                return new Double(lhs.width).compareTo(new Double(rhs.width));
            }
        });
        for (int i=sizes.size()-1;i>=0;i--) {
            Size size = sizes.get(i);
            if ((( Constants.EIGHT_HUNDRED < size.width && size.width < Constants.TWO_THOUSAND)
                    || (Constants.EIGHT_HUNDRED< size.height && size.height < Constants.TWO_THOUSAND))
                    && ((size.width * 9) == (size.height * 16) )) {
                optimalSize = size;
                break;
            }
        }
        return optimalSize;
    }
    /**
     * 设置的是拍照的图片的比例
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description 匹配分辨率
     */
    private Size getOptimalPictureSize(List<Size> sizes, double targetRatio) {
        if (sizes == null)
            return null;
        Size optimalSize = null;
        Collections.sort(sizes, new Comparator<Size>() {
            @Override
            public int compare(Size lhs, Size rhs) {
                return new Double(lhs.width).compareTo(new Double(rhs.width));
            }
        });
        for (int i=sizes.size()-1;i>=0;i--) {
            Size size = sizes.get(i);
            if (((Constants.NUMBER_ONE_THOUSAN < size.width && size.width < Constants.NUMBER_TWO_THOUSAND)
                    || (Constants.NUMBER_ONE_THOUSAN < size.height && size.height < Constants.NUMBER_TWO_THOUSAND))
                    && ((size.width * 9) ==(size.height * 16) )) {
                optimalSize = size;
                break;
            }
        }
        /**如果没找到16/9的就选择最接近的*/
        if(optimalSize == null)
        {
            double dMin = 100.0;
            Size RightSize = null;
            for (Size size : sizes) {
                double fRate = size.width/(float)size.height;
                double fDistance = Math.abs(fRate - 16.0/9.0);
                //找最接近169size;
                if(fDistance < dMin)
                {
                    dMin = fDistance;
                    RightSize = size;
                }
            }
            //最接近的值赋给变量optimalSize
            optimalSize = RightSize;
        }
        return optimalSize;
    }
    对图片进行处理我们需要时需要释放资源:

     

    /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
         *@namezhongshuiping
     *@Description Activity被暂停或收回cpu和其他资源时调用时调stopPreview释放资源
     */
    public void onPause() {
        super.onPause();
        sensorMag.unregisterListener(this);
        stopPreview();
    }
    /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description 释放资源
     */
    private void stopPreview() {
        if (mCamera != null) {
            try {
                mCamera.setPreviewDisplay(null);
                mCamera.stopPreview();
            } catch (Exception e) {
    
            }
        }
    }
      在拍照场景不是很理想的情况小我们需要打开闪光灯,辅助我们达到一个比较理想的拍照场景

     

    /**
     *@date 创建时间 2017/4/15
     *@author
     *@company
     *@namezhongshuiping
     *@Description 闪光灯操作
     */
    private void openFlashLamp(){
        if (mCamera == null) {
            mCamera = getCameraInstance();
        }
        Parameters params = mCamera.getParameters();
        /**
         * 闪光灯
         */
        if (flashlight) {
            // 关闭闪光灯
            params.setFlashMode(Parameters.FLASH_MODE_OFF);
            flashlightBtn.setBackgroundResource(R.drawable.zx_code_closelight);
            flashlight = false;
        } else {
            // 打开闪光灯
            params.setFlashMode(Parameters.FLASH_MODE_TORCH);
            flashlightBtn.setBackgroundResource(R.drawable.zx_code_openlight);
            flashlight = true;
        }
        mCamera.setParameters(params);
    }
      相机的聚焦是通过传感器来时时聚焦的 这里我设置了可手动聚焦 通过点击屏幕聚焦,这里的聚焦是全屏聚焦跟原生相机不一样,原生相机是触碰点附近聚焦:

     

       /**
         *@date 创建时间 2017/4/15
         *@author
         *@company
         *@namezhongshuiping
         *@Description AutoFocusCallback自动对焦
         */
        AutoFocusCallback autoFocusCB = new AutoFocusCallback() {
            public void onAutoFocus(boolean success, Camera camera) {
                bIsFocusing = false;
                if (success) {
                    bIsFocus = true;
                    finder_view.bFocused = true;
    //                watermark.setVisibility(View.GONE);
                    finder_view.invalidate();
                } else {
                    bIsFocus = false;
                    finder_view.bFocused = false;
    //                watermark.setVisibility(View.VISIBLE);
                    finder_view.invalidate();
                }
            }
        };


       

      以上是自定义相机的部分代码详细代码如下地址:

      http://download.csdn.net/download/androidzsp/10024882




    展开全文
  • Android自定义照相机实现 近期小巫在学校有一个创新项目,也不是最近,是一个拖了很久的项目,之前一直没有去搞,最近因为要中期检查,搞得我跟小组成员一阵忙活,其实开发一款照相机软件并不太难,下面就是通过...
  • Android自定义相机

    热门讨论 2015-09-07 11:07:52
    优美的Android自定义相机模块。博客地址:http://blog.csdn.net/gao_chun/article/details/48246871
  • Android自定义相机实现身份证拍照,并加入自动对焦与图片不规则裁剪
  • Android 自定义相机Camera2
  • Android自定义相机 身份证扫描界面 支持android6.0 8.0拍照适配
  • 这是本人自己写的一个android 自定义相机(身份证) + 图片保存的代码示例,开发的工具是 android studio,需要的小伙伴,可以下载
  • 相关教程:Android开发相机或相册获取图片裁剪 Android启动相机拍照并返回图片 <RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android xmlns:tools=http://schemas.android.com/tools ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,622
精华内容 3,848
关键字:

android自定义相机开发