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就是计算这个圆形,并且开启动画。

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

  • 相关阅读:
    2.9数据-paddlepaddle数据集wmt16
    2.8数据-paddlepaddle数据集uci_housing
    2.6数据-paddlepaddle数据集movielens
    2.5数据-paddlepaddle数据集imikolov
    2.4数据-paddlepaddle数据集imdb
    2.3数据-paddlepaddle数据集Conll05
    在android程序中怎么执行ifconfig命令来修改android 的ip地址,
    VMware 11安装Mac OS X 10.10 及安装Mac Vmware Tools.
    xcode7 如何真机测试
    海子
  • 原文地址:https://www.cnblogs.com/manuosex/p/4112283.html
Copyright © 2011-2022 走看看