安卓自定义水印相机

水印相机需求:可以选择地点,时间要实时显示,有公司logo等,支持拍照完成支持预览,可以保存到本地功能。

  • 附上Demo下载链接:https://download.csdn.net/download/qiushuiduren/90037747
    主要功能如下:
    * 自定义相机,支持水印功能:可直接将layout中的view转化为 bitmap添加到图片上
    * 支持前后摄像头切换,自动对焦
    * 地址可点击进行跳转选择(可按自己需求更改实现)
    * 时间为当前时间,动态显示
    * 解决相机预览与保存图片位置不一致的问题
    * 拍照完成进行图片预览,支持图片放大预览
    * 6.0 以上动态申请运行权限
    * 支持 ViewBinding绑定控件
    

先上图片,看看效果图,第三张是保存的图片

网上找了很多例子,没有特别符合的,于是重新完善写了一下

下面直接上代码

布局文件如下,包含水印相机和水印的 view

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F5F5F5"
    android:paddingTop="0dp">

    <View
        android:id="@+id/view_top"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@color/black"
        app:layout_constraintBottom_toTopOf="@+id/sfv_camera"
        app:layout_constraintTop_toTopOf="parent" />

    <!--    设置预览窗口的宽高比为 3:4-->
    <com.fix.watercamera.view.ResizeAbleSurfaceView
        android:id="@+id/sfv_camera"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/view_bottom_navigation"
        app:layout_constraintDimensionRatio="3:4"
        app:layout_constraintTop_toBottomOf="@+id/view_top" />

    <com.fix.watercamera.view.ZoomImageView
        android:id="@+id/iv_picture_preview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@color/black"
        android:scaleType="centerInside"
        app:layout_constraintDimensionRatio="3:4"
        android:visibility="invisible"
        app:layout_constraintTop_toBottomOf="@+id/view_top" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/csl_water_info_container"
        android:layout_width="160dp"
        android:layout_height="128dp"
        android:layout_marginStart="26dp"
        android:layout_marginBottom="26dp"
        android:alpha="0.9"
        android:background="@drawable/shape_background_grey_8"
        app:layout_constraintBottom_toBottomOf="@id/sfv_camera"
        app:layout_constraintStart_toStartOf="parent">

        <View
            android:id="@+id/view_water_top_logo"
            android:layout_width="0dp"
            android:layout_height="34dp"
            android:background="@mipmap/icon_water_camera_logo"
            app:layout_constraintEnd_toEndOf="@+id/csl_water_info_container"
            app:layout_constraintStart_toStartOf="@+id/csl_water_info_container"
            app:layout_constraintTop_toTopOf="@+id/csl_water_info_container" />

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:drawablePadding="4dp"
            android:gravity="center_vertical"
            android:paddingStart="8dp"
            android:paddingEnd="8dp"
            android:text="张三丰(5455454)"
            android:textColor="@color/white"
            android:textSize="12sp"
            app:drawableStartCompat="@mipmap/icon_water_person"
            app:layout_constraintBottom_toTopOf="@+id/tv_address"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/view_water_top_logo" />

        <TextView
            android:id="@+id/tv_address"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:drawablePadding="4dp"
            android:ellipsize="end"
            android:gravity="center_vertical"
            android:maxLines="2"
            android:paddingStart="8dp"
            android:paddingEnd="8dp"
            android:text="水印地址信息"
            android:textColor="@color/white"
            android:textSize="12sp"
            app:drawableStartCompat="@mipmap/icon_water_address"
            app:layout_constraintBottom_toTopOf="@+id/tv_time"
            app:layout_constraintStart_toStartOf="@id/tv_name"
            app:layout_constraintTop_toBottomOf="@id/tv_name" />

        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:drawablePadding="4dp"
            android:gravity="center_vertical"
            android:paddingStart="8dp"
            android:paddingEnd="8dp"
            android:text="2024-10-11 10:56"
            android:textColor="@color/white"
            android:textSize="12sp"
            app:drawableStartCompat="@mipmap/icon_water_time"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@id/tv_name"
            app:layout_constraintTop_toBottomOf="@id/tv_address" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <View
        android:id="@+id/view_bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="170dp"
        android:background="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/sfv_camera" />

    <ImageView
        android:id="@+id/iv_take_pictures"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_marginTop="30dp"
        android:src="@mipmap/icon_take_picture"
        app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
        app:layout_constraintStart_toStartOf="@id/view_bottom_navigation"
        app:layout_constraintTop_toTopOf="@id/view_bottom_navigation" />

    <ImageView
        android:id="@+id/iv_take_pictures_finish"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_marginTop="30dp"
        android:src="@mipmap/icon_take_picture_finish"
        android:visibility="invisible"
        app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
        app:layout_constraintStart_toStartOf="@id/view_bottom_navigation"
        app:layout_constraintTop_toTopOf="@id/view_bottom_navigation" />

    <TextView
        android:id="@+id/tv_camera_switch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawablePadding="4dp"
        android:gravity="center_horizontal"
        android:text="翻转"
        android:textColor="@color/white"
        android:textSize="10dp"
        app:drawableTopCompat="@mipmap/icon_camera_switch"
        app:layout_constraintBottom_toBottomOf="@+id/iv_take_pictures"
        app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
        app:layout_constraintStart_toEndOf="@+id/iv_take_pictures"
        app:layout_constraintTop_toTopOf="@id/iv_take_pictures" />


    <TextView
        android:id="@+id/tv_select_address"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawablePadding="4dp"
        android:gravity="center_horizontal"
        android:text="地点"
        android:textColor="@color/white"
        android:textSize="10dp"
        app:drawableTopCompat="@mipmap/icon_camera_address"
        app:layout_constraintBottom_toBottomOf="@+id/iv_take_pictures"
        app:layout_constraintEnd_toStartOf="@+id/iv_take_pictures"
        app:layout_constraintStart_toStartOf="@id/view_bottom_navigation"
        app:layout_constraintTop_toTopOf="@id/iv_take_pictures" />


    <TextView
        android:id="@+id/tv_take_picture_again"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawablePadding="4dp"
        android:gravity="center_horizontal"
        android:text="重拍"
        android:textColor="@color/white"
        android:textSize="10dp"
        android:visibility="invisible"
        app:drawableTopCompat="@mipmap/icon_take_picture_again"
        app:layout_constraintBottom_toBottomOf="@+id/iv_take_pictures"
        app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
        app:layout_constraintStart_toEndOf="@+id/iv_take_pictures"
        app:layout_constraintTop_toTopOf="@id/iv_take_pictures" />

