安卓自定义照相机(横竖屏感应)

实现功能:

通过安卓自带的SensorManager(传感器)来判断,当用户横向拍照时照片出来是竖向显示的

实现代码:

final Intent intent = new Intent(TakePic_Activity.this, PhotoActivity.class);
String url=Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
        + "PICvkl" + File.separator + szCarNum + szCarNumType + StrCode + ".jpg";
intent.putExtra("uri", url);
startActivityForResult(intent,55);

PhotoActivity

public class PhotoActivity extends Activity {

    private SurfaceView surfaceView;        //照片显示surfaceview
    private Button takephotoBtn;            //拍照按钮
    private TextView nameTv;                //项目名
    private SurfaceHolder surfaceHolder;    //surfaceview控制器
    private Camera camera;
    private ImageView lightImg;             //闪光灯
    private boolean isLightOpen = false;      //闪光灯是否开启
    ProgressDialog dialog;
    private String uri = "";
    /**
     * 新加缩放控件
     */
    private SeekBar mZoomSeekBar;
    private ScreenSwitchUtils instance;//传感器工具类对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_photo);
        instance = ScreenSwitchUtils.init(getApplicationContext());
        initView();
        initListener();
    }

    @Override
    protected void onStart() {
        super.onStart();
        instance.start(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        instance.stop();
    }

    private void initView() {
        if (getIntent() != null) {
            uri = getIntent().getStringExtra("uri");
            Log.e("lcb", "照片的保留地方:" + uri);
        }
        nameTv = (TextView) findViewById(R.id.name_tv);
        surfaceView = (SurfaceView) findViewById(R.id.surface_view);
        takephotoBtn = (Button) findViewById(R.id.takephoto_btn);
        lightImg = (ImageView) findViewById(R.id.light_img);
        mZoomSeekBar = (SeekBar) findViewById(R.id.zoomSeekBar);
        //设置标题文字   (请横拍)项目名:
        nameTv.setText("支持横竖拍,竖拍左右倾斜不要超过45度\n横拍请向左,保证\"拍照\"按钮在右边");
    }

    private void initListener() {
        surfaceHolder = surfaceView.getHolder();                        //设置Holder
        surfaceHolder.addCallback(surfaceCallback);                     //加入回调
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //设置缓冲类型
        //  图片大小在200K-600K之间 (检测线的规格是2080X1560)
        surfaceHolder.setFixedSize(3264, 2448);
        //闪光灯开关控制器
        lightImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isLightOpen) {
                    if (camera != null) {
                        Camera.Parameters parameters = camera.getParameters();
                        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                        camera.setParameters(parameters);
                        camera.startPreview();
                    }
                    isLightOpen = true;
                    lightImg.setImageResource(R.drawable.light_down);
                } else {
                    if (camera != null) {
                        Camera.Parameters parameters = camera.getParameters();
                        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                        camera.setParameters(parameters);
                        camera.startPreview();
                    }
                    isLightOpen = false;
                    lightImg.setImageResource(R.drawable.light_up);
                }
            }
        });

        //点击拍照按钮进行拍照。
        takephotoBtn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View arg0) {
                /*
                 * shutter:快门被按下
                 * raw:相机所捕获的原始数据
                 * jpeg:相机处理的数据
                 */
                takePhoto();
            }
        });
    }

    Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            //保存图片
            new SavePictureTask().execute(data, null, null);
            //开始预览
            camera.startPreview();
        }
    };

    /**
     * 3、异步保存图片。
     */
    class SavePictureTask extends AsyncTask {
        @Override
        protected String doInBackground(byte[]... params) {
            File file = new File(uri);
            try {
//                BitmapFactory.Options options=new BitmapFactory.Options();
//                options.inSampleSize=8;

                Bitmap bitmap = BitmapFactory.decodeByteArray(params[0], 0, params[0].length);
                //水印格式:流水号:XXX  车辆识别代号:XXX 时间:XXX  (如果有车牌号的,则把车牌号也输入上去)查验员代号:XXX  红色 左上方

                Log.v("lcb","当前角度:"+instance.getOrientation()+" 是否竖屏:"+instance.isPortrait());
                if (instance.isPortrait()) {
                    /*如果是竖屏压缩bitmap时候设置方向*/
                    Matrix matrix = new Matrix();
                    matrix.reset();
                    matrix.postRotate(90);
                    bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                }

                FileOutputStream fos = new FileOutputStream(file.getPath());
                //图片压缩到70%
                //如果第一张,清楚
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                fos.flush();
                fos.close();                    //关流
                bitmap.recycle();               //回收bitmap资源
                dialog.dismiss();
                setResult(Activity.RESULT_OK, null);
                finish();
            } catch (Exception e) {
                Log.e("lcb", "photo156:" + e.toString());
            }
            return null;
        }
    }

    /**
     * SurfaceHolder回调函数。重写SurfaceHolder.Callback接口的surfaceCreated、surfaceChanged、surfaceDestroyed方法。
     */
    SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
        private static final String TAG = "CameraPreview";

        public void surfaceCreated(SurfaceHolder holder) {
            camera = Camera.open();                 //打开摄像头
            try {
                camera.setDisplayOrientation(90);   //设置camera预览的角度,因为默认图片是倾斜90度的
                camera.setPreviewDisplay(holder);   //设置holder主要是用于surfaceView的图片的实时预览,以及获取图片等功能
            } catch (IOException e) {
                camera.release();                   //释放
                camera = null;
            }
        }

        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                   int height) {
//            Camera.Parameters parameters = camera.getParameters();
//            parameters.setPictureSize(3264, 2448);
//            camera.setParameters(parameters);
//            camera.startPreview();                  //开始预览
//            camera.autoFocus(autoFocusCallback);
            //当SurfaceView尺寸变化时(包括设备横屏竖屏改变时时),需要重新设定相关参数
            if (holder.getSurface() == null) {
                //检查SurfaceView是否存在
                return;
            }

            //改变设置前先关闭相机
            try {
                camera.stopPreview();
            } catch (Exception e) {
                e.printStackTrace();
            }
            //使用最佳比例配置重启相机
            try {
                camera.setPreviewDisplay(holder);
                Camera.Parameters parameters = camera.getParameters();
                Camera.Size size = getBestPreviewSize(width, height);
                parameters.setPreviewSize(size.width, size.height);
                parameters.setPictureSize(3264, 2448);
                camera.setParameters(parameters);
                camera.startPreview();
            } catch (Exception e) {
                Log.d(TAG, "Error starting camera preview: " + e.getMessage());
            }
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            camera.stopPreview();   // stop preview
            camera.release();       // Release camera resources
            camera = null;
        }
    };

    /**
     * 拍照操作
     */
    private void takePhoto() {
        try {
            camera.takePicture(null, null, pictureCallback);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("lcb", "拍照操作报错:" + e.toString());
        }
        if (dialog == null) {
            dialog = new ProgressDialog(PhotoActivity.this);
            dialog.setMessage("图片处理中……");
        }
        dialog.show();
        // 2017/4/26   dialog十秒没关,自动关掉dialog
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    sleep(10000);
                    if (!PhotoActivity.this.isFinishing()) {
                        dialog.dismiss();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private Camera.Size getBestPreviewSize(int width, int height) {
        Camera.Size result = null;
        final Camera.Parameters p = camera.getParameters();
        //特别注意此处需要规定rate的比是大的比小的,不然有可能出现rate = height/width,但是后面遍历的时候,current_rate = width/height,所以我们限定都为大的比小的。
        float rate = (float) Math.max(width, height) / (float) Math.min(width, height);
        float tmp_diff;
        float min_diff = -1f;
        for (Camera.Size size : p.getSupportedPreviewSizes()) {
            float current_rate = (float) Math.max(size.width, size.height) / (float) Math.min(size.width, size.height);
            tmp_diff = Math.abs(current_rate - rate);
            if (min_diff < 0) {
                min_diff = tmp_diff;
                result = size;
            }
            if (tmp_diff < min_diff) {
                min_diff = tmp_diff;
                result = size;
            }
        }
        return result;
    }


    /**
     * 记录是拖拉照片模式还是放大缩小照片模式
     */

    private static final int MODE_INIT = 0;
    /**
     * 放大缩小照片模式
     */
    private static final int MODE_ZOOM = 1;
    private int mode = MODE_INIT;// 初始状态

    private float startDis;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        /** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            // 手指压下屏幕
            case MotionEvent.ACTION_DOWN:
                mode = MODE_INIT;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                //如果mZoomSeekBar为null 表示该设备不支持缩放 直接跳过设置mode Move指令也无法执行
                if (mZoomSeekBar == null) return true;
                //移除token对象为mZoomSeekBar的延时任务
                mHandler.removeCallbacksAndMessages(mZoomSeekBar);
//                mZoomSeekBar.setVisibility(View.VISIBLE);
                mZoomSeekBar.setVisibility(View.GONE);
                mode = MODE_ZOOM;
                /** 计算两个手指间的距离 */
                startDis = spacing(event);
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == MODE_ZOOM) {
                    //只有同时触屏两个点的时候才执行
                    if (event.getPointerCount() < 2) return true;
                    float endDis = spacing(event);// 结束距离
                    //每变化10f zoom变1
                    int scale = (int) ((endDis - startDis) / 10f);
                    if (scale != 0) {
                        int zoom = getZoom() + scale;
                        //zoom不能超出范围
                        if (zoom > getMaxZoom()) zoom = getMaxZoom();
                        if (zoom < 0) zoom = 0;
                        setZoom(zoom);
                        mZoomSeekBar.setProgress(zoom);
                        //将最后一次的距离设为当前距离
                        startDis = endDis;
                    }
                }
                break;
            // 手指离开屏幕
            case MotionEvent.ACTION_UP:
                if (mode != MODE_ZOOM) {
                    //设置聚焦
                    Point point = new Point((int) event.getX(), (int) event.getY());
                    onCameraFocus(point);
                } else {
                    //ZOOM模式下 在结束两秒后隐藏seekbar 设置token为mZoomSeekBar用以在连续点击时移除前一个定时任务
                    mHandler.postAtTime(new Runnable() {

                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            mZoomSeekBar.setVisibility(View.GONE);
                        }
                    }, mZoomSeekBar, SystemClock.uptimeMillis() + 2000);
                }
                break;
        }
        return true;
    }

    /**
     * 两点的距离
     */
    private float spacing(MotionEvent event) {
        if (event == null) {
            return 0;
        }
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    /**
     * 相机对焦  默认不需要延时
     */
    private void onCameraFocus(final Point point) {
        onCameraFocus(point, false);
    }

    /**
     * @param needDelay 是否需要延时
     * @author Longchengbin
     * @description 相机对焦
     * @since 2020-11-2 10:02
     **/
    public void onCameraFocus(final Point point, boolean needDelay) {
        if (camera == null) {
            return;
        }
//        getMaxNumFocusAreas:获取支持的对焦区域的个数
//        setFocusAreas:设置对焦区域列表
//        getFocusAreas:获取对焦区域列表
//        getMaxNumMeteringAreas: 获取支持的测光区域的个数
//        setMeteringAreas:设置测光区域列表
//        getMeteringAreas:获取测光区域列表
        Camera.Parameters parameters = camera.getParameters();
        //不支持设置自定义聚焦,则使用自动聚焦,返回
        if (parameters.getMaxNumFocusAreas() <= 0) {
            camera.autoFocus(autoFocusCallback);
            return;
        }
        List areas = new ArrayList();
        int left = point.x - 30;
        int top = point.y - 30;
        int right = point.x + 30;
        int bottom = point.y + 30;
        left = left < -1000 ? -1000 : left;
        top = top < -1000 ? -1000 : top;
        right = right > 1000 ? 1000 : right;
        bottom = bottom > 1000 ? 1000 : bottom;
        areas.add(new Camera.Area(new Rect(left, top, right, bottom), 100));

        parameters.setFocusAreas(areas);
        camera.cancelAutoFocus();//结束上一次的对焦操作,不管是有没有完成对焦
        try {
            camera.setParameters(parameters);
        } catch (Exception e) {
            Log.e("lcb", "相机对焦报错:" + e.toString());
        }
        camera.autoFocus(autoFocusCallback);//执行一次对焦操作,通过Camera.AutoFocusCallback返回对焦结果
    }


    /**
     * 当前缩放
     */
    private int mZoom;

    public int getMaxZoom() {
        if (surfaceView == null) return -1;
        Camera.Parameters parameters = camera.getParameters();
        if (!parameters.isZoomSupported()) return -1;
        return parameters.getMaxZoom() > 40 ? 40 : parameters.getMaxZoom();
    }

    public void setZoom(int zoom) {
        if (camera == null) return;
        Camera.Parameters parameters;
        //注意此处为录像模式下的setZoom方式。在Camera.unlock之后,调用getParameters方法会引起android框架底层的异常
        //stackoverflow上看到的解释是由于多线程同时访问Camera导致的冲突,所以在此使用录像前保存的mParameters。
        parameters = camera.getParameters();
        if (!parameters.isZoomSupported()) return;
        parameters.setZoom(zoom);
        camera.setParameters(parameters);
        mZoom = zoom;
    }


    public int getZoom() {
        return mZoom;
    }

    /**
     * @author Longchengbin
     * @description 自动对焦回调
     * @since 2020-11-2 10:06
     **/
    Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean b, final Camera camera) {
            if (b) {
                camera.cancelAutoFocus();
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        try {
                            sleep(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        try {
                            if (!PhotoActivity.this.isFinishing()) {
                                // TODO: 2017/7/18 三星手机长时间自动对焦失败,取消自动对焦
                                camera.autoFocus(autoFocusCallback);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            Log.e("lcb", "自动对焦回调报错:" + e.toString());
                        }
                    }
                }.start();
            }
        }
    };

}

