美文网首页
Flutter Hybrid Composition实现自定义相

Flutter Hybrid Composition实现自定义相

作者: itfitness | 来源:发表于2023-05-19 16:36 被阅读0次

    目录

    前言

    在做Flutter开发的时候,有时候Flutter有些功能实现不了,因此需要自定义原生插件来解决,下面就是通过Hybrid Composition实现自定义相机预览原生控件

    实现方法

    1.创建Flutter插件工程

    这里要选择Plugin


    2.创建Widget

    这里要记住viewType的值('hybrid-view-camera')待会写Android原生代码的时候要对应起来

    class NativeAndroidView extends StatefulWidget {
      const NativeAndroidView({Key? key}) : super(key: key);
    
      @override
      State<NativeAndroidView> createState() => _NativeAndroidViewState();
    }
    
    class _NativeAndroidViewState extends State<NativeAndroidView> {
      @override
      Widget build(BuildContext context) {
        final String viewType = 'hybrid-view-camera';
        final Map<String, dynamic> creationParams = <String, dynamic>{};
    
        return PlatformViewLink(
          viewType: viewType,
          surfaceFactory:
              (BuildContext context, PlatformViewController controller) {
            return AndroidViewSurface(
              controller: controller as AndroidViewController,
              gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
              hitTestBehavior: PlatformViewHitTestBehavior.opaque,
            );
          },
          onCreatePlatformView: (PlatformViewCreationParams params) {
            return PlatformViewsService.initSurfaceAndroidView(
              id: params.id,
              viewType: viewType,
              layoutDirection: TextDirection.ltr,
              creationParams: creationParams,
              creationParamsCodec: StandardMessageCodec(),
            )
              ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
              ..create();
          },
        );
      }
    }
    
    3.编辑原生代码

    鼠标右键点击插件项目,选择Flutter->Open Android module in Android Studio

    新增NativeView用于实现相机预览

    internal class NativeView(context: Context, id: Int, creationParams: Any?) :
        PlatformView ,Camera.PreviewCallback, SurfaceHolder.Callback {
        private var mSurfaceView: SurfaceView? = null
        private var surfaceHolder: SurfaceHolder? = null
        private var mCamera: Camera? = null
        var autoFocusTimer: Timer? = null
        init {
            mSurfaceView = SurfaceView(context)
            surfaceHolder = mSurfaceView!!.holder
            surfaceHolder!!.addCallback(this)
            CameraUtils.init(context)
        }
    
        override fun getView(): View {
            return mSurfaceView!!
        }
    
        override fun dispose() {
            closeCamera()
        }
    
        /**
         * 初始化相机
         */
        private fun initCamera() {
            try {
                mCamera!!.setPreviewCallback(this)
                val parameters = mCamera!!.parameters
                parameters.previewFormat = ImageFormat.NV21
                //根据设置的宽高 和手机支持的分辨率对比计算出合适的宽高算法
                val optionSize = CameraUtils.findBestPreviewResolution(mCamera)
                // parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//部分手机无效
                parameters.setPreviewSize(optionSize.width, optionSize.height)
                //设置照片尺寸
                parameters.setPictureSize(optionSize.width, optionSize.height)
                mCamera!!.parameters = parameters
                mCamera!!.setDisplayOrientation(90)
                //开启预览
                mCamera!!.startPreview()
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }
    
        /**
         * 释放相机
         */
        private fun closeCamera() {
            try {
                if (autoFocusTimer != null) {
                    autoFocusTimer!!.cancel()
                }
                if (mCamera != null) {
                    mCamera!!.stopPreview()
    //                mCamera.release();//加上要挂啊
                    mCamera = null
                }
            } catch (e: java.lang.Exception) {
            }
        }
    
        override fun onPreviewFrame(p0: ByteArray?, p1: Camera?) {
            
        }
    
        override fun surfaceCreated(p0: SurfaceHolder) {
            try {
                mCamera = Camera.open(0) //0:后置 1:前置
                initCamera()
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }
    
        override fun surfaceChanged(holder: SurfaceHolder, p1: Int, p2: Int, p3: Int) {
            try {
                mCamera!!.setPreviewDisplay(holder)
                initAutoFocusTimer()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    
        override fun surfaceDestroyed(p0: SurfaceHolder) {
            closeCamera()
        }
    
        private fun initAutoFocusTimer() {
            if (autoFocusTimer == null) {
                autoFocusTimer = Timer()
                autoFocusTimer!!.schedule(object : TimerTask() {
                    override fun run() {
                        if (mCamera != null) {
                            mCamera!!.autoFocus { success, camera ->
    
                            }
                        }
                    }
                }, 0, 600)
            }
        }
    }
    

    这里的CameraUtils相机操作类代码如下

    public class CameraUtils {
        public static float scale;
    
        public static int densityDpi;
    
        public static float fontScale;
    
        public static int screenWidth;
        public static int screenHeight;
    
        public static void init(Context context) {
            DisplayMetrics dm = context.getResources().getDisplayMetrics();
            scale = dm.density;
            densityDpi = dm.densityDpi;
            fontScale = dm.scaledDensity;
            if (dm.widthPixels < dm.heightPixels) {
                screenWidth = dm.widthPixels;
                screenHeight = dm.heightPixels;
            } else {
                screenWidth = dm.heightPixels;
                screenHeight = dm.widthPixels;
            }
            Log.e("screen", "屏幕宽度是:" + screenWidth + " 高度是:" + screenHeight + " dp:" + scale + " fontScale:" + fontScale);
        }
    
        //降序
        private CameraDropSizeComparator dropSizeComparator = new CameraDropSizeComparator();
        //升序
        private CameraAscendSizeComparator ascendSizeComparator = new CameraAscendSizeComparator();
        private static CameraUtils myCamPara = null;
    
        private CameraUtils() {
    
        }
    
        public static CameraUtils getInstance() {
            if (myCamPara == null) {
                myCamPara = new CameraUtils();
                return myCamPara;
            } else {
                return myCamPara;
            }
        }
    
        /**
         * 保证预览方向正确
         *
         * @param activity
         * @param cameraId
         * @param camera
         */
        public void setCameraDisplayOrientation(Activity activity,
                                                int cameraId, Camera camera) {
            Camera.CameraInfo info =
                    new Camera.CameraInfo();
            Camera.getCameraInfo(cameraId, info);
            int rotation = activity.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 {  // back-facing
                result = (info.orientation - degrees + 360) % 360;
            }
            camera.setDisplayOrientation(result);
        }
    
    
        public Bitmap setTakePicktrueOrientation(int id, Bitmap bitmap) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(id, info);
            bitmap = rotaingImageView(id, info.orientation, bitmap);
            return bitmap;
        }
    
        /**
         * 把相机拍照返回照片转正
         *
         * @param angle 旋转角度
         * @return bitmap 图片
         */
        public Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
            //旋转图片 动作
            Matrix matrix = new Matrix();
            matrix.postRotate(angle);
            //加入翻转 把相机拍照返回照片转正
            if (id == 1) {
                matrix.postScale(-1, 1);
            }
            // 创建新的图片
            Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
                    bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            return resizedBitmap;
        }
    
        /**
         * 获取所有支持的预览尺寸
         */
        public Size getPropPreviewSize(List<Size> list, int minWidth) {
            Collections.sort(list, ascendSizeComparator);
    
            int i = 0;
            for (Size s : list) {
                if ((s.width >= minWidth)) {
                    break;
                }
                i++;
            }
            if (i == list.size()) {
                i = 0;//如果没找到,就选最小的size
            }
            return list.get(i);
        }
    
        /**
         * 获取所有支持的返回图片尺寸
         */
        public Size getPropPictureSize(List<Size> list, int minWidth) {
            Collections.sort(list, ascendSizeComparator);
    
            int i = 0;
            for (Size s : list) {
                if ((s.width >= minWidth)) {
                    break;
                }
                i++;
            }
            if (i == list.size()) {
                i = 0;//如果没找到,就选最小的size
            }
            return list.get(i);
        }
    
        public boolean equalRate(Size s, float rate) {
            float r = (float) (s.width) / (float) (s.height);
            return Math.abs(r - rate) <= 0.03;
        }
    
        //降序
        public class CameraDropSizeComparator implements Comparator<Size> {
            public int compare(Size lhs, Size rhs) {
                if (lhs.width == rhs.width) {
                    return 0;
                } else if (lhs.width < rhs.width) {
                    return 1;
                } else {
                    return -1;
                }
            }
    
        }
    
        //升序
        public class CameraAscendSizeComparator implements Comparator<Size> {
            public int compare(Size lhs, Size rhs) {
                if (lhs.width == rhs.width) {
                    return 0;
                } else if (lhs.width > rhs.width) {
                    return 1;
                } else {
                    return -1;
                }
            }
    
        }
    
        /**
         * 打印支持的previewSizes
         */
        public void printSupportPreviewSize(Camera.Parameters params) {
            List<Size> previewSizes = params.getSupportedPreviewSizes();
            for (int i = 0; i < previewSizes.size(); i++) {
                Size size = previewSizes.get(i);
            }
    
        }
    
        /**
         * 打印支持的pictureSizes
         */
        public void printSupportPictureSize(Camera.Parameters params) {
            List<Size> pictureSizes = params.getSupportedPictureSizes();
            for (int i = 0; i < pictureSizes.size(); i++) {
                Size size = pictureSizes.get(i);
            }
        }
    
        /**
         * 打印支持的聚焦模式
         */
        public void printSupportFocusMode(Camera.Parameters params) {
            List<String> focusModes = params.getSupportedFocusModes();
            for (String mode : focusModes) {
            }
        }
    
        /**
         * 打开闪关灯
         */
        public void turnLightOn(Camera mCamera) {
            if (mCamera == null) {
                return;
            }
            Camera.Parameters parameters = mCamera.getParameters();
            if (parameters == null) {
                return;
            }
            List<String> flashModes = parameters.getSupportedFlashModes();
            // Check if camera flash exists
            if (flashModes == null) {
                // Use the screen as a flashlight (next best thing)
                return;
            }
            String flashMode = parameters.getFlashMode();
            if (!Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {
                // Turn on the flash
                if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                    parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                    mCamera.setParameters(parameters);
                } else {
                }
            }
        }
    
    
        /**
         * 自动模式闪光灯
         */
        public void turnLightAuto(Camera mCamera) {
            if (mCamera == null) {
                return;
            }
            Camera.Parameters parameters = mCamera.getParameters();
            if (parameters == null) {
                return;
            }
            List<String> flashModes = parameters.getSupportedFlashModes();
            // Check if camera flash exists
            if (flashModes == null) {
                // Use the screen as a flashlight (next best thing)
                return;
            }
            String flashMode = parameters.getFlashMode();
            if (!Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)) {
                // Turn on the flash
                if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                    parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                    mCamera.setParameters(parameters);
                } else {
                }
            }
        }
    
    
        public static Camera.CameraInfo getCameraInfo(int facing) {
            int numberOfCameras = Camera.getNumberOfCameras();
            Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
            for (int i = 0; i < numberOfCameras; i++) {
                Camera.getCameraInfo(i, cameraInfo);
                if (cameraInfo.facing == facing) {
                    return cameraInfo;
                }
            }
            return null;
        }
    
    
        /**
         * 最小预览界面的分辨率
         */
        private static final int MIN_PREVIEW_PIXELS = 480 * 320;
        /**
         * 最大宽高比差
         */
        private static final double MAX_ASPECT_DISTORTION = 0.5;
    
    
        /**
         * 关闭闪光灯
         *
         * @param mCamera
         */
        public void turnLightOff(Camera mCamera) {
            if (mCamera == null) {
                return;
            }
            Camera.Parameters parameters = mCamera.getParameters();
            if (parameters == null) {
                return;
            }
            List<String> flashModes = parameters.getSupportedFlashModes();
            String flashMode = parameters.getFlashMode();
            // Check if camera flash exists
            if (flashModes == null) {
                return;
            }
            if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
                // Turn off the flash
                if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                    parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                    mCamera.setParameters(parameters);
                } else {
                }
            }
        }
    
        public static Size findBestPreviewResolution(Camera mCamera) {
            Camera.Parameters cameraParameters = mCamera.getParameters();
            Size defaultPreviewResolution = cameraParameters.getPreviewSize();
    
            List<Size> rawSupportedSizes = cameraParameters.getSupportedPreviewSizes();
            if (rawSupportedSizes == null) {
                return defaultPreviewResolution;
            }
    
            // 按照分辨率从大到小排序
            List<Size> supportedPreviewResolutions = new ArrayList<Size>(rawSupportedSizes);
            Collections.sort(supportedPreviewResolutions, new Comparator<Size>() {
                @Override
                public int compare(Size a, Size b) {
                    int aPixels = a.height * a.width;
                    int bPixels = b.height * b.width;
                    if (bPixels < aPixels) {
                        return -1;
                    }
                    if (bPixels > aPixels) {
                        return 1;
                    }
                    return 0;
                }
            });
    
            StringBuilder previewResolutionSb = new StringBuilder();
            for (Size supportedPreviewResolution : supportedPreviewResolutions) {
                previewResolutionSb.append(supportedPreviewResolution.width).append('x').append(supportedPreviewResolution.height)
                        .append(' ');
            }
    
            // 移除不符合条件的分辨率
            double screenAspectRatio = (double) (screenWidth / screenHeight);
            Iterator<Size> it = supportedPreviewResolutions.iterator();
            while (it.hasNext()) {
                Size supportedPreviewResolution = it.next();
                int width = supportedPreviewResolution.width;
                int height = supportedPreviewResolution.height;
    
                // 移除低于下限的分辨率,尽可能取高分辨率
                if (width * height < MIN_PREVIEW_PIXELS) {
                    it.remove();
                    continue;
                }
    
                // 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率
                // 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height
                // 因此这里要先交换然preview宽高比后在比较
                boolean isCandidatePortrait = width > height;
                int maybeFlippedWidth = isCandidatePortrait ? height : width;
                int maybeFlippedHeight = isCandidatePortrait ? width : height;
                double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
                double distortion = Math.abs(aspectRatio - screenAspectRatio);
    
                // 找到与屏幕分辨率完全匹配的预览界面分辨率直接返回
                if (maybeFlippedWidth == screenWidth
                        && maybeFlippedHeight == screenHeight) {
                    return supportedPreviewResolution;
                }
    
                if (distortion > MAX_ASPECT_DISTORTION) {
                    it.remove();
                    continue;
                }
    
            }
    
            // 如果没有找到合适的,并且还有候选的像素,则设置其中最大比例的,对于配置比较低的机器不太合适
            if (!supportedPreviewResolutions.isEmpty()) {
                Size largestPreview = supportedPreviewResolutions.get(0);
                return largestPreview;
            }
    
            // 没有找到合适的,就返回默认的
    
            return defaultPreviewResolution;
        }
    }
    
    public static int densityDpi;
    
    public static float fontScale;
    
    public static int screenWidth;
    public static int screenHeight;
    
    public static void init(Context context) {
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        scale = dm.density;
        densityDpi = dm.densityDpi;
        fontScale = dm.scaledDensity;
        if (dm.widthPixels < dm.heightPixels) {
            screenWidth = dm.widthPixels;
            screenHeight = dm.heightPixels;
        } else {
            screenWidth = dm.heightPixels;
            screenHeight = dm.widthPixels;
        }
        Log.e("screen", "屏幕宽度是:" + screenWidth + " 高度是:" + screenHeight + " dp:" + scale + " fontScale:" + fontScale);
    }
    
    //降序
    private CameraDropSizeComparator dropSizeComparator = new CameraDropSizeComparator();
    //升序
    private CameraAscendSizeComparator ascendSizeComparator = new CameraAscendSizeComparator();
    private static CameraUtils myCamPara = null;
    
    private CameraUtils() {
    
    }
    
    public static CameraUtils getInstance() {
        if (myCamPara == null) {
            myCamPara = new CameraUtils();
            return myCamPara;
        } else {
            return myCamPara;
        }
    }
    
    /**
     * 保证预览方向正确
     *
     * @param activity
     * @param cameraId
     * @param camera
     */
    public void setCameraDisplayOrientation(Activity activity,
                                            int cameraId, Camera camera) {
        Camera.CameraInfo info =
                new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotation = activity.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 {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        camera.setDisplayOrientation(result);
    }
    
    
    public Bitmap setTakePicktrueOrientation(int id, Bitmap bitmap) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(id, info);
        bitmap = rotaingImageView(id, info.orientation, bitmap);
        return bitmap;
    }
    
    /**
     * 把相机拍照返回照片转正
     *
     * @param angle 旋转角度
     * @return bitmap 图片
     */
    public Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
        //旋转图片 动作
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        //加入翻转 把相机拍照返回照片转正
        if (id == 1) {
            matrix.postScale(-1, 1);
        }
        // 创建新的图片
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
                bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizedBitmap;
    }
    
    /**
     * 获取所有支持的预览尺寸
     */
    public Size getPropPreviewSize(List<Size> list, int minWidth) {
        Collections.sort(list, ascendSizeComparator);
    
        int i = 0;
        for (Size s : list) {
            if ((s.width >= minWidth)) {
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = 0;//如果没找到,就选最小的size
        }
        return list.get(i);
    }
    
    /**
     * 获取所有支持的返回图片尺寸
     */
    public Size getPropPictureSize(List<Size> list, int minWidth) {
        Collections.sort(list, ascendSizeComparator);
    
        int i = 0;
        for (Size s : list) {
            if ((s.width >= minWidth)) {
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = 0;//如果没找到,就选最小的size
        }
        return list.get(i);
    }
    
    public boolean equalRate(Size s, float rate) {
        float r = (float) (s.width) / (float) (s.height);
        return Math.abs(r - rate) <= 0.03;
    }
    
    //降序
    public class CameraDropSizeComparator implements Comparator<Size> {
        public int compare(Size lhs, Size rhs) {
            if (lhs.width == rhs.width) {
                return 0;
            } else if (lhs.width < rhs.width) {
                return 1;
            } else {
                return -1;
            }
        }
    
    }
    
    //升序
    public class CameraAscendSizeComparator implements Comparator<Size> {
        public int compare(Size lhs, Size rhs) {
            if (lhs.width == rhs.width) {
                return 0;
            } else if (lhs.width > rhs.width) {
                return 1;
            } else {
                return -1;
            }
        }
    
    }
    
    /**
     * 打印支持的previewSizes
     */
    public void printSupportPreviewSize(Camera.Parameters params) {
        List<Size> previewSizes = params.getSupportedPreviewSizes();
        for (int i = 0; i < previewSizes.size(); i++) {
            Size size = previewSizes.get(i);
        }
    
    }
    
    /**
     * 打印支持的pictureSizes
     */
    public void printSupportPictureSize(Camera.Parameters params) {
        List<Size> pictureSizes = params.getSupportedPictureSizes();
        for (int i = 0; i < pictureSizes.size(); i++) {
            Size size = pictureSizes.get(i);
        }
    }
    
    /**
     * 打印支持的聚焦模式
     */
    public void printSupportFocusMode(Camera.Parameters params) {
        List<String> focusModes = params.getSupportedFocusModes();
        for (String mode : focusModes) {
        }
    }
    
    /**
     * 打开闪关灯
     */
    public void turnLightOn(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        // Check if camera flash exists
        if (flashModes == null) {
            // Use the screen as a flashlight (next best thing)
            return;
        }
        String flashMode = parameters.getFlashMode();
        if (!Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }
    
    
    /**
     * 自动模式闪光灯
     */
    public void turnLightAuto(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        // Check if camera flash exists
        if (flashModes == null) {
            // Use the screen as a flashlight (next best thing)
            return;
        }
        String flashMode = parameters.getFlashMode();
        if (!Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }
    
    
    public static Camera.CameraInfo getCameraInfo(int facing) {
        int numberOfCameras = Camera.getNumberOfCameras();
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == facing) {
                return cameraInfo;
            }
        }
        return null;
    }
    
    
    /**
     * 最小预览界面的分辨率
     */
    private static final int MIN_PREVIEW_PIXELS = 480 * 320;
    /**
     * 最大宽高比差
     */
    private static final double MAX_ASPECT_DISTORTION = 0.5;
    
    
    /**
     * 关闭闪光灯
     *
     * @param mCamera
     */
    public void turnLightOff(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        String flashMode = parameters.getFlashMode();
        // Check if camera flash exists
        if (flashModes == null) {
            return;
        }
        if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
            // Turn off the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }
    
    public static Size findBestPreviewResolution(Camera mCamera) {
        Camera.Parameters cameraParameters = mCamera.getParameters();
        Size defaultPreviewResolution = cameraParameters.getPreviewSize();
    
        List<Size> rawSupportedSizes = cameraParameters.getSupportedPreviewSizes();
        if (rawSupportedSizes == null) {
            return defaultPreviewResolution;
        }
    
        // 按照分辨率从大到小排序
        List<Size> supportedPreviewResolutions = new ArrayList<Size>(rawSupportedSizes);
        Collections.sort(supportedPreviewResolutions, new Comparator<Size>() {
            @Override
            public int compare(Size a, Size b) {
                int aPixels = a.height * a.width;
                int bPixels = b.height * b.width;
                if (bPixels < aPixels) {
                    return -1;
                }
                if (bPixels > aPixels) {
                    return 1;
                }
                return 0;
            }
        });
    
        StringBuilder previewResolutionSb = new StringBuilder();
        for (Size supportedPreviewResolution : supportedPreviewResolutions) {
            previewResolutionSb.append(supportedPreviewResolution.width).append('x').append(supportedPreviewResolution.height)
                    .append(' ');
        }
    
        // 移除不符合条件的分辨率
        double screenAspectRatio = (double) (screenWidth / screenHeight);
        Iterator<Size> it = supportedPreviewResolutions.iterator();
        while (it.hasNext()) {
            Size supportedPreviewResolution = it.next();
            int width = supportedPreviewResolution.width;
            int height = supportedPreviewResolution.height;
    
            // 移除低于下限的分辨率,尽可能取高分辨率
            if (width * height < MIN_PREVIEW_PIXELS) {
                it.remove();
                continue;
            }
    
            // 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率
            // 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height
            // 因此这里要先交换然preview宽高比后在比较
            boolean isCandidatePortrait = width > height;
            int maybeFlippedWidth = isCandidatePortrait ? height : width;
            int maybeFlippedHeight = isCandidatePortrait ? width : height;
            double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
            double distortion = Math.abs(aspectRatio - screenAspectRatio);
    
            // 找到与屏幕分辨率完全匹配的预览界面分辨率直接返回
            if (maybeFlippedWidth == screenWidth
                    && maybeFlippedHeight == screenHeight) {
                return supportedPreviewResolution;
            }
    
            if (distortion > MAX_ASPECT_DISTORTION) {
                it.remove();
                continue;
            }
    
        }
    
        // 如果没有找到合适的,并且还有候选的像素,则设置其中最大比例的,对于配置比较低的机器不太合适
        if (!supportedPreviewResolutions.isEmpty()) {
            Size largestPreview = supportedPreviewResolutions.get(0);
            return largestPreview;
        }
    
        // 没有找到合适的,就返回默认的
    
        return defaultPreviewResolution;
    }
    

    然后创建NativeViewFactory用于创建NativeView

    internal class NativeViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
        override fun create(context: Context, id: Int, args: Any?): PlatformView {
            return NativeView(context, id, args)
        }
    }
    

    打开MainActivity,进行注册,注意这里的"hybrid-view-camera"要与之前提到的Flutter中的viewType对应起来

    class MainActivity: FlutterActivity() {
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
            super.configureFlutterEngine(flutterEngine)
            flutterEngine
                .platformViewsController
                .registry
                .registerViewFactory("hybrid-view-camera",  NativeViewFactory())
        }
    }
    

    增加回调

    有时候我们会根据相机的预览数据进行一些处理,然后我们需要把处理的结果发给Flutter,所以这里我们需要使用EventChannel进行通信。
    修改_NativeAndroidViewState如下,增加EventChannel

    class _NativeAndroidViewState extends State<NativeAndroidView> {
      final EventChannel? _eventChannel = EventChannel("flutter_camera/event");
      StreamSubscription<dynamic>? _streamSubscription;
      @override
      void initState() {
        super.initState();
        _streamSubscription =  _eventChannel?.receiveBroadcastStream().listen((event) {
          print(event);
          widget._dataCallBack(event);
        }) as StreamSubscription;
      }
      @override
      Widget build(BuildContext context) {
        final String viewType = 'hybrid-view-camera';
        final Map<String, dynamic> creationParams = <String, dynamic>{};
    
        return PlatformViewLink(
          viewType: viewType,
          surfaceFactory:
              (BuildContext context, PlatformViewController controller) {
            return AndroidViewSurface(
              controller: controller as AndroidViewController,
              gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
              hitTestBehavior: PlatformViewHitTestBehavior.opaque,
            );
          },
          onCreatePlatformView: (PlatformViewCreationParams params) {
            return PlatformViewsService.initSurfaceAndroidView(
              id: params.id,
              viewType: viewType,
              layoutDirection: TextDirection.ltr,
              creationParams: creationParams,
              creationParamsCodec: StandardMessageCodec(),
            )
              ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
              ..create();
          },
        );
      }
    }
    

    另外我们还要NativeAndroidView中增加一个接收的回调

    typedef CameraDataCallBack = void Function(String);
    class NativeAndroidView extends StatefulWidget {
      final CameraDataCallBack _dataCallBack;
      const NativeAndroidView( this._dataCallBack,{Key? key}) : super(key: key);
    
      @override
      State<NativeAndroidView> createState() => _NativeAndroidViewState();
    }
    

    然后在使用的时候传入回调函数

    Positioned(
                      child: SizedBox(
                        width: double.maxFinite,
                        height: double.maxFinite,
                        child: NativeAndroidView((event) {
                          setState(() {
                            _timeString = event;
                          });
                        }),
                      )
                  ),
    

    然后我们在原生代码的MainActivity中加入EventChannel用于与Flutter通信

    class MainActivity: FlutterActivity() {
        private lateinit var channel: EventChannel
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
            super.configureFlutterEngine(flutterEngine)
            channel = EventChannel(flutterEngine.dartExecutor.binaryMessenger, "flutter_camera/event")
            channel.setStreamHandler(
                object : EventChannel.StreamHandler {
                    override fun onListen(arguments: Any?, events: EventSink) {
                        NativeView.eventSink = events
                    }
                    override fun onCancel(arguments: Any?) {
                        Log.w("Android", "EventChannel onCancel called")
                    }
                })
            flutterEngine
                .platformViewsController
                .registry
                .registerViewFactory("hybrid-view-camera",  NativeViewFactory())
        }
    }
    

    NativeView中增加EventChannel.EventSink用于发送数据

    internal class NativeView(context: Context, id: Int, creationParams: Any?) :
        PlatformView ,Camera.PreviewCallback, SurfaceHolder.Callback {
        companion object {
            var eventSink: EventChannel.EventSink? = null
        }
        ...省略部分代码
        override fun onPreviewFrame(p0: ByteArray?, p1: Camera?) {
            eventSink?.success("${System.currentTimeMillis()}")
        }
    
    }
    

    最终效果如下(注意这里我们运行的是Flutter项目不是Android插件项目),左上角的时间戳是Android原生传过来的


    案例源码

    https://gitee.com/itfitness/flutter-camera-plugin

    相关文章

      网友评论

          本文标题:Flutter Hybrid Composition实现自定义相

          本文链接:https://www.haomeiwen.com/subject/weknsdtx.html