目錄
前言
在做Flutter開(kāi)發(fā)的時(shí)候,有時(shí)候Flutter有些功能實(shí)現(xiàn)不了,因此需要自定義原生插件來(lái)解決,下面就是通過(guò)Hybrid Composition實(shí)現(xiàn)自定義相機(jī)預(yù)覽原生控件
實(shí)現(xiàn)方法
1.創(chuàng)建Flutter插件工程
這里要選擇Plugin
2.創(chuàng)建Widget
這里要記住viewType的值('hybrid-view-camera')待會(huì)寫Android原生代碼的時(shí)候要對(duì)應(yīng)起來(lái)
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.編輯原生代碼
鼠標(biāo)右鍵點(diǎn)擊插件項(xiàng)目苞尝,選擇Flutter->Open Android module in Android Studio
新增NativeView用于實(shí)現(xiàn)相機(jī)預(yù)覽
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()
}
/**
* 初始化相機(jī)
*/
private fun initCamera() {
try {
mCamera!!.setPreviewCallback(this)
val parameters = mCamera!!.parameters
parameters.previewFormat = ImageFormat.NV21
//根據(jù)設(shè)置的寬高 和手機(jī)支持的分辨率對(duì)比計(jì)算出合適的寬高算法
val optionSize = CameraUtils.findBestPreviewResolution(mCamera)
// parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//部分手機(jī)無(wú)效
parameters.setPreviewSize(optionSize.width, optionSize.height)
//設(shè)置照片尺寸
parameters.setPictureSize(optionSize.width, optionSize.height)
mCamera!!.parameters = parameters
mCamera!!.setDisplayOrientation(90)
//開(kāi)啟預(yù)覽
mCamera!!.startPreview()
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
/**
* 釋放相機(jī)
*/
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相機(jī)操作類代碼如下
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;
}
}
/**
* 保證預(yù)覽方向正確
*
* @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;
}
/**
* 把相機(jī)拍照返回照片轉(zhuǎn)正
*
* @param angle 旋轉(zhuǎn)角度
* @return bitmap 圖片
*/
public Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
//旋轉(zhuǎn)圖片 動(dòng)作
Matrix matrix = new Matrix();
matrix.postRotate(angle);
//加入翻轉(zhuǎn) 把相機(jī)拍照返回照片轉(zhuǎn)正
if (id == 1) {
matrix.postScale(-1, 1);
}
// 創(chuàng)建新的圖片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
/**
* 獲取所有支持的預(yù)覽尺寸
*/
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;//如果沒(méi)找到,就選最小的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;//如果沒(méi)找到佑惠,就選最小的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) {
}
}
/**
* 打開(kāi)閃關(guān)燈
*/
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 {
}
}
}
/**
* 自動(dòng)模式閃光燈
*/
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;
}
/**
* 最小預(yù)覽界面的分辨率
*/
private static final int MIN_PREVIEW_PIXELS = 480 * 320;
/**
* 最大寬高比差
*/
private static final double MAX_ASPECT_DISTORTION = 0.5;
/**
* 關(guān)閉閃光燈
*
* @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膜楷,我們?cè)O(shè)置的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);
// 找到與屏幕分辨率完全匹配的預(yù)覽界面分辨率直接返回
if (maybeFlippedWidth == screenWidth
&& maybeFlippedHeight == screenHeight) {
return supportedPreviewResolution;
}
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
}
// 如果沒(méi)有找到合適的贞奋,并且還有候選的像素赌厅,則設(shè)置其中最大比例的,對(duì)于配置比較低的機(jī)器不太合適
if (!supportedPreviewResolutions.isEmpty()) {
Size largestPreview = supportedPreviewResolutions.get(0);
return largestPreview;
}
// 沒(méi)有找到合適的轿塔,就返回默認(rèn)的
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;
}
}
/**
* 保證預(yù)覽方向正確
*
* @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;
}
/**
* 把相機(jī)拍照返回照片轉(zhuǎn)正
*
* @param angle 旋轉(zhuǎn)角度
* @return bitmap 圖片
*/
public Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
//旋轉(zhuǎn)圖片 動(dòng)作
Matrix matrix = new Matrix();
matrix.postRotate(angle);
//加入翻轉(zhuǎn) 把相機(jī)拍照返回照片轉(zhuǎn)正
if (id == 1) {
matrix.postScale(-1, 1);
}
// 創(chuàng)建新的圖片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
/**
* 獲取所有支持的預(yù)覽尺寸
*/
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;//如果沒(méi)找到特愿,就選最小的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;//如果沒(méi)找到,就選最小的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) {
}
}
/**
* 打開(kāi)閃關(guān)燈
*/
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 {
}
}
}
/**
* 自動(dòng)模式閃光燈
*/
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;
}
/**
* 最小預(yù)覽界面的分辨率
*/
private static final int MIN_PREVIEW_PIXELS = 480 * 320;
/**
* 最大寬高比差
*/
private static final double MAX_ASPECT_DISTORTION = 0.5;
/**
* 關(guān)閉閃光燈
*
* @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,我們?cè)O(shè)置的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);
// 找到與屏幕分辨率完全匹配的預(yù)覽界面分辨率直接返回
if (maybeFlippedWidth == screenWidth
&& maybeFlippedHeight == screenHeight) {
return supportedPreviewResolution;
}
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
}
// 如果沒(méi)有找到合適的毒嫡,并且還有候選的像素,則設(shè)置其中最大比例的幻梯,對(duì)于配置比較低的機(jī)器不太合適
if (!supportedPreviewResolutions.isEmpty()) {
Size largestPreview = supportedPreviewResolutions.get(0);
return largestPreview;
}
// 沒(méi)有找到合適的审胚,就返回默認(rèn)的
return defaultPreviewResolution;
}
然后創(chuàng)建NativeViewFactory用于創(chuàng)建NativeView
internal class NativeViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, id: Int, args: Any?): PlatformView {
return NativeView(context, id, args)
}
}
打開(kāi)MainActivity,進(jìn)行注冊(cè)礼旅,注意這里的"hybrid-view-camera"要與之前提到的Flutter中的viewType對(duì)應(yīng)起來(lái)
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine
.platformViewsController
.registry
.registerViewFactory("hybrid-view-camera", NativeViewFactory())
}
}
增加回調(diào)
有時(shí)候我們會(huì)根據(jù)相機(jī)的預(yù)覽數(shù)據(jù)進(jìn)行一些處理,然后我們需要把處理的結(jié)果發(fā)給Flutter洽洁,所以這里我們需要使用EventChannel進(jìn)行通信痘系。
修改_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中增加一個(gè)接收的回調(diào)
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();
}
然后在使用的時(shí)候傳入回調(diào)函數(shù)
Positioned(
child: SizedBox(
width: double.maxFinite,
height: double.maxFinite,
child: NativeAndroidView((event) {
setState(() {
_timeString = event;
});
}),
)
),
然后我們?cè)谠a的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用于發(fā)送數(shù)據(jù)
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()}")
}
}
最終效果如下(注意這里我們運(yùn)行的是Flutter項(xiàng)目不是Android插件項(xiàng)目)饿自,左上角的時(shí)間戳是Android原生傳過(guò)來(lái)的