相机的使用 --- 拍照,相册导入

来源:互联网 发布:手机数据老是自动打开 编辑:程序博客网 时间:2024/05/21 11:05

一、相机的基本使用

1,获取SurfaceView控件的初始化

        surfaceView = (SurfaceView) findViewById(R.id.surface_sv);        holder = surfaceView.getHolder();        surfaceView.setBackgroundColor(0);        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);        holder.addCallback(this);
2,添加CallBack事件监听

    @Override    public void surfaceCreated(SurfaceHolder holder) {        startPreview(camera, holder);    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {        camera.stopPreview();        startPreview(camera, holder);    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        releaseCamera();    }
3,预览相机并设置参数

    /**     * 预览相机     */    private void startPreview(Camera camera, SurfaceHolder holder) {        try {            setupCamera(camera);            camera.setPreviewDisplay(holder);            //亲测的一个方法 基本覆盖所有手机 将预览矫正            CameraUtil.getInstance().setCameraDisplayOrientation(this, 0, camera);            camera.startPreview();            if (camera.getParameters() != null) {                Camera.Size size = camera.getParameters().getPreviewSize(); // 获取预览大小                if (Integer.parseInt(Build.VERSION.SDK) >= 8) {                    initSurfaceView(size.height, size.width);                } else {                    initSurfaceView(size.width, size.height);                }            } else {                Toast.makeText(MainActivity.this, "无法启动相机", Toast.LENGTH_SHORT).show();                finish();            }        } catch (Exception e) {            Toast.makeText(MainActivity.this, "无法启动相机", Toast.LENGTH_SHORT).show();            e.printStackTrace();            finish();        }    }

    /**     * 设置     */    private void setupCamera(Camera camera) {        try {            Camera.Parameters parameters = camera.getParameters();            if (parameters.getSupportedFocusModes().contains(                    Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);            }            //这里第三个参数为最小尺寸 getPropPreviewSize方法会对从最小尺寸开始升序排列 取出所有支持尺寸的最小尺寸            Camera.Size previewSize = CameraUtil.getInstance().getPropSizeForHeight(parameters.getSupportedPreviewSizes(), 800);            parameters.setPreviewSize(previewSize.width, previewSize.height);            Camera.Size pictrueSize = CameraUtil.getInstance().getPropSizeForHeight(parameters.getSupportedPictureSizes(), 800);            parameters.setPictureSize(pictrueSize.width, pictrueSize.height);            camera.setParameters(parameters);            /**             * 设置surfaceView的尺寸 因为camera默认是横屏,所以取得支持尺寸也都是横屏的尺寸             * 我们在startPreview方法里面把它矫正了过来,但是这里我们设置设置surfaceView的尺寸的时候要注意 previewSize.height<previewSize.width             * previewSize.width才是surfaceView的高度             * 一般相机都是屏幕的宽度 这里设置为屏幕宽度 高度自适应 你也可以设置自己想要的大小             *             */            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(screenWidth, screenHeight);            surfaceView.setLayoutParams(params);        } catch (Exception e) {            e.printStackTrace();            Toast.makeText(MainActivity.this, "无法启动相机", Toast.LENGTH_SHORT).show();            finish();        }    }
4,纠正和矫正Surfaceview

    /**     * 初始化SurfaceView     *     * @param camera_w     * @param camera_h     */    private void initSurfaceView(int camera_w, int camera_h) {        int height = MainApplication.getInstance().getHeight()                - ScreenUtil.getStatusBarHeight();        int width = MainApplication.getInstance().getWidth();        int w = (int) ((height * camera_w) / camera_h);        int h = (int) ((camera_h * width) / camera_w);        int h_outer = 0;        int w_outer = 0;        if (w <= width) {            Log.e("", "高度为屏幕高度");            h_outer = height;            w_outer = w;        } else if (h <= height) {            Log.e("", "宽度为屏幕宽度");// ..            h_outer = h;            w_outer = width;        } else {            Log.e("", "都不合适========================");        }        if (w_outer > 0) {            if (w_outer < width || h_outer < height) {                //无法铺满全屏                float scale_w = (float) width / (float) w_outer;                float scale_h = (float) height / (float) h_outer;                float scale = ((scale_h - scale_w) > 0) ? scale_h : scale_w;                w_outer = (int) (w_outer * scale);                h_outer = (int) (h_outer * scale);            }            preViewSize[0] = w_outer;            preViewSize[1] = h_outer;            FrameLayout.LayoutParams lpsurface = new FrameLayout.LayoutParams(w_outer, h_outer);//            lpsurface.setMargins((width-w_outer)/2,(height-h_outer)/2,0,0);            surfaceView.setLayoutParams(lpsurface);            Log.e("111", "相机预览:" + camera_w + "x" + camera_h);//960x1280            Log.e("111", "surfaceview :" + w_outer + "x" + h_outer);//1305x1740   1080x1440        }    }
5,合适的地方释放相机资源

    /**     * 释放相机资源     */    private void releaseCamera() {        if (camera != null) {            camera.setPreviewCallback(null);            camera.stopPreview();            camera.release();            camera = null;        }    }

此时,相机应该可以正常使用。

【在4中,适配不同的相机预览,便于获取图片不变形】

6,生命周期中的维护

    @Override    protected void onResume() {        super.onResume();        if (camera == null) {            camera = getCamera();            if (holder != null) {                startPreview(camera, holder);            }        }    }    @Override    protected void onDestroy() {        super.onDestroy();        releaseCamera();    }

    /**     * 获取Camera实例     *     * @return     */    private Camera getCamera() {        Camera camera = null;        try {            camera = Camera.open(0);        } catch (Exception e) {        }        return camera;    }

7,拍照

private void captrue() {        camera.takePicture(null, null, new Camera.PictureCallback() {            @Override            public void onPictureTaken(byte[] data, Camera camera) {                //将data 转换为位图 或者你也可以直接保存为文件使用 FileOutputStream                //这里我相信大部分都有其他用处把 比如加个水印 后续再讲解                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);                Bitmap saveBitmap = CameraUtil.getInstance().setTakePicktrueOrientation(0, bitmap);                bitmap.recycle();                bitmap = Bitmap.createScaledBitmap(saveBitmap, preViewSize[0], preViewSize[1], true);                saveBitmap.recycle();                rotateMIV.setmSrc(bitmap);                changeToShowPic();            }        });    }

二、图片处理   ---  旋转,缩放,截图
1,图片处理的主要实现类,由以下自定义控件实现
@SuppressLint("AppCompatCustomView")public class MatrixImageView extends ImageView {    private static final int MODE_NONE = 0x00123;// 默认的触摸模式    private static final int MODE_DRAG = 0x00321;// 拖拽模式    private static final int MODE_ZOOM = 0x00132;// 缩放or旋转模式    private int mode;// 当前的触摸模式    private float preMove = 1F;// 上一次手指移动的距离    private float saveRotate = 0F;// 保存了的角度值    private float rotate = 0F;// 旋转的角度    private float[] preEventCoor;// 上一次各触摸点的坐标集合    private PointF startPointF, midPointF;// 起点、中点对象    private Matrix currentMatrix, savedMatrix;// 当前和保存了的Matrix对象    /**     * 新增截图保存功能     * 旋转展示的图片和页面上截图的矩形运算,形成新的图片,将图片保存成为新图片即可!     * 1,触发事件     * 2,截图矩形获取     * 3,图片保存 -- 保存路径     */    /**     * 保存文件的路径     */    private String fileURL;    /**     * 单字Code码 -- 文件保存名称     */    private String fontCode;    /**     * 截图矩形     */    private Rect cutRect;    //原始图片    private Bitmap mSrc;    //控件的宽度    private int mWidth;    // 控件的高度    private int mHeight;    private PaintFlagsDrawFilter mDrawFilter;    int i = 0;    private Context context;    public MatrixImageView(Context context, AttributeSet attrs) {        super(context, attrs);        this.context = context;        // 初始化        init();    }    /**     * 初始化     */    private void init() {        // 实例化对象        currentMatrix = new Matrix();        savedMatrix = new Matrix();        startPointF = new PointF();        midPointF = new PointF();        mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);        // 模式初始化        mode = MODE_NONE;        Drawable drawable = getDrawable();        try {            mSrc = drawableToBitamp(drawable);        } catch (Exception e) {            mSrc = null;        }        if (mSrc == null) {            //如果为空,赋默认值            mSrc = BitmapFactory.decodeResource(getResources(), R.mipmap.desc6, null);        }    }    /**     * 计算控件的高度和宽度     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // 设置宽度        int specMode = MeasureSpec.getMode(widthMeasureSpec);        int specSize = MeasureSpec.getSize(widthMeasureSpec);        //match_parent或者设置的精确值获取        //MeasureSpec.EXACTLY        if (specMode == MeasureSpec.EXACTLY) {            mWidth = specSize;        } else {            // 由图片决定的宽            //getPaddingLeft(),getPaddingRight()这两个值是控件属性的向内偏移的距离值,所以的一起计算            //区别于layout_marginLeft,两个控件的左间距值设置            int desireByImg = getPaddingLeft() + getPaddingRight()                    + mSrc.getWidth();            // wrap_content            if (specMode == MeasureSpec.AT_MOST) {                //所以最小的值,宽度的话是左右内偏移距离之和                mWidth = Math.min(desireByImg, specSize);            } else                mWidth = desireByImg;        }        // 设置高度,部分解释同上        specMode = MeasureSpec.getMode(heightMeasureSpec);        specSize = MeasureSpec.getSize(heightMeasureSpec);        //match_parent或者设置的精确值获取        //MeasureSpec.EXACTLY        if (specMode == MeasureSpec.EXACTLY) {            mHeight = specSize;        } else {            int desire = getPaddingTop() + getPaddingBottom()                    + mSrc.getHeight();            // wrap_content            if (specMode == MeasureSpec.AT_MOST) {                mHeight = Math.min(desire, specSize);            } else                mHeight = desire;        }        //计算好的宽度以及高度是值,设置进去        setMeasuredDimension(mWidth, mHeight);    }    //drawable转bitmap    private Bitmap drawableToBitamp(Drawable drawable) {        //从控件的src获取背景,也是drawable文件获取        if (drawable instanceof BitmapDrawable) {            BitmapDrawable bd = (BitmapDrawable) drawable;            return bd.getBitmap();        }        //如果没有绘图一个,只不过是空白的图片//        int w = drawable.getIntrinsicWidth();//        int h = drawable.getIntrinsicHeight();        int w = MainApplication.getInstance().getWidth();        int h = MainApplication.getInstance().getHeight();        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);        Canvas canvas = new Canvas(bitmap);        drawable.setBounds(0, 0, w, h);        drawable.draw(canvas);        return bitmap;    }    final Paint paint = new Paint();    @Override    protected void onDraw(Canvas canvas) {        //消除锯齿, 图片旋转后的锯齿消除不成功,实在不行图片边缘加一些白色像素点        canvas.setDrawFilter(mDrawFilter);        //画经过Matrix变化后的图        canvas.drawBitmap(mSrc, currentMatrix, null);        /**         * 测试切图区域和显示区域一致         */        if (cutRect != null) {            paint.setColor(Color.RED);            canvas.drawRect(cutRect, paint);        }    }    /**     * 裁剪图片并保存到本地     */    public boolean cutBitmapAndSave() {        try {            Bitmap out = Bitmap.createBitmap(cutRect.width(), cutRect.height(), Bitmap.Config.ARGB_8888);            //画布在右上角            Canvas outC = new Canvas(out);            //平移画布到和截图区域一致【注意反向】            outC.translate(-cutRect.left, -cutRect.top);            outC.drawBitmap(mSrc, currentMatrix, null);            String fileParent = Environment.getExternalStorageDirectory().toString() + "/camera/demo/";            File fileDirectory = new File(fileParent);            if (!fileDirectory.exists()) {                boolean mkdirs = fileDirectory.mkdirs();                Log.i("parent", mkdirs + "");                if (mkdirs) {                    File file = new File(fileParent + System.currentTimeMillis() + ".png");                    if (!file.exists()) {                        boolean newFile = file.createNewFile();                        if (newFile) {                            BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);                            return true;                        } else {                            Toast.makeText(context, "创建文件失败", Toast.LENGTH_SHORT).show();                            return false;                        }                    } else {                        BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);                        return true;                    }                } else {                    Toast.makeText(context, "创建文件夹失败", Toast.LENGTH_SHORT).show();                    return false;                }            } else {                File file = new File(fileParent + System.currentTimeMillis() + ".png");                if (!file.exists()) {                    boolean newFile = file.createNewFile();                    if (newFile) {                        BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);                        return true;                    } else {                        Toast.makeText(context, "创建文件失败", Toast.LENGTH_SHORT).show();                        return false;                    }                } else {                    BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);                    return true;                }            }//            return true;        } catch (Exception e) {            e.printStackTrace();            return false;        }    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction() & MotionEvent.ACTION_MASK) {            case MotionEvent.ACTION_DOWN:// 单点接触屏幕时                savedMatrix.set(currentMatrix);                startPointF.set(event.getX(), event.getY());                //单点触摸是移动模式                mode = MODE_DRAG;                preEventCoor = null;                break;            case MotionEvent.ACTION_POINTER_DOWN:// 第二个点接触屏幕时                preMove = calSpacing(event);                if (preMove > 10F) {                    savedMatrix.set(currentMatrix);                    // 计算两个触摸点的中点坐标                    calMidPoint(midPointF, event);                    //两点是旋转或者缩放模式                    mode = MODE_ZOOM;                }                preEventCoor = new float[4];                preEventCoor[0] = event.getX(0);                preEventCoor[1] = event.getX(1);                preEventCoor[2] = event.getY(0);                preEventCoor[3] = event.getY(1);                saveRotate = calRotation(event);                break;            case MotionEvent.ACTION_UP:// 单点离开屏幕时            case MotionEvent.ACTION_POINTER_UP:// 第二个点离开屏幕时                mode = MODE_NONE;                preEventCoor = null;                break;            case MotionEvent.ACTION_MOVE:// 触摸点移动时            /*             * 单点触控拖拽平移             */                if (mode == MODE_DRAG) {                    currentMatrix.set(savedMatrix);                    float dx = event.getX() - startPointF.x;                    float dy = event.getY() - startPointF.y;                    currentMatrix.postTranslate(dx, dy);                }            /*             * 两点触控拖放旋转             */                else if (mode == MODE_ZOOM && event.getPointerCount() == 2) {                    float currentMove = calSpacing(event);                    currentMatrix.set(savedMatrix);                /*                 * 指尖移动距离大于10F缩放                 */                    if (currentMove > 10F) {                        float scale = currentMove / preMove;                        currentMatrix.postScale(scale, scale, midPointF.x, midPointF.y);                    }                /*                 * 保持两点时旋转                 */                    if (preEventCoor != null) {                        rotate = calRotation(event);                        r = rotate - saveRotate;                        currentMatrix.postRotate(r, getMeasuredWidth() / 2, getMeasuredHeight() / 2);                    }                }                break;        }        setImageMatrix(currentMatrix);        return true;    }    float r;    /**     * 计算两个触摸点间的距离     */    private float calSpacing(MotionEvent event) {        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 calMidPoint(PointF point, MotionEvent event) {        float x = event.getX(0) + event.getX(1);        float y = event.getY(0) + event.getY(1);        point.set(x / 2, y / 2);    }    /**     * 计算旋转角度     *     * @param     * @return 角度值     */    private float calRotation(MotionEvent event) {        double deltaX = (event.getX(0) - event.getX(1));        double deltaY = (event.getY(0) - event.getY(1));        double radius = Math.atan2(deltaY, deltaX);        return (float) Math.toDegrees(radius);    }    /**     * 设置原始图片     *     * @param mSrc     */    public void setmSrc(Bitmap mSrc) {        this.mSrc = mSrc;        currentMatrix = new Matrix();        requestLayout();        postInvalidate();    }    public void setFileURL(String fileURL) {        this.fileURL = fileURL;    }    public void setFontCode(String fontCode) {        this.fontCode = fontCode;    }    public void setCutRect(Rect cutRect) {        this.cutRect = cutRect;    }    /**     * 更新当前Matrix     */    public void setCurrentMatrix() {        this.currentMatrix = new Matrix();    }} 

2,控件的正常使用,需要设置原始图片的默认值

        if (mSrc == null) {            //如果为空,赋默认值            mSrc = BitmapFactory.decodeResource(getResources(), R.mipmap.desc6, null);        }    /**     * 设置原始图片     *     * @param mSrc     */    public void setmSrc(Bitmap mSrc) {        this.mSrc = mSrc;        currentMatrix = new Matrix();        requestLayout();        postInvalidate();    }

在切换到显示图片时,对控件设置原始图片,并刷新显示,绘制。


3,在Ondraw()中绘制裁剪区域
      /**         * 测试切图区域和显示区域一致         */        if (cutRect != null) {            paint.setColor(Color.RED);            canvas.drawRect(cutRect, paint);        }

在Activity中获取裁剪区域大小:
    @Override    public void onWindowFocusChanged(boolean hasFocus) {        super.onWindowFocusChanged(hasFocus);        cutRectIV.post(new Runnable() {            @Override            public void run() {                Rect viewRect = new Rect();                int[] location = new int[2];                int[] location1 = new int[2];//                cutRectIV.getLocalVisibleRect(viewRect);                cutRectIV.getLocationOnScreen(location);                contentTakePicRL.getLocationOnScreen(location1);                /**                 * getLeft() /getTop() /getBottom() /getRight()                 * 都是相对父控件的位置                 */                //设置截图区域                viewRect.left = cutRectIV.getLeft();                viewRect.top = contentTakePicRL.getTop();                viewRect.bottom = cutRectIV.getBottom() + contentTakePicRL.getTop();                viewRect.right = cutRectIV.getRight();                rotateMIV.setCutRect(viewRect);            }        });    }

特别注意:gettop(),getLeft()都是相对父布局,在计算时,父布局与实际所在的位置一定要清晰。



三、相册导入

1,相册导入一般使用系统相册导入
注意点:
(1)自己构建相册需要管理相册内存;
(2)实际倒入效果并不会有很好的提升。
导入时注意对图片进行压缩:

 @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        //获取图片路径        if (requestCode == IMAGE && resultCode == Activity.RESULT_OK && data != null) {            Uri selectedImage = data.getData();            String[] filePathColumns = {MediaStore.Images.Media.DATA};            Cursor c = getContentResolver().query(selectedImage, filePathColumns, null, null, null);            c.moveToFirst();            int columnIndex = c.getColumnIndex(filePathColumns[0]);            String imagePath = c.getString(columnIndex);            int[] size = getImageWidthHeight(imagePath);            int width = screenWidth;            //导入进来,让宽适配屏幕宽度            int height = (int) (((float) (screenWidth * size[1])) / ((float) size[0]));            Bitmap bitmap = BitmapUtil.getBitmapBySize(imagePath, width, height);            rotateMIV.setmSrc(bitmap);            changeToShowPic();            c.close();        }    }

现获取图片大小,再依据最后显示大小,压缩图片后再显示。

    public static Bitmap getBitmapBySize(String path, int width, int height) {        BitmapFactory.Options option = new BitmapFactory.Options();        option.inJustDecodeBounds = true;        BitmapFactory.decodeFile(path, option);        option.inSampleSize = computeSampleSize(option, -1, width * height);        option.inJustDecodeBounds = false;        Bitmap bitmap = null;        try {            bitmap = BitmapFactory.decodeFile(path, option);            if (bitmap.getWidth() != width || bitmap.getHeight() != height) {                Matrix mx = new Matrix();                mx.setScale((float) width / (float) bitmap.getWidth(), (float) height / (float) bitmap.getHeight());                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mx, true);            }        } catch (Exception e) {            e.printStackTrace();        }        return bitmap;    }

2,保存图片

在图片保存中,存在已经设置权限了,还是创建目录不成功的情形,需要动态设置权限。
onCreate()中添加权限动态访问:

    ActivityCompat.requestPermissions(MainActivity.this, new String[]{android                .Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);


在权限获取结果中处理文件夹的创建管理:

    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,                                           @NonNull int[] grantResults) {        switch (requestCode) {            case 1:                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {                    //创建文件夹                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {                        File file = new File(Environment.getExternalStorageDirectory().toString() + "/camera/demo/");                        if (!file.exists()) {                            Log.d("fileCreate", "file create:" + file.mkdirs());                        }                    }                    break;                }        }    }


3,有些手机有虚拟导航,为保持页面的一致,需要管控页面。

    @SuppressLint("NewApi")    private void hideVirtualGuideBar() {        //隐藏不显示        Window window = getWindow();        WindowManager.LayoutParams params = window.getAttributes();        params.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE;        window.setAttributes(params);    }


因为需求的关系,获取到的图片,还需要做二值化。
且听下回分解。



这里是简化版本呐

传送门 -- 源码哦



偶尔会依旧很迷茫,偶尔会找不到自身的定位。总是在跌跌撞撞中找寻。。。
































 
原创粉丝点击