</androidx.constraintlayout.widget.ConstraintLayout>

下面是水印相机的主代码,代码有点长,注释写的很详细,感兴趣的可以研究下,如有问题还望各位大佬指正

public class WaterCameraActivity extends BaseActivity<ActivityWaterCameraBinding> implements SurfaceHolder.Callback {
    private Handler handler;
    private Runnable updateTimeTask;
    private Camera mCamera;
    private int mCameraId;
    private Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
    private SurfaceHolder mSurfaceHolder;
    private Bitmap tempBitmap;

    @Override
    public void init() {
        overridePendingTransition(R.anim.activtiy_alpha_in, R.anim.activtiy_alpha_out);

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        binding.sfvCamera.setFocusable(true);
        mSurfaceHolder = binding.sfvCamera.getHolder();
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mSurfaceHolder.setKeepScreenOn(true);
        mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
        mSurfaceHolder.addCallback(this);

        mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;

        // 初始化日期及时间
        handler = new Handler();
        updateTimeTask = new Runnable() {
            @Override
            public void run() {
                // 获取当前时间并格式化为字符串
                String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
                // 更新TextView上的时间
                binding.tvTime.setText(currentTime);

                // 安排这个任务再次在1000毫秒(1秒)后执行
                handler.postDelayed(this, 1000);
            }
        };
        handler.post(updateTimeTask);

        // 点击设置对焦
//        binding.sfvCamera.setOnTouchListener((v, event) -> {
//            mCamera.autoFocus(autoFocusCallback); //设置相机为自动对焦模式,就不用认为去点击了
//            return false;
//        });

        binding.tvCameraSwitch.setOnClickListener(v -> {
            switchCamera();
        });

        // 跳转到选择地址页面
        binding.tvAddress.setOnClickListener(v -> {
            Toast.makeText(context, "去选择地址", Toast.LENGTH_SHORT).show();
        });
        binding.tvSelectAddress.setOnClickListener(v -> {
            Toast.makeText(context, "去选择地址", Toast.LENGTH_SHORT).show();
        });

        // 拍照
        binding.ivTakePictures.setOnClickListener(v -> {
            takePictures();
        });

        // 删除并重新拍照
        binding.tvTakePictureAgain.setOnClickListener(v -> {
            tempBitmap = null;
            setViewVisibilityOrNot(true);
        });

        // 保存拍照
        binding.ivTakePicturesFinish.setOnClickListener(v -> {
            saveBitmapToAlbum(tempBitmap);
            // 保存完照片关闭相机
            finish();
        });
    }


    /**
     * 这是点击surfaceview聚焦所调用的方法
     */
    private Camera.AutoFocusCallback autoFocusCallback = (success, camera) -> {
        //success = true,聚焦成功,否则聚焦失败
        //在这里我们可以在点击相机后是否聚焦成功,然后做我们的一些操作
    };

    /**
     * 检查设备是否有摄像头
     *
     * @param context context
     * @return boolean
     */
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
            return true;
        }
        return false;
    }


    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        startCamera(mCameraId);
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {


    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
        releaseCamera();
    }

    /**
     * 拍照时设置相机控件的显示隐藏
     */
    private void setViewVisibilityOrNot(boolean isTakingPicture) {
        if (isTakingPicture) {
            binding.sfvCamera.setVisibility(View.VISIBLE);
            binding.cslWaterInfoContainer.setVisibility(View.VISIBLE);
            binding.ivPicturePreview.setVisibility(View.INVISIBLE);
            binding.tvTakePictureAgain.setVisibility(View.INVISIBLE);
            binding.ivTakePicturesFinish.setVisibility(View.INVISIBLE);
            binding.ivTakePictures.setVisibility(View.VISIBLE);
            binding.tvCameraSwitch.setVisibility(View.VISIBLE);
        } else {
            binding.ivPicturePreview.setVisibility(View.VISIBLE);
            binding.sfvCamera.setVisibility(View.INVISIBLE);
            binding.cslWaterInfoContainer.setVisibility(View.INVISIBLE);
            binding.tvTakePictureAgain.setVisibility(View.VISIBLE);
            binding.ivTakePicturesFinish.setVisibility(View.VISIBLE);
            binding.ivTakePictures.setVisibility(View.INVISIBLE);
            binding.tvCameraSwitch.setVisibility(View.INVISIBLE);
        }
    }

    /**
     * 打开相机
     */
    private void startCamera(int mCameraId) {
        try {
            mCamera = Camera.open(mCameraId);
            Camera.getCameraInfo(0, cameraInfo);
            Camera.Parameters parameters = mCamera.getParameters();
            // 设置图片格式
            parameters.setPictureFormat(ImageFormat.JPEG);
            // 设置照片质量
            parameters.setJpegQuality(100);
            // 首先获取系统设备支持的所有颜色特效,如果设备不支持颜色特性将返回一个null, 如果有符合我们的则设置
            List<String> colorEffects = parameters.getSupportedColorEffects();
            for (String currColor : colorEffects) {
                if (currColor.equals(Camera.Parameters.EFFECT_SOLARIZE)) {
                    parameters.setColorEffect(Camera.Parameters.EFFECT_AQUA);
                    break;
                }
            }

            // 获取对焦模式
            List<String> focusModes = parameters.getSupportedFocusModes();
            if (focusModes != null && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                // 设置自动对焦
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            }

            // 设置闪光灯自动开启
            List<String> flashModes = parameters.getSupportedFlashModes();
            if (flashModes != null && flashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {
                // 自动闪光
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
            }
            mCamera.setDisplayOrientation(setCameraDisplayOrientation());
            // 设置显示
            mCamera.setPreviewDisplay(mSurfaceHolder);

            // 看需求是否需要指定拍照图片的大小(下面代码为高品质图片)
//            List<Camera.Size> photoSizes = parameters.getSupportedPictureSizes();//获取系统可支持的图片尺寸
//            int width = 0, height = 0;
//            for (Camera.Size size : photoSizes) {
//                if (size.width > width) width = size.width;
//                if (size.height > height) height = size.height;
//            }
//            parameters.setPictureSize(width, height);

            // 获取相机的支持的预览尺寸,找出相近的,解决 SurfaceView预览和保存的图片大小不一致的问题
            Camera.Size closelyPreSize = getCloselyPreSize(binding.sfvCamera.getWidth(), binding.sfvCamera.getHeight(), mCamera.getParameters().getSupportedPreviewSizes());
            parameters.setPreviewSize(closelyPreSize.width,closelyPreSize.height);
            // 重新设置 SurfaceView的宽高(注:相机的宽高比和layout的是反着的)  match_parent=-1
            binding.sfvCamera.resize(-1, binding.sfvCamera.getWidth() * closelyPreSize.width /closelyPreSize.height);

            // 设置预览图片的尺寸大小
            ViewGroup.LayoutParams layoutParams = binding.ivPicturePreview.getLayoutParams();
            layoutParams.height =binding.ivPicturePreview.getWidth() * closelyPreSize.width /closelyPreSize.height;
            binding.ivPicturePreview.setLayoutParams(layoutParams);

            // 设置完成需要再次调用setParameter方法才能生效
            mCamera.setParameters(parameters);
            // 开始预览
            mCamera.startPreview();
        } catch (Exception e) {
            e.printStackTrace();
            releaseCamera();
        }
    }

    /**
     * 取接近比例的方法
     */
    protected Camera.Size getCloselyPreSize(int surfaceWidth, int surfaceHeight,
                                            List<Camera.Size> preSizeList) {
        int ReqTmpWidth;
        int ReqTmpHeight;
        // 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高
        if (true) {
            ReqTmpWidth = surfaceHeight;
            ReqTmpHeight = surfaceWidth;
        } else {
            ReqTmpWidth = surfaceWidth;
            ReqTmpHeight = surfaceHeight;
        }
        //先查找preview中是否存在与surfaceview相同宽高的尺寸
        for (Camera.Size size : preSizeList) {
            if ((size.width == ReqTmpWidth) && (size.height == ReqTmpHeight)) {
                return size;
            }
        }

        // 得到与传入的宽高比最接近的size
        float reqRatio = ((float) ReqTmpWidth) / ReqTmpHeight;
        float curRatio, deltaRatio;
        float deltaRatioMin = Float.MAX_VALUE;
        Camera.Size retSize = null;
        for (Camera.Size size : preSizeList) {
            curRatio = ((float) size.width) / size.height;
            deltaRatio = Math.abs(reqRatio - curRatio);
            if (deltaRatio < deltaRatioMin) {
                deltaRatioMin = deltaRatio;
                retSize = size;
            }
        }
        return retSize;
    }


    /**
     * 摄像头转换
     */
    private void switchCamera() {
        // 切换前后摄像头
        if (Camera.getNumberOfCameras() > 1) {
            mCameraId = (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) ?
                    Camera.CameraInfo.CAMERA_FACING_FRONT :
                    Camera.CameraInfo.CAMERA_FACING_BACK;
            if (mCamera != null) {
                this.surfaceDestroyed(mSurfaceHolder);
                this.surfaceCreated(mSurfaceHolder);
            }
        }
    }

    /**
     * 拍照逻辑
     */
    private void takePictures() {
        mCamera.takePicture(null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                if (tempBitmap != null) {
                    Matrix m = new Matrix();
                    // 分别设置前后摄像头保存图片的旋转角度
                    if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                        // 后置摄像头
                        m.postRotate(90);
                    } else if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                        // 前置摄像头
                        m.postRotate(270);
                        // 解决镜像问题
                        m.postScale(-1, 1);
                    }

                    tempBitmap = Bitmap.createBitmap(tempBitmap, 0, 0, tempBitmap.getWidth(), tempBitmap.getHeight(), m, true);

                    // 获取水印bitmap view
                    Bitmap waterInfoBitmap = createBitmapByView(binding.cslWaterInfoContainer);
                    // 给照片添加水印
                    tempBitmap = addWaterInfo(tempBitmap, waterInfoBitmap);

                    ImageLoadUtil.loadBitmapImage(context, binding.ivPicturePreview, tempBitmap);

                    setViewVisibilityOrNot(false);
                } else {
                    releaseCamera();
                }
            }
        });
    }

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

    @Override
    protected void onDestroy() {
        tempBitmap = null;
        // 取消任务以防止内存泄漏
        handler.removeCallbacks(updateTimeTask);
        super.onDestroy();
    }

    /**
     * 添加水印
     */
    public Bitmap addWaterInfo(Bitmap srcBitmap, Bitmap waterBitmap) {
        // 获取原始图片与水印图片的宽与高
        int srcBitmapWidth = srcBitmap.getWidth();
        int srcBitmapHeight = srcBitmap.getHeight();
        Bitmap newBitmap = Bitmap.createBitmap(srcBitmapWidth, srcBitmapHeight, Bitmap.Config.ARGB_8888);
        Canvas mCanvas = new Canvas(newBitmap);
        // 往位图中开始画入src原始图片
        mCanvas.drawBitmap(srcBitmap, 0, 0, null);

        // 获取surface view 和图片view的换算比例
        double ratio = 1.0 * srcBitmapWidth / binding.sfvCamera.getWidth();
        // 获取水印图片的位置及大小
        int left = (int) (binding.cslWaterInfoContainer.getLeft() * ratio);
        int right = (int) (binding.cslWaterInfoContainer.getRight() * ratio);
        int height = (int) (binding.cslWaterInfoContainer.getHeight() * ratio);
        int top = srcBitmapHeight - height - left; // 减去left是为了保证图片的左边距和下边距相同
        int bottom = srcBitmapHeight - left;

        Paint paint = new Paint();
        paint.setAlpha(230); // 设置透明度 0 ~ 255
        mCanvas.drawBitmap(waterBitmap, null, new Rect(left, top, right, bottom), paint);

        mCanvas.save();
        mCanvas.restore();
        return newBitmap;
    }

    /**
     * view转bitmap
     *
     * @param view view
     * @return Bitmap
     */
    private Bitmap createBitmapByView(View view) {
        final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        return bitmap;
    }

    /**
     * 保存bitmap到相册
     */
    private void saveBitmapToAlbum(Bitmap bitmap) {
        final File appDir = new File(context.getExternalCacheDir(), "image");
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        final String fileName = System.currentTimeMillis() + ".jpg";
        final File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            try {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
                fos.flush();
            } finally {
                fos.close();
            }
            //把文件插入到系统相册
            String tempPath = MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null);
            String realPath = getRealPathFromURI(Uri.parse(tempPath));

            //通知图库更新
