基于SurfaceView封装相机,并添加自定义水印效果

封德华
2023-12-01

基于SurfaceView封装相机,并添加自定义水印效果

根据项目需要,android自带的相机不能满足自己需求,需要在拍完相机后能处理照片添加水印,并且照相机界面能自己定义风格,在网上找了现有代码自己加以重构(PS:可能封装的不够好),基本满足相机功能和业务逻辑分离,到达代码复用效果。

重构代码遇到3个问题这里和大家分享下:

1.自定义相机设置尺寸不对导致SurfaceView显示画面变形
2.手动点击对焦
3.水印添加美观满足用户自定义

问题一:

针对这个问题也在网上找了资料,基本解释是 由于预览图像大小跟SurfaceView 大小不一致引起

于是找了可行代码块

代码块

代码块语法遵循标准markdown代码,例如:

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        int rotation = getDisplayOrientation();
        mCamera.setDisplayOrientation(rotation);
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setRotation(rotation);
        mCamera.setParameters(parameters);
        adjustDisplayRatio(rotation);
    }
    public int getDisplayOrientation() {

        android.hardware.Camera.CameraInfo camInfo = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(
                Camera.CameraInfo.CAMERA_FACING_BACK, camInfo);

        Display display = ((WindowManager) getContext().getSystemService(
                Context.WINDOW_SERVICE)).getDefaultDisplay();
        int rotation = display.getRotation();
        int degrees = 0;
        switch (rotation) {
        case Surface.ROTATION_0:
            degrees = 0;
            break;
        case Surface.ROTATION_90:
            degrees = 90;
            break;
        case Surface.ROTATION_180:
            degrees = 180;
            break;
        case Surface.ROTATION_270:
            degrees = 270;
            break;
        }

        int result = (camInfo.orientation - degrees + 360) % 360;
        return result;
    }
    private void adjustDisplayRatio(int rotation) {
        ViewGroup parent = ((ViewGroup) getParent());
        Rect rect = new Rect();
        parent.getLocalVisibleRect(rect);
        int width = rect.width();
        int height = rect.height();
        Parameters mParameters = mCamera.getParameters();
        Camera.Size previewSize = mParameters.getPreviewSize();
        int previewWidth = previewSize.width;
        int previewHeight = previewSize.height;
        if (rotation == 90 || rotation == 270) {
            previewWidth = previewSize.height;
            previewHeight = previewSize.width;
        } else {
            previewWidth = previewSize.width;
            previewHeight = previewSize.height;
        }
        if (width * previewHeight > height * previewWidth) {
            final int scaledChildWidth = previewWidth * height / previewHeight;

            layout((width - scaledChildWidth) / 2, 0,
                    (width + scaledChildWidth) / 2, height);
        } else {
            final int scaledChildHeight = previewHeight * width / previewWidth;
            layout(0, (height - scaledChildHeight) / 2, width,
                    (height + scaledChildHeight) / 2);
        }
    }

在surfaceChanged方法里面设置相机的显示,里面最重要的方法是adjustDisplayRatio,这个方法是适配预览图像大小跟SurfaceView 大小,目前魅族手机显示是正常。

问题二:手动点击对焦

手动点击对焦需要捕获onTouchEvent方法的event.getPointerCount(),若event.getPointerCount() == 1则表示SurfaceView 点击回调然后调camera.autoFocus完成手动对焦

    public boolean onTouchEvent(MotionEvent event) {
        if (event.getPointerCount() == 1) {
            handleFocusMetering(event, mCamera);
        }}
    private void handleFocusMetering(MotionEvent event, Camera camera) {
        camera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                Camera.Parameters params = camera.getParameters();
                params.setFocusMode(params.getFocusMode());
                camera.setParameters(params);
            }
        });

    }

问题三:优化水印显示
刚拿到代码时候发现,该功能和业务绑定很严重,其他项目拿到这份代码需要改动很大,达不到复用的效果。重构是时候主要的思想是返回可用的水印照片就行,至于什么业务逻辑生成照片代码里面不用关心。
将PhotoGraphyView自定义一个公共view,在xml直接引用,获得对象后然后设置照片顶部和底部水印
代码如下

        List<TextView> listTextView_up = new ArrayList<TextView>();
        TextView tv1 = new TextView(this);
        tv1.setTextColor(Color.GREEN);
        tv1.setText("报案号:XXXXXXXXXXXXXXXX");
        tv1.setTextSize(18);
        TextView tv2 = new TextView(this);
        tv2.setText("姓名:张三");
        tv2.setTextSize(18);
        tv2.setTextColor(Color.GREEN);
        listTextView_up.add(tv1);
        listTextView_up.add(tv2);
        photoGraphyView.setImageUpView(listTextView_up);//设置头部水印


        List<TextView> listTextView_down = new ArrayList<TextView>();
        TextView tv3 = new TextView(this);
        tv3.setTextColor(Color.RED);
        tv3.setText("经度:138.298374");
        tv3.setTextSize(18);
        TextView tv4 = new TextView(this);
        tv4.setTextColor(Color.RED);
        tv4.setText("纬度:64.293772");
        tv4.setTextSize(18);
        listTextView_down.add(tv3);
        listTextView_down.add(tv4);

        TextView tv5 = new TextView(this);
        tv5.setTextColor(Color.RED);
        tv5.setText("时间:2016-08-15 15:45:47");
        tv5.setTextSize(18);
        listTextView_down.add(tv5);
        photoGraphyView.setImageDownView(listTextView_down);//设置底部水印

设置水印的代码主要是运用Canvas对象

    private Paint drawUpText(Canvas cv) {
        Paint paint = new Paint();
        StringBuffer sb = new StringBuffer();
        if (listTextView_top.size() > 0) {
            cv.setDrawFilter(new PaintFlagsDrawFilter(0, 3));
            for (int i = 0; i < listTextView_top.size(); i++) {
                paint.setColor(listTextView_top.get(i).getTextColors() == null ? Color.BLACK
                        : listTextView_top.get(i).getTextColors()
                                .getDefaultColor());
                paint.setTextSize((float) (listTextView_top.get(i)
                        .getTextSize() + 4));
                paint.setTypeface(fontFace);
                paint.setTextAlign(Paint.Align.CENTER);
                paint.setAntiAlias(true);
                paint.setDither(true);
                sb.append(listTextView_top.get(i).getText().toString());
                if (i > 0) {
                    cv.drawText(listTextView_top.get(i).getText().toString(),
                            200f + getTextWidth(paint, sb.toString()), 100f,
                            paint);
                } else {
                    cv.drawText(listTextView_top.get(i).getText().toString(),
                            500f, 100f, paint);
                }
            }
        }
        return paint;
    }

Canvas 生成如下

        Bitmap newb = null;
        newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas cv = new Canvas(newb);
 类似资料: