精华内容
参与话题
问答
  • 使用自定义Camera实现简单拍照功能

    千次阅读 2017-03-14 12:50:52
    闲来无事,就把之前用自定义Camera实现的简单拍照功能记录一下。 Camera类在5.0以后不推荐使用了,取而代之的是android.hardware.camera2包下的类,本文使用Camera。 我们首先自定义一个View去继承SurfaceView:...

    闲来无事,就把之前用自定义Camera实现的简单拍照功能记录一下。
    Camera类在5.0以后不推荐使用了,取而代之的是android.hardware.camera2包下的类,本文使用Camera。
    我们首先自定义一个View去继承SurfaceView:

    public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {
        private SurfaceHolder mHolder;
        private Camera mCamera;
        private static final int ORIENTATION = 90;
        private int mScreenWidth;
        private int mScreenHeight;
        private boolean isOpen;
    
        public CameraSurfaceView(Context context, AttributeSet attrs) {
            super(context, attrs);
            getScreenMatrix(context);
            mHolder = getHolder();
            mHolder.addCallback(this);
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }
    
        private void getScreenMatrix(Context context) {
            WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            DisplayMetrics outMetrics = new DisplayMetrics();
            WM.getDefaultDisplay().getMetrics(outMetrics);
            mScreenWidth = outMetrics.widthPixels;
            mScreenHeight = outMetrics.heightPixels;
        }
    
        public void takePicture(Camera.ShutterCallback mShutterCallback, Camera.PictureCallback rawPictureCallback, Camera.PictureCallback jpegPictureCallback) {
            if (mCamera != null)
                mCamera.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
        }
    
        public void startPreview() {
            mCamera.startPreview();
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            if (!checkCameraHardware(getContext()))
                return;
            if (mCamera == null) {
                isOpen = safeCameraOpen(Camera.CameraInfo.CAMERA_FACING_BACK);
            }
            if (!isOpen) {
                return;
            }
            mCamera.setDisplayOrientation(ORIENTATION);
            try {
                mCamera.setPreviewDisplay(holder);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            if (mCamera != null) {
                setCameraParams(mScreenWidth, mScreenHeight);
                mCamera.startPreview();
            }
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            releaseCameraAndPreview();
        }
    
        private boolean safeCameraOpen(int id) {
            boolean qOpened = false;
            try {
                releaseCameraAndPreview();
                mCamera = Camera.open(id);
                qOpened = (mCamera != null);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return qOpened;
        }
    
        private void releaseCameraAndPreview() {
            if (mCamera != null) {
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
            }
        }
    
        private boolean checkCameraHardware(Context context) {
            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
                return true;
            } else {
                return false;
            }
        }
    
    
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
    
        }
    
        private void setCameraParams(int width, int height) {
            Camera.Parameters parameters = mCamera.getParameters();
            // 获取摄像头支持的PictureSize列表
            List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
            /**从列表中选取合适的分辨率*/
            Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
            if (null == picSize) {
                picSize = parameters.getPictureSize();
            }
            // 根据选出的PictureSize重新设置SurfaceView大小
            float w = picSize.width;
            float h = picSize.height;
            parameters.setPictureSize(picSize.width, picSize.height);
            this.setLayoutParams(new RelativeLayout.LayoutParams((int) (height * (h / w)), height));
            // 获取摄像头支持的PreviewSize列表
            List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
            Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
            if (null != preSize) {
                parameters.setPreviewSize(preSize.width, preSize.height);
            }
    
            parameters.setJpegQuality(100); // 设置照片质量
            if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式
            }
    
            mCamera.setDisplayOrientation(90);// 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示
            mCamera.setParameters(parameters);
    
        }
    
        /**
         * 选取合适的分辨率
         */
        private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) {
            Camera.Size result = null;
            for (Camera.Size size : pictureSizeList) {
                float currentRatio = ((float) size.width) / size.height;
                if (currentRatio - screenRatio == 0) {
                    result = size;
                    break;
                }
            }
    
            if (null == result) {
                for (Camera.Size size : pictureSizeList) {
                    float curRatio = ((float) size.width) / size.height;
                    if (curRatio == 4f / 3) {// 默认w:h = 4:3
                        result = size;
                        break;
                    }
                }
            }
    
            return result;
        }
    }

    代码没什么难度,在View创建的时候完成Camera的初始化,然后对Camera进行参数的设置(图片尺寸,质量之类的),最后别忘了在View销毁的时候对资源进行释放。

    控件定义完了之后我们就要去使用它,在布局文件中添加就OK:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    
        <com.padoon.cameratest.CameraSurfaceView
            android:id="@+id/sv_camera"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="4"/>
    
        <ImageView
            android:id="@+id/img_take_photo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_gravity="bottom"
            android:layout_marginBottom="10dp"
            android:layout_marginRight="10dp"
            android:src="@mipmap/icon_camera"/>
    </RelativeLayout>

    然后在Activity中去完成拍照功能:

    public class CameraActivity extends AppCompatActivity {
        private boolean isClick = true;
        private static final String PATH_IMAGES = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "easy_check";
        private CameraSurfaceView mCameraSurfaceView;
        //拍照快门的回调
        private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
            @Override
            public void onShutter() {
    
            }
        };
        //拍照完成之后返回原始数据的回调
        private Camera.PictureCallback rawPictureCallback = new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
    
            }
        };
        //拍照完成之后返回压缩数据的回调
        private Camera.PictureCallback jpegPictureCallback = new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                mCameraSurfaceView.startPreview();
                saveFile(data);
                Toast.makeText(CameraActivity.this, "拍照成功", Toast.LENGTH_SHORT).show();
                isClick = true;
    
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ImageView img_take_photo = (ImageView) findViewById(R.id.img_take_photo);
            mCameraSurfaceView = (CameraSurfaceView) findViewById(R.id.sv_camera);
            img_take_photo.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    takePhoto();
                }
            });
        }
    
        public void takePhoto() {
            if (isClick) {
                isClick = false;
                mCameraSurfaceView.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
            }
        }
        //保存图片到硬盘
        public void saveFile(byte[] data) {
            String fileName = UUID.randomUUID().toString() + ".jpg";
            FileOutputStream outputStream = null;
            try {
                File file = new File(PATH_IMAGES);
                if (!file.exists()) {
                    file.mkdirs();
                }
                outputStream = new FileOutputStream(PATH_IMAGES + File.separator + fileName);
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
                bufferedOutputStream.write(data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    最后记得添加拍照跟磁盘操作权限:

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    到这一个非常简单的拍照Demo就完成了,只能当做Demo使用,离开发正式使用还有一段的距离,再次特地记录一下。

    源码

    展开全文
  • Flutter自定义相机、相册选择照片

    题记
    —— 执剑天涯,从你的点滴积累开始,所及之处,必精益求精。

    github? 测试源码在这里 百度同步
    CSDN 网易云课堂教程 掘金
    知乎 Flutter系列文章 头条同步

    在这里插入图片描述

    实际项目开发中,谷歌官方推荐的几个相机插件总是满足不了需要,更令人不舒服的是在华为某些高版本系列的手机中使用系统相机拍照或者是选择照片会让应用闪退。

    小编也分析了原因,在这些手机拍照的照片过大,在手机相机拍完的那一瞬间,手机系统还没有处理完照片,然后应用就去读取这个照片,导致系统异常崩溃。

    至本插件编写的 0.0.1 版本时,各相机应用插件都未修复其兼容性,以小编的性格,那就是再造个轮子,于是乎 本插件就诞生了。

    0.0.1 版本 只支持 Android (已发布 2020-09-09)
    0.0.3 版本 同时支持 iOS (已发布 2020-09-12)
    0.0.5 版本 自定义相机 同时支持 iOS (已发布 2020-09-15)

    0.0.5 版本的功能修复
    iOS平台的图片资源策略修改


    本插件实现的最终目标​:

    • 1、调用原生默认的自定义相机

    • 2、调用原生的系统相机、相册选择图片

    • 3、拍照或者选择照片后 调用 系统裁剪(自定义)功能

    • 4、在 Flutter Widget 中嵌入 自定义相机,允许使用 Widget 开发的页面布局来操作相机


    如下图是默认情况下自定义相机的页面效果:

    在这里插入图片描述


    1 添加依赖

    小编以将这个自定义相机封装成一个插件组件,直接使用flutter_custom_camera_pugin插件就可使用,一般小编的作风就是提供两种集成方式如下:

    通过pub仓库添加依赖,代码如下:最新版本查看这里

      dependencies:
    	 flutter_custom_camera_pugin: ^0.0.1
    

    或者是通过 github 点击查看github方式添加依赖,代码如下:

    dependencies:
    	shake_animation_widget:
    	      git:
    	        url: https://github.com/zhaolongs/FlutterCustomCameraPugin.git
    	        ref: master
    

    然后加载依赖,代码如下:

    flutter pub get
    

    然后在使用的地方导包,代码如下:

    import 'package:flutter_custom_camera_pugin/flutter_custom_camera_pugin.dart';
    

    2 打开自定义相机 拍照

    你可以使用 FlutterCustomCameraPugin 的 openCamera 方法来打开自定义相机拍照页面,其中 可选参数 cameraConfigOptions 用来配置自定义相机页面的的按钮是否显示,如下代码清单 2-1:

      ///代码清单 2-1 
      ///打开相机
      void openCamera() async {
        CameraConfigOptions options = new CameraConfigOptions();
    
        ///默认自定义相册是否显示 相册切换
        options.isShowSelectCamera = true;
    
        ///默认自定义相册是否显示 前后镜头切换
        options.isShowPhotoAlbum = true;
    
        ///默认自定义相册是否显示 闪光灯开关按钮
        options.isShowFlashButtonCamera = true;
    
        ///调起自定义相机
        ///拍照的返回结果
        CameraResultInfo resultInfo =
            await FlutterCustomCameraPugin.openCamera(cameraConfigOptions: options);
    
        if (resultInfo.code == 200) {
          imageFile = new File(resultInfo.data["lImageUrl"]);
        }else if (resultInfo.code == 201) {
         ///201 是拍照取消 如点击了关闭按钮 
         ///或者是 Android 手机的后退按钮
        }
        setState(() {});
      }
    
    

    CameraConfigOptions 用来配置相机参数,如下所示:

    /// 相册配置使用参数
    class CameraConfigOptions {
    
      ///0.0.1 版本 
      ///默认自定义相册是否显示 相册切换
      bool isShowPhotoAlbum = true;
    
      ///默认自定义相册是否显示 前后镜头切换
      bool isShowSelectCamera = true;
    
      ///默认自定义相册是否显示 闪光灯开关按钮
      bool isShowFlashButtonCamera = true;
    
    }
    

    CameraResultInfo 是拍照或者相册选择结果封闭,拍照成功、拍照取消、相册选择成功、相册选择失败均会回调:

    class CameraResultInfo {
      ///消息标识 
      int code;
      ///回调的消息
      String message ='';
      ///回调的数据
      dynamic data ;
      ///回调的方法名
      String method ='';
    }
    

    3 打开 相册选择照片

      ///打开相册
      void openPhotoAlbum() async {
        /// 相册的选择返回结果
        /// 选择成功与取消都会回调
        CameraResultInfo resultInfo =await FlutterCustomCameraPugin.openPhotoAlbum();
        if (resultInfo.code == 200) {
          imageFile = new File(resultInfo.data["lImageUrl"]);
        }
      }
    

    4 打开一个弹框选择

    在这里插入图片描述

     void openSystemAlert() async {
       /// 相册的选择返回结果
       /// 选择成功与取消都会回调
       CameraResultInfo resultInfo =await FlutterCustomCameraPugin.openSystemAlert();
       
       if (resultInfo.code == 200) {
         imageFile = new File(resultInfo.data["lImageUrl"]);
       }
    
       setState(() {});
     }
    

    本公众号会首发系列专题文章,付费的视频课程会在公众号中免费刊登,在你上下班的路上或者是睡觉前的一刻,本公众号都是你浏览知识干货的一个小选择,收藏不如行动,在那一刻,公众号会提示你该学习了。
    在这里插入图片描述

    展开全文
  • #相机 camera: ^0.5.8+2 video_player: ^0.10.11+1 flutter_image_compress: ^0.6.8 flutter_spinkit: ^4.1.2+1 2.新建IdentifyCard.dart类 代码如下 import 'dart:async'; import 'dart:io'; import 'package:...

    实现效果
    在这里插入图片描述
    1.首先在pubspec.yaml引用插件
    #相机
    camera: ^0.5.8+2
    video_player: ^0.10.11+1
    flutter_image_compress: ^0.6.8
    flutter_spinkit: ^4.1.2+1

    2.新建IdentifyCard.dart类
    代码如下

    import 'dart:async';
    import 'dart:io';
    
    import 'package:camera/camera.dart';
    import 'package:flutter/material.dart';
    import 'package:path_provider/path_provider.dart';
    import 'package:video_player/video_player.dart';
    import 'package:dio/dio.dart';
    import 'package:flutter_image_compress/flutter_image_compress.dart';
    import 'package:path_provider/path_provider.dart' as path_provider;
    import 'package:flutter_spinkit/flutter_spinkit.dart';
    
    Future<void> getIdentifyCardDemo() async {
    // Fetch the available cameras before initializing the app.
      try {
        cameras = await availableCameras();
        FlutterImageCompress.showNativeLog = true;
      } on CameraException catch (e) {
        logError(e.code, e.description);
      }
      //return new IdentifyCard();
    }
    
    class IdentifyCard extends StatefulWidget {
      @override
      _IdentifyCardState createState() {
        return _IdentifyCardState();
      }
    }
    
    void logError(String code, String message) => print('Error: $code\nError Message: $message');
    
    class _IdentifyCardState extends State<IdentifyCard> {
      CameraController controller;
      //String imagePath;
      String videoPath;
      VideoPlayerController videoController;
      VoidCallback videoPlayerListener;
      WidgetsBinding widgetsBinding;
      List<CameraDescription> cameras;
      final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
      @override
      void initState() {
        super.initState();
        getCameras();
        photoPath=null;
      }
    
      @override
      void dispose(){
        super.dispose();
        controller.dispose();
        videoController.dispose(); 
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            key: _scaffoldKey,
            body: new Container(
              color: Colors.black,
              child: new Stack(children: <Widget>[
                new Column(
                  children: <Widget>[
                    Expanded(
                      flex: 6, //flex用来设置当前可用空间的占优比
                      child: new Stack(children: <Widget>[
                        _cameraPreviewWidget(), //相机视图
                        // _cameraFloatImage(),//悬浮的身份证框图
                      ]),
                    ),
                    Expanded(
                      //flex用来设置当前可用空间的占优比
                      flex: 1,
                      child: _takePictureLayout(), //拍照操作区域布局
                    ),
                  ],
                ),
                getPhotoPreview(), //图片预览布局
                Visibility(
                  child: Positioned(
                    top: 20,
                    left: 20,
                    child: IconButton(
                      icon: Icon(
                        Icons.close,
                        color: Colors.white,
                      ),
                      onPressed: () {
                        photoPath = null;
                        if (mounted) {
                          setState(() {});
                        }
                      },
                    ),
                  ),
                  visible: null != photoPath,
                ),
                Visibility(
                  child: Positioned(
                    top: 20,
                    right: 20,
                    child: IconButton(
                      icon: Icon(
                        Icons.check_circle_outline,
                        color: Colors.white,
                      ),
                      onPressed: () {
                        //返回图片  
                        Navigator.pop(context, {"Img": photoPath});
                      },
                    ),
                  ),
                  visible: null != photoPath,
                ),
              ]),
            ));
      }
    
      //相机
      Future<void> getCameras() async {
    // Fetch the available cameras before initializing the app.
        try {
          cameras = await availableCameras();
          //FlutterImageCompress.showNativeLog = true;
          if (cameras != null && !cameras.isEmpty) {
            onNewCameraSelected(cameras[0]); // 后置摄像头
            // onNewCameraSelected(cameras[1]);// 前置摄像头
            if (mounted) {
              setState(() {});
            }
          }
        } on CameraException catch (e) {
          print(e.toString());
        }
      }
    
      Widget _takePictureLayout() {
        return new Align(
            alignment: Alignment.bottomCenter,
            child: new Container(
                color: Colors.blueAccent,
                alignment: Alignment.center,
                child: Row(
                  children: <Widget>[
                    Expanded(
                      flex: 4, //flex用来设置当前可用空间的占优比
                      child: new IconButton(
                        iconSize: 50.0,
                        onPressed: controller != null && controller.value.isInitialized && !controller.value.isRecordingVideo
                            ? onTakePictureButtonPressed
                            : null,
                        icon: Icon(
                          Icons.photo_camera,
                          color: Colors.white,
                        ),
                      ),
                    ),
                    Expanded(
                        flex: 3, //flex用来设置当前可用空间的占优比
                        child: IconButton(
                          icon: Text(
                            '返回',
                            style: TextStyle(color: Colors.white, fontSize: 18),
                          ),
                          onPressed: () {
                            Navigator.pop(context, null);
                          },
                        )),
                  ],
                )));
      }
    
      /// Display the preview from the camera (or a message if the preview is not available).
      Widget _cameraPreviewWidget() {
        if (controller == null || !controller.value.isInitialized) {
          return IconButton(
            icon: Icon(
              Icons.close,
              color: Colors.white,
            ),
            onPressed: () {
              Navigator.pop(context, null);
            },
          );
        } else {
          return new Container(
            width: double.infinity,
            child: AspectRatio(
              aspectRatio: controller.value.aspectRatio,
              child: CameraPreview(controller),
            ),
          );
        }
      }
    
      String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
    
      void showInSnackBar(String message) {
        _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message)));
      }
    
      void onNewCameraSelected(CameraDescription cameraDescription) async {
        if (controller != null) {
          await controller.dispose();
        }
        controller = CameraController(cameraDescription, ResolutionPreset.high);
    
        // If the controller is updated then update the UI.
        controller.addListener(() {
          if (mounted) setState(() {});
          if (controller.value.hasError) {
            showInSnackBar('Camera error ${controller.value.errorDescription}');
          }
        });
    
        try {
          await controller.initialize();
        } on CameraException catch (e) {
          _showCameraException(e);
        }
    
        if (mounted) {
          setState(() {});
        }
      }
    
      void onTakePictureButtonPressed() {
        takePicture().then((String filePath) {
          if (mounted) {
            setState(() {
              videoController = null;
              videoController?.dispose();
            });
            if (filePath != null) {
              //showInSnackBar('Picture saved to $filePath');
              photoPath = filePath;
              setState(() {});
            }
          }
        });
      }
    
      Future<String> takePicture() async {
        if (!controller.value.isInitialized) {
          showInSnackBar('Error: select a camera first.');
          return null;
        }
        final Directory extDir = await getApplicationDocumentsDirectory();
        final String dirPath = '${extDir.path}/Pictures/flutter_test';
        await Directory(dirPath).create(recursive: true);
        final String filePath = '$dirPath/${timestamp()}.jpg';
    
        if (controller.value.isTakingPicture) {
          // A capture is already pending, do nothing.
          return null;
        }
    
        try {
          await controller.takePicture(filePath);
        } on CameraException catch (e) {
          _showCameraException(e);
          return null;
        }
        return filePath;
      }
    
      void _showCameraException(CameraException e) {
        logError(e.code, e.description);
        showInSnackBar('Error: ${e.code}\n${e.description}');
      }
    
      toRestartIdentify() {
        restart = true;
        photoPath = null;
        setState(() {});
        restart = false;
      }
    }
    
    class IdentifyCardPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: showNextPage(context),
        );
      }
    
      showNextPage(BuildContext context) async {
        // await getIdentifyCardDemo(context);
      }
    }
    
    List<CameraDescription> cameras;
    bool showProgressDialog = false;
    bool restart = false;
    var photoPath = null;
    
    Future<File> getCompressImage(File file, String targetPath) async {
      var path = file.absolute.path;
      var result = await FlutterImageCompress.compressAndGetFile(
        path,
        targetPath,
        quality: 88,
        rotate: 180,
      );
    
      print(file.lengthSync());
      print(result.lengthSync());
    
      return result;
    }
    
    Future<String> getTempDir() async {
      var dir = await path_provider.getTemporaryDirectory();
      var targetPath = dir.absolute.path + "/temp.png";
      return targetPath;
    }
    
    Widget getPhotoPreview() {
      if (null != photoPath) {
        return new Container(
          width: double.infinity,
          height: double.infinity,
          color: Colors.black,
          alignment: Alignment.center,
          child: Image.file(File(photoPath)),
        );
      } else {
        return new Container(
          height: 1.0,
          width: 1.0,
          color: Colors.black,
          alignment: Alignment.bottomLeft,
        );
      }
    }
    
    

    3.使用方式

      if (Platform.isAndroid) {
            Navigator.push(context, new MaterialPageRoute(builder: (context) => new IdentifyCard())).then((dynamic result) {
              if (result != null) {
                 Navigator.pop(context);  
                 //这里返回图片路径
                upLoadImage(result["Img"]); 
              }
            });
          }
    
    展开全文
  • 自定义相机(一)

    千次阅读 2018-05-04 16:40:51
    第一步:拍照预览 第二步:相机参数设置、对焦实现、拍照存储在本地、拍照声音、闪光灯 自定义相机(二)自定义相机(一)主要介绍,第一步拍照浏览功能的实现主要涉及:Camera类(注意是android.handware类下的,...
    第一步:拍照预览
    第二步:相机参数设置、对焦实现、拍照存储在本地、拍照声音、闪光灯 自定义相机(二)

    自定义相机(一)主要介绍,第一步拍照浏览功能的实现

    主要涉及:
    Camera类(注意是android.handware类下的,不是android.graphics类)(Camera2暂时不讨论)
    SurfaceView类 SurfaceHolder类 SurfaceHolder.Callback回调接口

    1.首先必需获取拍照需要的权限:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-feature android:name="android.hardware.camera" />
    2.接下来android的UI界面离不开Activity,所以我们创建个CameraActivity.java来显示相机
    当然Camera类是必不可少的,它主要用来管理相机相关配置(闪光灯)和实现拍照功能
    那SurfaceView又是干什么的呢?我们发现SurfaceView继承自View说明是视图,那就是用来显示
    相机预览的。
    SurfaceView做为相机专用View,为摄像等方法重写了很多方法,而且SurfaceView类会重新创建一个新的
    单独线程绘制画面,而普通的View是在UI线程上绘制画面,我们知道UI线程阻塞超过5秒程序就会无响应,所以尽量
    在子线程中执行消耗大的动作,带来良好的用户体验。


    3.在写Activity之前先看看Google是如何继承重写SurfaceView的


    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
        private SurfaceHolder mHolder;
        private Camera mCamera;
    
        public CameraPreview(Context context, Camera camera) {
            super(context);
            mCamera = camera;
    
            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed. 安装SurfaceHolder.Callback回调接口,
    //以便SurfaceView 被创建和销毁时能及时得到通知(接口回调的本质是,给其他类能执行当前类重新实现的接口方法)
            mHolder = getHolder();
    
            mHolder.addCallback(this);
            // deprecated setting, but required on Android versions prior to 3.0
    //已弃用的设置,但在3.0之前的Android版本上需要
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }
    
        public void surfaceCreated(SurfaceHolder holder) {
            // The Surface has been created, now tell the camera where to draw the preview.
    // Surface已创建,现在告诉相机在哪里绘制预览
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            } catch (IOException e) {
                Log.d(TAG, "Error setting camera preview: " + e.getMessage());
            }
        }
    
        public void surfaceDestroyed(SurfaceHolder holder) {
            // empty. Take care of releasing the Camera preview in your activity.
    //管理相机资源的释放
        }
    
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.
    
          //如果您的预览可以更改或旋转,请在这里处理这些事件。
             //确保在调整大小或重新格式化之前停止预览。
            if (mHolder.getSurface() == null){
              // preview surface does not exist
              return;
            }
    
            // stop preview before making changes
            try {
                mCamera.stopPreview();
            } catch (Exception e){
              // ignore: tried to stop a non-existent preview
            }
    
            // set preview size and make any resize, rotate or
            // reformatting changes here
    	/ /设置预览大小,并进行任何调整大小,旋转或
             / /重新格式化这里的变化
    
            // start preview with new settings
            try {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
    
            } catch (Exception e){
                Log.d(TAG, "Error starting camera preview: " + e.getMessage());
            }
        }
    }
    4.SurfaceView已经完成,那怎么在Activity中使用呢?


    public class CameraActivity extends Activity implements View.OnClickListener{
    
        Camera camera=null;
        private LinearLayout container;
        private ImageView iv_camera;
        private ImageView btn_camera;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    //实现全屏,没有标题栏,也没有状态栏
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
            setContentView(R.layout.activity_camera);
            if (checkCameraHardware()){
                openCamera();
                initView();
            }else {
                Toast.makeText(this,"当前不支持相机!",Toast.LENGTH_SHORT).show();
            }
    
        }
        private void initView(){
    
            iv_camera=(ImageView) findViewById(R.id.iv_camera);
            btn_camera=(ImageView) findViewById(R.id.btn_camera);
            iv_camera.setOnClickListener(this);
            iv_camera.setOnClickListener(this);
    
        }
        //开始预览相机
        private void openCamera(){
            if (camera==null){
    //获得相机实例
                camera=camera.open();
    //注册容器控件
                container=(LinearLayout)findViewById(R.id.ll_contain);
    //实例化SurfaceView
                CameraPreview cameraPreview=new CameraPreview(CameraActivity.this,camera);
                container.addView(cameraPreview);
            }
        }
        //释放相机资源
        private  void releaseCamera(){
            if (camera!=null){
                camera.setPreviewCallback(null);
                camera.stopPreview();
                camera.release();
                camera=null;
            }
    
        }
        // 判断相机是否支持
        private boolean checkCameraHardware() {
            if (this.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_CAMERA)) {
                return true;
            } else {
                return false;
            }
        }
    //相机不使用时 释放资源
    @Override
    protected void onPause() {
        super.onPause();
        releaseCamera();
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        openCamera();
    }
        @Override
        public void onClick(View v) {
    
        }
    }
    值得注意的是,android的相机只能被一个APP线程绑定,所以同一时间内,不能打开两个相机,我们用完相机时应该释放camera(Camera.release()来释放相机资源)


    5.Activity的布局文件


    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <LinearLayout
            android:id="@+id/ll_contain"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </LinearLayout>
            <ImageView
                android:id="@+id/iv_camera"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:background="@drawable/ic_focusing_begin"/>
    
            <ImageView
                android:id="@+id/btn_camera"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_centerHorizontal="true"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="10dp"
                android:src="@drawable/ic_bt"/>
        </RelativeLayout>
    </LinearLayout>

    运行程序发现(暂时没有拍照功能)有了拍照预览功能了,但是显示效果不佳,拍照方向不正确,尺寸不正确等
    为什么会这样呢?自定义相机(二)我们将继续讲述...

    自定义相机源码


    展开全文
  • Flutter实现自定义相机界面

    千次阅读 2020-03-03 14:42:20
    自定义相册使用到的插件 camera: 0.5.7+4 permission_handler: 3.2.2 path_provider: 1.6.0 主要代码 主界面: class MyHomePage extends StatefulWidget { @override _MyHomePageState...
  • Flutter选择图片和拍照示例,详情请看博客:https://yuzhiqiang.blog.csdn.net/article/details/88345232
  • Flutter

    千次阅读 2019-09-27 11:00:21
    作为谷歌的开源移动UI框架,Flutter可以快速在iOS和Android上构建高质量的原生用户界面,2019年,Google I/O 大会宣布了 flutter 支持 web 开发。flutter 目前的目的就是全平台开发,一套代码同时运行在 iOS 、...
  • flutter 输入框组件TextField

    万次阅读 多人点赞 2018-09-08 12:35:56
    TextField 顾名思义文本输入框,类似于Ios中...相信大家在原生客户端上都用过这个功能,就不在做具体介绍了,接下来还是具体介绍下Flutter中TextField的用法。 TextField的构造方法: const TextField({ Key ...
  • flutter 屏幕适配 字体大小适配

    万次阅读 热门讨论 2018-09-21 00:08:21
    前言: 现在的手机品牌和型号越来越多,导致我们平时写布局的时候会在个不同的移动设备上显示的效果不同, 比如我们的设计稿一个View的大小是300px,如果直接写300px,可能在当前设备显示...而flutter本身并没有适...
  • FLUTTER

    千次阅读 2018-12-06 16:11:28
    安装FLUTTER 官方教程: https://codelabs.flutter-io.cn/codelabs/first-flutter-app-pt1-cn/index.html#1 下载Flutter的SDK...
  • Kotlin vs Flutter,我到底应该怎么选?

    万次阅读 多人点赞 2020-01-17 08:05:15
    在移动应用开发方面,Kotlin和Flutter是目前比较火热的两门技术。其中,Kotlin是由Jetbrains研发,后被Google大力扶持,Flutter则是由Google自己独立研发的。从目前Google官方的态度来看,这两门技术都被无限看好,...
  • flutter 提供了 print(Object object) 来向开发工具的控制 台输出日志信息 print("test"); common_utils 工具类已经将pring 封装为工具类 common_utils: ^1.1.1 使用common_utils工具类中的LogUtil //初始化设置 ...
  • flutter基础教程

    万人学习 2018-08-27 15:50:34
    Flutter基础入门视频教程,Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。通过这门课程的学习使得大家掌握使用flutter来编写跨平台移动客户端应用。
  • 这个是我粘贴复制来的,做个随笔记录。看见好几个平台都有,也不知道最初是谁整理的,就不贴原作者了。。。 漂亮的开源库 第三方库 ... 网络...
  • flutter、rn、uni-app比较

    万次阅读 多人点赞 2019-08-28 16:30:46
    转自:DCloud社区 uni-app频道的文章Flutter、RN、uni-app比较 前言 每当我们评估新技术时要问的第一个问题就是“它会给我们的业务和客户带来哪些价值?”,工程师们很容易对闪闪发光的新事物着迷,却经常会忽略...
  • 如何使用Flutter开发web应用

    万次阅读 2019-06-21 22:48:53
    前言:Flutter系列的文章我应该会持续更新,从User Interface(UI)到数据相关(文件、数据库、网络)再到Flutter进阶(平台特定代码编写、测试、插件开发等),欢迎感兴趣的读者持续关注(可以扫描左边栏二维码或者...
  • Flutter加载Html并实现与JS 的双向调用

    万次阅读 热门讨论 2019-07-20 16:01:01
    Flutter 加载Html、Flutter加载静态Html、Flutter与JS的双向调用
  • flutter 轮播组件 Swiper

    万次阅读 热门讨论 2018-09-19 19:18:54
    flutter_swiper flutter最强大的siwiper, 多种布局方式,无限轮播,Android和IOS双端适配. 先放上github上的几张图看一下效果。 ✨✨ 新功能 更多 截图 图就先放到这里,我们看一下用法。 安装 ...
  • Flutter介绍

    千次阅读 2018-08-24 13:29:48
    1.flutter简介 Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能快速构建高性能、高保真的iOS和Android应用程序,并且在排版、图标、滚动、点击等方面实现零差异。 快速开发: 毫秒级的...
  • flutter 的像素尺寸

    万次阅读 热门讨论 2019-09-24 08:49:25
    但是在flutter中写尺寸是没有单位的。如: SizedBox(height: 736,width: 375,child: Container(color: Colors.lightBlueAccent) ) 实际上它的尺寸当然是有的。 在不同设备中它与devicePixelRatio 属性有关,通过...
  • Flutter 1.22 正式发布

    万次阅读 多人点赞 2020-10-09 21:49:38
    支持iOS 14和Android 11,新的i18n和l10n支持,可用于生产的Google Maps和WebView插件,新的App Size工具等等!...Flutter 1.22在以前版本的基础上构建,使开发人员能够从一个代码库为多个平台构建快速,美观的用..
  • Flutter实战一Flutter聊天应用(一)

    万次阅读 2017-06-05 20:02:08
    不知不觉,进阶的教程已经写了几十篇了,通过前面的学习,大家已经打下了良好的基础,接下来我们就开始进行项目实战吧!...首先我们要在IntelliJ编辑器中启动一个新的Flutter项目: 启动IntelliJ IDE
  • 本来这篇文章应该讲一下Flutter的插件开发,但是在插件开发的基础是PlatformChannel,也就是Flutter与Android/iOS Native的通信,理解了这一个知识点,Flutter的插件开发也就不在话下。 1.PlatformChannel概述 Flu.....
  • Flutter 所有UI控件使用教程

    万次阅读 2018-06-14 17:20:07
    本篇博客将把Flutter所有UI控件的使用方法写出,我学习Flutter是通过Flutter官网来学习的,不需要科学上网,比如这这个网址说的就是Flutter的UI控件的使用方法 https://flutter.io/widgets/widgetindex/ ...
  • Flutter从入门到实战

    万次阅读 热门讨论 2018-05-28 00:06:52
    Flutter从入门到实战 Flutter概述  Flutter是一款移动应用程序SDK,一份代码可以同时生成iOS和Android两个高性能、高保真的应用程序。  Flutter目标是使开发人员能够交付在不同平台上都感觉自然流畅的高...
  • Flutter解密

    千次阅读 2018-08-16 14:47:34
    Flutter:谷歌的移动端UI开源框架,2018年2月27日, Google发布了Flutter的第一个Beta版本。它是Google使用Dart语言开发的移动应用开发框架,使用Dart代码构建高性能、高保真的iOS和Android应用程序。Flutter的工具...
  • 官方解决方案:https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps 这一篇讲一下iOS老项目集成Flutter流程。并且实现 iOS 页面跳转到 Flutter 页面 Flutter 页面跳转到 iOS 页面 一、...
  • Flutter混合开发二-FlutterBoost使用介绍

    千次阅读 2019-05-31 07:55:15
    Flutter混合开发专题一》中我们介绍了Flutter官方提供的混合开发解决方案,但是其存在着一些问题并没有解决,比如原生和Flutter页面叠加跳转由于Flutter Engine重复创建而导致内存暴增的问题、Flutter应用中全局...
  • 作为 GSY 开源系列的作者,在去年也整理过 《移动端跨...Flutter 进行全面的分析对比,希望能给你更有价值的参考。 是的,这次没有了 Weex,超长内容预警,建议收藏后阅。 前言 临冬之际,移动端跨平台在经历...
  • Flutter基础—开发环境与入门

    万次阅读 热门讨论 2016-10-21 01:30:06
    Flutter开发环境的前提要求如下: Windows 7以上版本(64位)、Mac或Linux操作系统(64位) 磁盘空间:400 MB(不包括Android Studio的磁盘空间)。 已经安装及配置Git Mac开发环境 Flutter SDK Dart ...

空空如也

1 2 3 4 5 ... 20
收藏数 69,987
精华内容 27,994
关键字:

flutter自定义相机