Flutter Hybrid Composition实现自定义相机预览原生插件

目录

前言

在做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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容