R.layout.activity_photo




    

    

        

    

    

    

        

ScreenSwitchUtils

public class ScreenSwitchUtils {
    private int mOrientation;
    private static final String TAG = "test";

    private volatile static ScreenSwitchUtils mInstance;

    private Activity mActivity;

    // 是否是竖屏
    private boolean isPortrait = true;

    private SensorManager sm;
    private OrientationSensorListener listener;
    private Sensor sensor;

    private SensorManager sm1;
    private Sensor sensor1;
    private OrientationSensorListener1 listener1;

    /**
     * 返回ScreenSwitchUtils单例
     **/
    public static ScreenSwitchUtils init(Context context) {
        if (mInstance == null) {
            synchronized (ScreenSwitchUtils.class) {
                if (mInstance == null) {
                    mInstance = new ScreenSwitchUtils(context);
                }
            }
        }
        return mInstance;
    }

    private ScreenSwitchUtils(Context context) {
        Log.d(TAG, "初始化监听");
        // 注册重力感应器,监听屏幕旋转
        sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        if (sm != null) {
            sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        }
        //当前倾斜角度赋值
        @SuppressLint("HandlerLeak")
        Handler mHandler = new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 888:
                        int orientation = msg.arg1;
                        mOrientation = orientation;//当前倾斜角度赋值
                        if (orientation > 45 && orientation < 135) {
                            if (isPortrait) {
                                isPortrait = false;
                                Log.w("test", "切换成横屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait());
                            }
                        } else if (orientation > 135 && orientation < 225) {
                            if (!isPortrait) {
                                isPortrait = true;
                                Log.e("test", "切换成竖屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait());
                            }
                        } else if (orientation > 225 && orientation < 315) {
                            if (isPortrait) {
                                isPortrait = false;
                                Log.w("test", "切换成横屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait());
                            }
                        } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {
                            if (!isPortrait) {
                                isPortrait = true;
                                Log.e("test", "切换成竖屏 当前角度:" + getOrientation() + " 是否竖屏:" + isPortrait());
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
        };
        listener = new OrientationSensorListener(mHandler);

        // 根据 旋转之后/点击全屏之后 两者方向一致,激活sm.
        sm1 = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        if (sm1 != null) {
            sensor1 = sm1.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        }
        listener1 = new OrientationSensorListener1();
    }

    public void start(Activity activity) {
        Log.d(TAG, "开始监听");
        mActivity = activity;
        sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
    }

    public void stop() {
        Log.d(TAG, "停止监听");
        sm.unregisterListener(listener);
        sm1.unregisterListener(listener1);
    }

    /**
     * 手动横竖屏切换方向
     */
    public void toggleScreen() {
        sm.unregisterListener(listener);
        sm1.registerListener(listener1, sensor1, SensorManager.SENSOR_DELAY_UI);
        if (isPortrait) {
            isPortrait = false;
            // 切换成横屏
            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        } else {
            isPortrait = true;
            // 切换成竖屏
            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }

    /*当前是否竖屏*/
    public boolean isPortrait() {
        return this.isPortrait;
    }

    /**
     * 重力感应监听者
     */
    public class OrientationSensorListener implements SensorEventListener {
        private static final int _DATA_X = 0;
        private static final int _DATA_Y = 1;
        private static final int _DATA_Z = 2;

        public static final int ORIENTATION_UNKNOWN = -1;

        private Handler rotateHandler;

        public OrientationSensorListener(Handler handler) {
            rotateHandler = handler;
        }

        public void onAccuracyChanged(Sensor arg0, int arg1) {
        }

        public void onSensorChanged(SensorEvent event) {
            float[] values = event.values;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];
            float magnitude = X * X + Y * Y;
            // Don't trust the angle if the magnitude is small compared to the y
            // value
            if (magnitude * 4 >= Z * Z) {
                // 屏幕旋转时
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - Math.round(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                }
                while (orientation < 0) {
                    orientation += 360;
                }
            }
            if (rotateHandler != null) {
                rotateHandler.obtainMessage(888, orientation, 0).sendToTarget();
            }
        }
    }

    public class OrientationSensorListener1 implements SensorEventListener {
        private static final int _DATA_X = 0;
        private static final int _DATA_Y = 1;
        private static final int _DATA_Z = 2;

        public static final int ORIENTATION_UNKNOWN = -1;

        public OrientationSensorListener1() {
        }

        public void onAccuracyChanged(Sensor arg0, int arg1) {
        }

        public void onSensorChanged(SensorEvent event) {
            float[] values = event.values;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];
            float magnitude = X * X + Y * Y;
            //如果幅度比y值小,则不要相信角度
            if (magnitude * 4 >= Z * Z) {
                // 屏幕旋转时
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - Math.round(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                }
                while (orientation < 0) {
                    orientation += 360;
                }
            }
            if (orientation > 225 && orientation < 315) {// 检测到当前实际是横屏
                if (!isPortrait) {
                    sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
                    sm1.unregisterListener(listener1);
                }
            } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {// 检测到当前实际是竖屏
                if (isPortrait) {
                    sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
                    sm1.unregisterListener(listener1);
                }
            }
        }
    }


    /*返回当前角度*/
    public int getOrientation() {
        return mOrientation;
    }
}

 

 

你可能感兴趣的:(SensorManager,传感器,Sensor,SurfaceView,SurfaceHolder)