Android 相机学习

需求是万恶之源,今天我们来说说相机

调用系统相机

如果只是调起相机拍照,很简单就两步:

1.权限

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

2.创建意图跳转

     Intent intent =new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
     startActivity(intent);

但是我们一般都会需要获取图片数据,Android也给我们提供了调用的方式

1.替换跳转方式

    startActivityForResult(intent, REQUEST_CODE_CAPTURE_RAW);

2.监听返回

    /**
     * 图片拍照回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //通过data获取图片信息
    }

如果你使用过上面的方式你会发现得到图片质量并不高,这是由于现在相机的相机像素都很高,拍出来的照片所占用的内存(这里说的内存是bitmap占用的,不是图片在SD卡占用的,注意区分)都很高,容易oom,所以我们这里我们得到不是原图是缩略图

那如果需要原图怎么办了?这里也是有方式获取的

直接上代码

//拍照(返回原始图)
    public void gotoCaptureRaw() {
        imageFile = createImageFile(false);
        if (imageFile != null) {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            //获取设备sdk版本
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri imgUri = FileProvider.getUriForFile(this, AUTHORITY, imageFile);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);
            } else {//7.0 以下
                //设置拍照图片保存路径
                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
            }
            //设置图片保存格式
            intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
            ComponentName componentName = intent.resolveActivity(getPackageManager());
            //判断组件是否为null,避免崩溃
            if (componentName != null) {
                startActivityForResult(intent, REQUEST_CODE_CAPTURE_RAW);
            }
        }
    }

想要获取原图,就得指定拍摄照片后图片的存储路径
关键代码

 intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);

注意7.0以上需要使用FileProvider,怎么使用这里就不多讲了,自行查阅资料
然后在onActivityResult回调中,通过imageFile获取图片,不过有一点需要注意,由于获取是原图,可能会导致oom,建议对图片压缩处理

简单拍个照,上面这些应该够用了.但有时系统相机并不满足实际需求,这时就需要自定义相机了

自定义相机

除非万不得已,千万不要自定义
除非万不得已,千万不要自定义
除非万不得已,千万不要自定义

为什么这样说,因为坑多呀

想要自定义相机主要用到两个类

Camera
SurfaceView

他们的关系你可以看成:

image

注意Android5.0 用Camera2 取代Camera1 ,这里Camera1已经可以满足大部分需求,如果要实现更复杂的功能,建议使用Camera2

下面我们就来自己开发一个自定义相机

  1. 添加相关权限
  2. 在相关xml中添加SurfaceView,用于显示预览界面
  3. 获取SurfaceHolder添加相关监听
  4. 在surfaceCreated回调处打开相机,设置相关参数,开启预览
  5. 拍照,获得图片
  6. 图片处理,保存
  7. 退出,释放资源

权限

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

在相关xml中添加SurfaceView,用于显示预览界面

<!--预览内容-->
    <SurfaceView
        android:id="@+id/sv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />     

获取SurfaceHolder添加相关监听

        mSurfaceView = findViewById(R.id.sv);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        surfaceHolderCallBack = new SurfaceHolderCallBack();
        mSurfaceHolder.addCallback(surfaceHolderCallBack);
    
class SurfaceHolderCallBack implements SurfaceHolder.Callback {

        @Override
        public void surfaceCreated(final SurfaceHolder surfaceHolder) {
            new Thread(new Runnable() {//启动相机 特别耗时 需要开启线程处理 
                @Override
                public void run() {
                  //打开相机操作
                }
            }).start();

        }

        @Override
        public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
         //释放相机资源
        }
    }

在surfaceCreated回调处打开相机,设置相关参数,开启预览

     //打开相机
     Camera camera = Camera.open(//相机Id);
     //设置预览界面
     camera.setPreviewDisplay(//surfaceHolder);
     //设置预览角度
     //camera.setDisplayOrientation(//角度);
     //开启预览
     camera.startPreview();

完成上述几步,就可以看见预览界面了,不过会发现你的预览结果和你想象的应该不太一样,第一个坑出现了,这里你只需要知道camera.setDisplayOrientation(//角度)是处理这个问题的,后面会对出现的坑做统一的解释

预览完成了,接下来解释拍照了

拍照,获得图片

我采用的自动变焦的方式拍照,大概流程是:点击拍照后,开始自动变焦,变焦完成,相机开始拍照,拍照完成获取图片

   /**
     * 照相
     */
    public void capture(Camera camera) {
        if (camera != null) {
            Camera.Parameters parameters = camera.getParameters();
            //TODO 判断是否支持自动变焦
            parameters.setFlashMode(Camera.Parameters.FOCUS_MODE_AUTO);
            //需要设置图片的尺寸
//            List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
//            for (Camera.Size supportedPictureSize : supportedPictureSizes) {
//                if (supportedPictureSize.width == 1280) {//取景方向默认是横向的
//                    parameters.setPictureSize(supportedPictureSize.width, supportedPictureSize.height);
//                }
//            }
            //TODO 一定注意保存
            camera.setParameters(parameters);
            camera.autoFocus(new Camera.AutoFocusCallback() {
                @Override
                public void onAutoFocus(boolean success, Camera camera) {
                    if (success) {
                        camera.takePicture(null, null, pictureCallback);
                    }
                }
            });
        }
    }
    
    /**
     * 相机生成的图片数据
     */
    Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] bytes, Camera camera) {
            //TODO 可能需要一个线程去执行
            saveImage(bytes, "temp1.png");
        }
    };

恩,拍照获取图片也完成了,细心的朋友会返现上面有一段注释掉的代码,这是为了第二坑,至于坑是什么后面说.
获取到图片我们就可以保存了

图片处理,保存

 //保存图片
        File rootFile = new File(rootFolderPath + File.separator + "capture" + File.separator + "temp1.jpg");
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(rootFile);
            bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, os);
        } catch (Exception e) {
            e.printStackTrace();
        }

这样图片也保存了,看起来没啥问题,不过第三坑出现了,依旧后面解释.

最后

退出,释放资源

    ...
   @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
         //释放相机资源
          if (camera != null) {
              camera.stopPreview();
              camera.release();
        }
        }
    ...

一个简单的相机就完成了,试着运行一下,结果貌似有些出入,这就需要我们来解决上面提到的三个坑了,坑是什么,怎么解决,请看下篇文章

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

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,979评论 3 119
  • 1、描述 一双白色的运动鞋,一身黑色运动服,体型略胖,黑色边框的眼镜后配上一副时髦的苹果耳机。神情专注着手机屏幕,...
    钱程浩瀚阅读 155评论 0 0
  • 为什么想说媒体呢?朋友圈经常会有一些转发的文章,这些文章有各类数据啊、表啊、排名啊等等,其实如果有点基本批判思维,...
    昆言昆语阅读 196评论 0 0
  • 今天的冥想练习要试着去倾听思想深处的安宁。有一种安宁感可以超越来自外在自我的噪音,当你的思想变得越来越平静的时候,...
    格锅阅读 178评论 0 0
  • 我的悲催前半生,从我还是一颗小精子就开始了。 1980年夏天,我大哥意外夭折,那年他12岁。 亲生儿子!养了 12...
    于小洺阅读 1,407评论 16 4