zoukankan      html  css  js  c++  java
  • android动手写控件系列——老猪叫你写相机

      前记:Android这个开源而自由的系统,为我们带来开发便利,同时也埋下太多的深坑。例如调用系统自带的相机就会出现照片丢失,或者其他各种各样的问题。因此,看来自定义一个相机十分的必要。

      要自定义相机我们无法要利用surfaceview与自带camera两把利器。

      首先了解surfaceview的基本含义:

      通常情况程序的View和用户响应都是在同一个线程中处理的,这也是为什么处理长时间事件(例如访问网络)需要放到另外的线程中去(防止阻塞当前UI线程的操作和绘制)。但是在其他线程中却不能修改UI元素,例如用后台线程更新自定义View(调用View的在自定义View中的onDraw函数)是不允许的。 如果需要在另外的线程绘制界面、需要迅速的更新界面或则渲染UI界面需要较长的时间,这种情况就要使用SurfaceView了。

      SurfaceView中包含一个Surface对象,而Surface是可以在后台线程中绘制的。Surface属于 OPhone底层显示系统。SurfaceView的性质决定了其比较适合一些场景:需要界面迅速更新、对帧率要求较高的情况。使用SurfaceView需要注意以下几点情况: SurfaceView和SurfaceHolder.Callback函数都从当前SurfaceView窗口线程中调用(一般而言就是程序的主线程)。有关资源状态要注意和绘制线程之间的同步。 在绘制线程中必须先合法的获取Surface才能开始绘制内容,在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之间的状态为合法的,另外在Surface类型为SURFACE_TYPE_PUSH_BUFFERS时候是不合法的。 额外的绘制线程会消耗系统的资源,在使用SurfaceView的时候要注意这点。

      了解了这么多,我们来开始写我们的相机。首先,需要surfaceview来判断刷新界面。同样surefaceview三要素holder,callback,created.

      使用SurfaceView 只要继承SurfaceView类并实现(1)SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView了,(2)SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知View,SurfaceHolder.Callback具有如下的接口:

      (3)surfaceCreated(SurfaceHolder holder):当Surface第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface。

      有了这些理论的铺垫,我们所需做的就是在surfaceCreated对camera打开使其聚焦,做好拍照的操作:相应的源码如下:

      

    private void initSurfaceView() {
            try {
                camera = Camera.open();// 打开硬件摄像头,这里导包得时候一定要注意是android.hardware.Camera
                Camera.Parameters parameters = camera.getParameters();// 得到摄像头的参数
                int PreviewWidth = 0;
                int PreviewHeight = 0;
                List<Camera.Size> size_list = parameters.getSupportedPreviewSizes();
    
                if (size_list.size() > 0) {
                    Iterator<Camera.Size> itor = size_list.iterator();
                    while (itor.hasNext()) {
                        Camera.Size cur = itor.next();
                        if (cur.width >= PreviewWidth
                                && cur.height >= PreviewHeight) {
                            PreviewWidth = cur.width;
                            PreviewHeight = cur.height;
                            // break;
                        }
                    }
                }
    
                WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);// 得到窗口管理器
                // Display display = wm.getDefaultDisplay();// 得到当前屏幕
    
                parameters.setPreviewSize(PreviewWidth, PreviewHeight);// 设置预览照片的大小
                parameters.setPictureFormat(PixelFormat.JPEG);// 设置照片的格式
                parameters.setJpegQuality(100);// 设置照片的质量
                System.out.println(camera.getParameters().flatten());
                List<Size> picSizeValues = camera.getParameters()
                        .getSupportedPictureSizes();
                if (picSizeValues.get(0).width > picSizeValues.get(picSizeValues
                        .size() - 1).width) {
                    for (Size size : picSizeValues) {
                        Log.v("show", size.width + "
    ");
                        if (size.width <= 1024) {
                            parameters.setPictureSize(size.width, size.height);// 设置照片的大小,默认是和
                            break;
                        }
                    }
                } else {
                    for (int i = picSizeValues.size() - 1; i >= 0; i -= 1) {
                        Log.v("show", picSizeValues.get(i).width + "
    ");
                        if (picSizeValues.get(i).width <= 1024) {
                            parameters.setPictureSize(picSizeValues.get(i).width,
                                    picSizeValues.get(i).height);
                            break;
                        }
                    }
                }
                parameters.setFlashMode(parameters.FLASH_MODE_AUTO);
                // 屏幕一样大
                camera.setParameters(parameters);
                camera.setPreviewDisplay(surface_view.getHolder());// 通过SurfaceView显示取景画面
                camera.startPreview();// 开始预览
                is_preview = true;// 设置是否预览参数为真
            } catch (IOException e) {
                // Log.e(TAG, e.toString());
            }
        }

      通过代码,我们清晰看到了我们利用camera对象,他是何物了?他是Android提供的对摄像头操作的相应的API。我们看到上面的代码,我们设置camera拍摄的范围应该控制宽度在1024以下,并且预览的实在surefaceview范围内。

      这样应该简单相机就做出来了。但是,似乎不是那么完美,我们相机似乎差了什么样的,首先要有一个手动聚焦光圈。要完成这个功能,工作量不小啊:首先需要一个focusmanager来管理聚焦的操作:他的内部应该是这样的:

    private Camera mCamera;
    
        private static final String TAG="FocusManager";
    
        private static final int FOCUS_WIDTH = 80;
    
        private static final int FOCUS_HEIGHT = 80;
        public interface FocusListener {
            public void onFocusStart(boolean smallAdjust);
    
            public void onFocusReturns(boolean smallAdjust, boolean success);
        }
    
        private int mFocusKeepTimeMs = 3000;
        private long mLastFocusTimestamp = 0;
    
        private Handler mHandler;
        private FocusListener mListener;
        private boolean mIsFocusing;
        public final FocusManager mfocusManager;
        private Object mParametersSync=new  Object();
    
        public FocusManager(Camera mCamera) {
            mHandler = new Handler();
            mIsFocusing = false;
            this.mCamera = mCamera;
            mfocusManager=this;
            Camera.Parameters params = mCamera.getParameters();
            if (params.getSupportedFocusModes().contains("auto")) {
                params.setFocusMode("auto");
            }
    
            // Do a first focus after 1 second
            mHandler.postDelayed(new Runnable() {
                public void run() {
                    checkFocus();
                }
            }, 1000);
        }
    
        public void setListener(FocusListener listener) {
            mListener = listener;
        }
    
        public void checkFocus() {
            long time = System.currentTimeMillis();
    
            if (time - mLastFocusTimestamp > mFocusKeepTimeMs && !mIsFocusing) {
                refocus();
            } else if (time - mLastFocusTimestamp > mFocusKeepTimeMs * 2) {
                // Force a refocus after 2 times focus failed
                refocus();
            }
        }
    
        public void refocus() {
            if (doAutofocus(this)) {
                mIsFocusing = true;
    
                if (mListener != null) {
                    mListener.onFocusStart(false);
                }
            }
    
        }
    
        private boolean doAutofocus(FocusManager focusManager) {
            if (mCamera != null) {
                try {
                    
                    // Trigger af
                    mCamera.cancelAutoFocus();
    
                    mHandler.post(new Runnable() {
                        public void run() {
                            try {
                                mCamera.autoFocus(mfocusManager);
                            } catch (Exception e) {
                                // Do nothing here
                            }
                        }
                    });
    
                } catch (Exception e) {
                    return false;
                }
                return true;
            } else {
                return false;
            }
        }
    
    
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            mLastFocusTimestamp = System.currentTimeMillis();
            mIsFocusing = false;
    
            if (mListener != null) {
                mListener.onFocusReturns(false, success);
            }
    
        }
    
        /***
         * 最大区域
         * @return
         */
         @SuppressLint("NewApi")
        public boolean isFocusAreaSupported() {
                if (mCamera != null) {
                    try {
                        return (getParameters().getMaxNumFocusAreas() > 0);
                    } catch (Exception e) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
    
        private Parameters getParameters() {
            Parameters Parameters=null;
            synchronized (mParametersSync) {
                if (mCamera == null) {
                    Log.w("", "getParameters when camera is null");
                    return null;
                }
    
                int tries = 0;
                while (Parameters == null) {
                    try {
                        Parameters = mCamera.getParameters();
                        break;
                    } catch (RuntimeException e) {
                        Log.e(TAG, "Error while getting parameters: ", e);
                        if (tries < 3) {
                            tries++;
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }
                        } else {
                            Log.e(TAG, "Failed to get parameters after 3 tries");
                            break;
                        }
                    }
                }
            }
    
            return Parameters;
        }
    
        @SuppressLint("NewApi")
        public void setFocusPoint(int x, int y) {
            if (x < -1000 || x > 1000 || y < -1000 || y > 1000) {
                Log.e(TAG, "setFocusPoint: values are not ideal " + "x= " + x + " y= " + y);
                return;
            }
            Camera.Parameters params = getParameters();
    
            if (params != null && params.getMaxNumFocusAreas() > 0) {
                List<Camera.Area> focusArea = new ArrayList<Camera.Area>();
                focusArea.add(new Camera.Area(new Rect(x, y, x + FOCUS_WIDTH, y + FOCUS_HEIGHT), 1000));
    
                params.setFocusAreas(focusArea);
    
                try {
                    mCamera.setParameters(params);
                } catch (Exception e) {
                }
            }
        }

      首先,有一个camera对象,来通知我们来操作那个camera对象的聚焦。

      又上而下的来扫描此对象,我们观察操作最多的是对对焦调整。在checkFocus中,在其一定时间范围内使其强制对焦。在refocus这个方法中,用户的自动对焦开始,并且监听相应对焦状态的变化,用以调整对焦光圈的变化。而在isFocusAreaSupported方法中,我们要判断camera对象所支持的参数的聚焦的最大的范围是否大于0。经过这一系列的判断逻辑,我们对camera聚焦有了控制了。

      我们接下来要做的就是需要对其对焦的光圈来唠叨唠叨。对焦光圈就是相机聚焦区域来显示了一个光圈:他的类名叫做FocusHudRing。源码如下:

      

    private FocusManager mFocusManager;
        public FocusHudRing(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            // TODO Auto-generated constructor stub
        }
    
        public FocusHudRing(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
        }
    
        public FocusHudRing(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
        }
    
        @Override
        @SuppressLint("ClickableViewAccessibility")
        protected void onFinishInflate() {
            // TODO Auto-generated method stub
            super.onFinishInflate();
            setImage(true);
        }
    
        public void setImage(boolean success) {
            // TODO Auto-generated method stub
            if (success) {
                setImageResource(R.drawable.hud_focus_ring_success);
            } else {
                setImageResource(R.drawable.hud_focus_ring_fail);
            }
        }
        
         public void setPosition(float x, float y) {
                setX(x - getWidth() / 2.0f);
                setY(y - getHeight() / 2.0f);
                applyFocusPoint();
            }
    
        private void applyFocusPoint() {
            ViewGroup parent = (ViewGroup) getParent();
            if (parent == null) return;
    
            // We swap X/Y as we have a landscape preview in portrait mode
            float centerPointX = getY() + getHeight() / 2.0f;
            float centerPointY = parent.getWidth() - (getX() + getWidth() / 2.0f);
    
            centerPointX *= 1000.0f / parent.getHeight();
            centerPointY *= 1000.0f / parent.getWidth();
    
            centerPointX = (centerPointX - 500.0f) * 2.0f;
            centerPointY = (centerPointY - 500.0f) * 2.0f;
            if (mFocusManager != null) {
                mFocusManager.setFocusPoint((int) centerPointX, (int) centerPointY);
            }
        }
        public boolean onTouch(View view, MotionEvent motionEvent) {
            super.onTouch(view, motionEvent);
    
            if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
                applyFocusPoint();
                if (mFocusManager != null) {
                    mFocusManager.refocus();
                }
            }
           return true;     
        }
    
        public FocusManager getFocusManager() {
            return mFocusManager;
        }
    
        public void setFocusManager(FocusManager mFocusManager) {
            this.mFocusManager = mFocusManager;
        }

      focushudring又不是简单存在,他又继承与hudring这个自定义控件,用户点击到了某个区域的话,这个控件就会出现在一定区域。就是依靠这个setposition控件,applyfocuspoint就是计算这个圆形,并且开启动画。

      有了这两个类以后,一个自定义聚焦控件就做好,你所做的就是监听下屏幕的手势事件,可能我说的不够清楚,大家请看代码吧?代码的位置是:

  • 相关阅读:
    net core 使用 rabbitmq
    asp.net core WebApi 返回 HttpResponseMessage
    asp.net core 2.1 WebApi 快速入门
    JQuery EasyUI combobox动态添加option
    php截取字符去掉最后一个字符
    JQuery EasyUI Combobox的onChange事件
    对于不返回任何键列信息的 selectcommand 不支持 updatecommand 的动态 sql 生成
    Access2007 操作或事件已被禁用模式阻止解决办法
    Easyui 中 Tabsr的常用方法
    Win 7 IE11不能下载文件,右键另存为也不行
  • 原文地址:https://www.cnblogs.com/manuosex/p/4112283.html
Copyright © 2011-2022 走看看