Flutter Hybrid Composition实现自定义相
2023-05-19 本文已影响0人
itfitness
目录
前言
在做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原生传过来的