【 Android 】Camera2 初探

作者: Tyhoo_Wu | 来源:发表于2017-12-23 10:19 被阅读57次

    调用手机摄像头,在当今的众多 App 中都有使用,比如:拍照、扫二维码等。

    本篇文章将实现:调用系统相机进行拍照,然后将拍的图片在 App 上显示(显示最后一张图片,和显示之前拍过的所有图片)。

    在没接触过 Camera2 之前一定要查文档,官方文档 是首选,而且是最准确的,最新的。

    下面就进入进入本篇文章的正题。

    先来看一下本文实现的演示图:


    示例图片.gif

    一、配置文件

    调用系统相机就一定要申请相机权限:

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

    因为相机权限是危险权限,6.0+ 需要动态申请,这里我只做简易的动态申请:

    private static String[] PERMISSION_REQUEST = {
            Manifest.permission.CAMERA
    };
    
    private static final int INTENT_CAMERA_REQUEST_CODE = 666;  // 数字随便写
    
    // 运行时权限请求(简易版)
    ActivityCompat.requestPermissions(MainActivity.this, PERMISSION_REQUEST, INTENT_CAMERA_REQUEST_CODE);
    

    二、实现流程

    流程: ① 在 App 上点击按钮调用系统相机 ② 拍照 ③ 拍照之后返回到 App ④ 在 App 上显示照片(显示最后一张,显示所有拍照的图片)

    重点:
    ① 调用系统相机
    startActivityForResultonActivityResult 的使用
    ③ 显示所有的图片,我们这里使用 RecyclerView ,因为是将所有拍过的图片都添加到列表里,直接将图片添加到列表里显然很不安卓,所以将图片先转换成 Base64 二进制流的形式,然后添加到列表里,在显示的时候将 Base64 二进制流形式 转换成图片,显示出来。

    好的思路捋清了,就开始代码实现。

    三、代码实现

    ① 调用系统相机

    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    

    我们通过 intent.putExtra() 传值使用 Android 官方 提供的方法:

    /**
     * Create a File for saving an image
     */
    private static File getOutputMediaFile(int type) {
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "Camera2Demo");
    
        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                Log.d("Camera2Demo", "failed to create directory");
                return null;
            }
        }
    
        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "Camera2Demo_" + timeStamp + ".jpg");
    
            Log.d(TAG, "getOutputMediaFile: " + mediaStorageDir.getPath() + File.separator);    // 图片存储的文件夹路径
        } else {
            return null;
        }
    
        return mediaFile;
    }
    
    /**
     * Create a file Uri for saving an image or video
     */
    private static Uri getOutputMediaFileUri(int type) {
        return Uri.fromFile(getOutputMediaFile(type));
    }
    

    那么调用系统相机的那部分代码就是:

    private Uri mFileUri;
    
    按钮.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            // 跳转到系统相机
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);    // 拍摄照片
            mFileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);             // 创建文件保存图片
            intent.putExtra(MediaStore.EXTRA_OUTPUT, mFileUri);             // 设置图片名
            startActivityForResult(intent, INTENT_CAMERA_REQUEST_CODE);     // 捕获相机拍摄的图片
        }
    });
    

    这样系统相机就启动了,拍照完之后,将图片保存到已创建的文件夹里,供 App 使用。

    ② 使用 onActivityResult 将拍的图片返回给 App
    我们在上面将图片保存到手机本地文件夹里,那么在读取本地文件夹里的文件的时候,6.0+ 的设备就要动态申请运行时读写权限

    private static String[] PERMISSION_REQUEST = {
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE
    };
    
    ActivityCompat.requestPermissions(MainActivity.this, PERMISSION_REQUEST, INTENT_CAMERA_REQUEST_CODE);
    

    还要做的一步操作就是检查是否是图片。

    /**
     * 检查扩展名,得到图片格式的文件
     */
    private boolean checkIsImageFile(String fName) {
        boolean isImageFile;
        // 获取扩展名
        String FileEnd = fName.substring(fName.lastIndexOf(".") + 1,
                fName.length()).toLowerCase();
        if (FileEnd.equals("jpg") || FileEnd.equals("png") || FileEnd.equals("gif")
                || FileEnd.equals("jpeg") || FileEnd.equals("bmp")) {
            isImageFile = true;
        } else {
            isImageFile = false;
        }
        return isImageFile;
    }
    

    好的,我们的 onActivityResult 的结构可以看成是这样:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == INTENT_CAMERA_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
    
            } else if (resultCode == RESULT_CANCELED) {
                
            } else {
                
            }
        }
    }
    

    让我们来实现 if (resultCode == RESULT_OK) { } 里面的内容

    首先是读取本地存储图片的路径,然后将所有的图片添加到列表里
    List<String> imagePathList = new ArrayList<>();
    
    File fileAll = new File("/storage/emulated/0/Pictures/Camera2Demo/");
    File[] files = fileAll.listFiles();
    for (int i = 0; i < files.length; i++) {
        File file = files[i];
        if (checkIsImageFile(file.getPath())) {
            imagePathList.add(file.getPath());
        }
    }
    
    得到所有的图片之后,将图片转换成 Base64 二进制流形式并添加到列表里

    公开一个把文件转换成 Base64 的通用方法:

    public static String encodeBase64File(String path) throws Exception {
        byte[] videoBytes;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        @SuppressWarnings("resource")
        FileInputStream fis = new FileInputStream(new File(path));
        byte[] buf = new byte[1024];
        int n;
        while (-1 != (n = fis.read(buf)))
            byteArrayOutputStream.write(buf, 0, n);
        videoBytes = byteArrayOutputStream.toByteArray();
        return Base64.encodeToString(videoBytes, Base64.NO_WRAP);
    }
    
    List<String> base64PhotoList = new ArrayList<>();
    
    for (int i = 0; i < imagePathList.size(); i++) {
        try {
            // 将图片转换成 Base64
            String base64PhotoFile = BitmapUtils.encodeBase64File(imagePathList.get(i));
            // 将所有的 Base64 文件存到 List 里
            base64PhotoList.add(base64PhotoFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    ③ 显示图片
    显示图片我们使用强大的 Glide 代替安卓默认的图片显示。

    添加 Glide

    implementation 'com.github.bumptech.glide:glide:4.4.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.4.0'
    

    添加 RecyclerView

    implementation 'com.android.support:recyclerview-v7:27.0.2'
    

    单张显示:

    Bitmap bitmap = BitmapFactory.decodeFile(imagePathList.get(i));
    Glide.with(this).load(bitmap).into(图片布局);
    

    显示所有图片:
    创建 Adapter ,然后自定义一个 public 方法接收传来的列表数据。

    private List<String> mList = new ArrayList<>();
    
    public void setDataList(List<String> list) {
        mList = list;
        notifyDataSetChanged();
    }
    
    // 将列表数据传给 Adapter (在 Adapter 对应的 Activity 里执行)
    mAdapter.setDataList(base64PhotoList);
    

    onBindViewHolder 里面将图片依次显示出来

    @Override
    public void onBindViewHolder(MainViewHolder holder, int position) {
        // Base64 转换成 Bitmap
        Bitmap bitmap = BitmapUtils.convertToBitmap(mList.get(position));
    
        Glide.with(mContext).load(bitmap).into(图片文件);
    }
    

    示例项目已上传至 GitHub ,如果对你有帮助请 Start,谢谢!

    关于 Camera2 最基本的用法就讲到这里。文章对你有帮助,给点个赞,关注我。谢谢!

    相关文章

      网友评论

        本文标题:【 Android 】Camera2 初探

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