Android实战技巧之三十一:拍照和录像 with Camera

Developer Guides中有一篇是专门讲Camera的,而且讲的特别细。千万别以为有了这么好的文档就可以轻松的使用android.hardware.Camera这个包去拍照和录像了,各种坑在前面等着你呢。好了,下面将要讲述我们如何像辽宁队在常规赛中填坑的经历。

一、借助intent

这就十分easy了,发个intent就有人帮你搞定拍照和录像。

拍照:

    public void onTakePhoto(View view) {
        // create Intent to take a picture and return control to the calling application
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        fileUri = CameraUtils.getOutputMediaFileUri(CameraUtils.MEDIA_TYPE_IMAGE); // create a file to save the image
        intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name

        // start the image capture Intent
        startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
    }

录像:

    public void onTakeVideo(View view) {
        //create new Intent
        Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

        fileUri = CameraUtils.getOutputMediaFileUri(CameraUtils.MEDIA_TYPE_VIDEO);  // create a file to save the video
        intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);  // set the image file name

        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high

        // start the Video Capture Intent
        startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
    }

对结果的处理:

    private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
    private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 101;
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                // Image captured and saved to fileUri specified in the Intent
                Toast.makeText(this, "Image saved to:\n" +
                        data.getData(), Toast.LENGTH_LONG).show();
            } else if (resultCode == RESULT_CANCELED) {
                // User cancelled the image capture
            } else {
                // Image capture failed, advise user
            }
        }

        if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                // Video captured and saved to fileUri specified in the Intent
                Toast.makeText(this, "Video saved to:\n" +
                        data.getData(), Toast.LENGTH_LONG).show();
            } else if (resultCode == RESULT_CANCELED) {
                // User cancelled the video capture
            } else {
                // Video capture failed, advise user
            }
        }
    }

不用上截图了,就是这么简单。

二、使用Camera编码

按照文档的步骤,最后却没有搞定,真心让我很受伤。经过一周的折腾,最后还是借助网友的力量搞定。

需要的权限

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-permission
         android:name="android.permission.RECORD_AUDIO">
    </uses-permission>
    <uses-permission
     android:name="android.permission.WRITE_EXTERNAL_STORAGE">
    </uses-permission>

流程

简单的流程如下图:

MediaRecorder状态图:

初始化SurfaceView与SurfaceViewHolder

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSurfaceView = (SurfaceView)findViewById(R.id.surfaceview);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(new Callback() {
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                releaseCamera();
            }

            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                initPreview();
            }

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

            }
        });
protected void initPreview() {
        mCamera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
        try {
            mCamera.setPreviewDisplay(mSurfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        setCameraDisplayOrientation(this,CameraInfo.CAMERA_FACING_BACK,mCamera);
        mCamera.startPreview();
    }

    public static void setCameraDisplayOrientation(Activity activity,
             int cameraId, android.hardware.Camera camera) {
         android.hardware.Camera.CameraInfo info =
                 new android.hardware.Camera.CameraInfo();
         android.hardware.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);
     }

拍照

拍照还是很简单的,实现PictureCallback接口:

    private Camera.PictureCallback mPicture = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            Log.d(TAG,"onPictureTaken");
            mPhotoFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
            if (mPhotoFile == null){
                Log.d(TAG, "Error creating media file, check storage permissions: ");
                return;
            }

            try {
                FileOutputStream fos = new FileOutputStream(mPhotoFile);
                fos.write(data);
                fos.close();
                Log.d(TAG,"save picture success");
                //notify
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    Intent mediaScanIntent = new Intent(
                            Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                    Uri contentUri = Uri.fromFile(mPhotoFile); //out is your output file
                    mediaScanIntent.setData(contentUri);
                    MainActivity.this.sendBroadcast(mediaScanIntent);
                } else {
                    sendBroadcast(new Intent(
                            Intent.ACTION_MEDIA_MOUNTED,
                            Uri.parse("file://"
                                    + Environment.getExternalStorageDirectory())));
                }
                mCamera.reconnect();
            } catch (FileNotFoundException e) {
                Log.d(TAG, "File not found: " + e.getMessage());
            } catch (IOException e) {
                Log.d(TAG, "Error accessing file: " + e.getMessage());
            }
        }
    };