//            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(file.getPath()))));
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse(tempPath)));

            // Toast 保存路径
            Toast.makeText(context, realPath, Toast.LENGTH_SHORT).show();


        } catch (Exception e) {
            e.printStackTrace();
        }
        releaseCamera();
    }

    public String getRealPathFromURI(Uri contentUri) {//通过本地路经 content://得到URI路径
        Cursor cursor = null;
        String locationPath = null;
        try {
            String[] proj = {MediaStore.Images.Media.DATA};
            cursor = getContentResolver().query(contentUri, proj, null, null, null);
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            locationPath = cursor.getString(column_index);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return locationPath;
    }


    public int setCameraDisplayOrientation() {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(0, info);
        int rotation = this.getWindowManager().getDefaultDisplay().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;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;   // compensate the mirror
        } else {
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }
}

加载图片的方法用的是 Glide,很简单,加载一下图片就可以了

ResizeAbleSurfaceView 是自定义可以改变宽高的 SurfaceView,方便动态的改变大小,达到预览效果和图片效果水印位置一致的目的,下面是代码
public class ResizeAbleSurfaceView extends SurfaceView {

    private int mWidth = -1;
    private int mHeight = -1;

    public ResizeAbleSurfaceView(Context context) {
        super(context);
    }

    public ResizeAbleSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ResizeAbleSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (-1 == mWidth || -1 == mHeight) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
        else {
            setMeasuredDimension(mWidth, mHeight);
        }
    }

    public void resize(int width, int height) {
        mWidth = width;
        mHeight = height;
        getHolder().setFixedSize(width, height);
        requestLayout();
        invalidate();
    }
}

基本就这些,希望能帮到你,感兴趣的同学可以研究下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值