调用takePitcture方法:

    public void onPhotoClicked(View view) {
        mCamera.takePicture(null, null, mPicture);
    }

录像

有了上述的准备,启动录像的功能就相对清晰一些,它分为下面的步骤:

    private void startMediaRecorder() {
        mCamera.unlock();
        mIsRecording = true;
        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.reset();
        mMediaRecorder.setCamera(mCamera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        CamcorderProfile mCamcorderProfile = CamcorderProfile.get(CameraInfo.CAMERA_FACING_BACK,
                CamcorderProfile.QUALITY_HIGH);
        mMediaRecorder.setProfile(mCamcorderProfile);
        mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());
        mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());

        try {
            mMediaRecorder.prepare();
        } catch (Exception e) {
            mIsRecording = false;
            Toast.makeText(this, "fail", Toast.LENGTH_LONG).show();
            e.printStackTrace();
            mCamera.lock();
        }
        mMediaRecorder.start();
    }

图片与视频保存的路径:

    private File getOutputMediaFile(int type) {
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "Camera App");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                Log.d("linc", "failed to create directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "IMG_"+ timeStamp + ".jpg");
        } else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "VID_"+ timeStamp + ".mp4");
        } else {
            return null;
        }

        return mediaFile;
    }

代码的其余部分:

    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;

    private Camera mCamera;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private Button startButton;
    private boolean mIsRecording = false;
    private MediaRecorder mMediaRecorder;
    private File mPhotoFile;
    protected void releaseCamera() {
        if(mCamera!=null){
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
        private void stopMediaRecorder() {
        if(mMediaRecorder !=null){
            if(mIsRecording){
                mMediaRecorder.stop();
                //mCamera.lock();
                mMediaRecorder.reset();
                mMediaRecorder.release();
                mMediaRecorder =null;
                mIsRecording = false;
                try {
                    mCamera.reconnect();
                } catch (IOException e) {
                    Toast.makeText(this, "reconect fail", Toast.LENGTH_LONG).show();
                    e.printStackTrace();
                }
            }
        }
    }

参考:

sdk/docs/guide/topics/media/camera.html#custom-camera

http://blog.csdn.net/shen332401890/article/details/8819564

时间: 2024-10-03 23:03:54

Android实战技巧之三十一:拍照和录像 with Camera的相关文章

Android实战技巧之十一:Android Studio和Gradle

经过两个多月的AS体验,我认为是时候将Android的开发环境迁移到AS上了.目前最新版本是1.0.2,除了UI控件拖拽偶尔崩溃的问题(Ubuntu),其他功能用来还是十分流畅和高效.打动我的有如下几个特色: 智能感知体验特好,堪比VS 布局预览,手写布局后预览页面即时显示,便于布局调整和优化 编辑速度飞快流畅,毫无eclipse的卡顿 布局或源码中有图标和颜色的预览,十分直观 调试时体验极佳 集成了Terminal,喜欢命令行操作的伙伴不用额外启动终端了. 总之一句话,就是用起来特别爽! An

Android实战技巧之三十:人脸检测-静态

最近微软的how-old.net把人脸识别技术又大大的火了一把.通过大数据和复杂的算法,能够神奇的预测出照片中人物的性别和年龄.虽然错误率也不低,但是大家都抱着玩一玩乐一乐的心态把照片传上去让机器来鉴定一下自己的颜龄. 人脸识别算法是高深复杂的,面对着计算机视觉的种种数学公式,我就已经投降了.先来简单的玩玩人脸检测吧.Android早已提供了FaceDetector类,今天就来看看如何使用这个类人脸检测吧. 流程: 1.打开文件夹选择照片 2.将照片加载到bitmap中并缩放到设置的宽高 3.用

Android实战技巧之三十七:图片的Base64编解码

通常用Base64这种编解码方式将二进制数据转换成可见的字符串格式,就是我们常说的大串,10块钱一串的那种,^_^. Android的android.util包下直接提供了一个功能十分完备的Base64类供我们使用,下面就演示一下如何将一张图片进行Base64的编解码. 1.找到那张图片 public void onEncodeClicked(View view) { //select picture Intent intent = new Intent(); intent.setType("i

Android实战技巧之三十二:Android Studio中的源代码管理

Android Studio最近经过了两次升级到了Android Studio 1.2.1.1, 用起来是越来越顺手了.AS中加入了主流的源码管理工具,让开发者不用离开AS就可以提交和管理代码. 下面就演示一下在AS中使用git管理代码. 选择要提交的代码 右键->commit 编写commit message 可以选择commit and push一起完成提交的动作 确认后push 查看提交历史和对比文件 总结: 玩git的都知道在命令行下有些版本历史信息的显示是不方便的,我们需要借助gitk

Android实战技巧之三十五:了解native activity

1.native activity的意义 很多人觉得Android的Fwk提供的支持足够好了,既然Google不推荐用Ndk开发为什么又放宽Ndk的限制而推出可以无Java开发Android App呢?我的理解是不同的技术实现会有其适合的场景. Ndk的适用场景官方给出三点:1.平台间的App移植 2.复用现有库 3.对软件性能要求较高的场合比如游戏等.那么native activity在十分适合游戏领域,比如cocos-2dx对其的使用. 2.初步了解native activity 借助SDK

Android实战技巧之四十一:制作自己的Android SDK

编译自己的SDK 特种设备会有一些额外的功能,这样就需要给应用层提供特定的API,从而需要定制自己的SDK. 编译自己的sdk还是很简单的,因为google帮我们做好基础工作,步骤如下: $ source build/envsetup.sh $launch 'what you want' $ make update-api $ make PRODUCT-sdk-sdk -j8 如果一切顺利的编译完成,那么恭喜你.如果遇到错误也不要紧,因为这是正常的.下面列出我遇到的主要错误:tools/base

Android实战技巧之五十一:libjpeg与Android

libjpeg是一个被广泛使用的JPEG解码.JPEG编码和其他的JPEG功能的实现库. 说它使用广泛,是因为它跨了很多平台.比如Linux平台.JDK.Android和其他库如tess-two等等. 最近正在研究Android中直接用C/C++将图片的字节数组保存成图片,libjpeg库十分擅长. 官网www.ijg.org下载最新的版本9b,解压后会看到数量众多makefile,足以见得其对多平台的支持. 获取libjpeg.so 1.libjpeg库在安卓源码路径为/external/jp

Android实战技巧之三十六:Makefile快速入门

目标 通过一篇文章的介绍达到能够编写简单Makefile以及能够看懂普通的Makefile之目的. make简介 make是一个老牌的构建(build)工具,1970年问世以来已经度过了45年的时光而魅力不减,这在技术发展日新月异的今天是不可思议的.make在大型的软件项目中发挥着巨大作用.我是在学习Linux kernel时才第一次接触它,Android系统也是用make和python等脚本一起构建系统,所以掌握make知识是你迈进这些系统的第一道坎.你一定要给予make足够的重视,不要以为掌

Android实战技巧之三十八:Handler使用中可能引发的内存泄漏

问题描写叙述 曾几何时,我们用原来的办法使用Handler时会有以下一段温馨的提示: This Handler class should be static or leaks might occur 以下是更具体的说明(Android Studio上的警告,不知道Eclipse上是否同样) Since this Handler is declared as an inner class, it may prevent the outer class from being garbage coll