转载:Android多媒体应用开发系列(一) 拍照

转载:http://blog.csdn.net/yjp19871013/article/details/53431358 


前段时间工作很忙,忙了也好,开始思考自己到底应该如何工作,如何生活。前段时间的项目主要围绕Android,MFC,Winform开展,每个项目基本上都进行过一轮架构调整,所以对这些方面都有了比较深刻的理解,最近闲下来了,有时间,把前段时间项目的东西做一些整理,方便查阅,也提供一个大家共同学习的平台。

       首先从Android的开始吧,比较热门嘛,主要是使用了Android多媒体开发的各个部分(Camera,Surface,MediaPlayer,MediaRecorder,MediaCodec等),知识也算系统了,基本都用到了,所以考虑写成一个系列,先从基本的拍照走起。讲原理贴代码想必大家也受够了,不如换一种轻松的方式,中间融合上开发的流程结合重构,能够更好地掌握技能。跳过创建项目等等,直接开始创作。

      Android拍照,就要使用相机Camera,核心的接口

  1. android.hardware.Camera.takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)  
参数是三个回调,分别是shutter——快门回调,raw——没有压缩过的原始数据回调,jpeg——返回jpeg数据的回调

这里有一个小发现,如果将shutter设置为null,将不会发出拍照的“咔嚓”声,有兴趣的可以尝试一下。

       现在知道了要使用的接口,还有许多准备工作要做,但是好不容易查到的接口该如何是好,先封装函数吧,留着用(相机记得要释放)

  1. public class MainActivity extends AppCompatActivity {  
  2.     private Camera mCamera;  
  3.     ...  
  4.   
  5.     @Override  
  6.     protected void onStart() {  
  7.         super.onStart();  
  8.         mCamera = Camera.open();  
  9.     }  
  10.   
  11.   
  12.     @Override  
  13.     protected void onStop() {  
  14.         if (mCamera != null) {  
  15.             mCamera.release();  
  16.         }  
  17.         super.onStop();  
  18.     }  
  19.     ...  
  20.   
  21.     private void takePicture() {  
  22.         mCamera.takePicture(nullnullnull);  
  23.     }  
  24. }  
清单文件中添加使用Camera的权限
  1. <!--suppress DeprecatedClassUsageInspection -->  
  2. <uses-feature android:name="android.hardware.Camera" />  
  3.   
  4. <uses-permission android:name="android.permission.CAMERA" />  

       现在有点空虚了?不知道下一步该做什么了?好吧,那就先把回调填充上,shutter是要添加的,个人比较喜欢那种声音,jpeg也是需要的,我想把图片保存为jpeg格式,添加接口实现

  1. public class MainActivity extends AppCompatActivity implements Camera.ShutterCallback, Camera.PictureCallback {   
  2.     ...  
  3.     private void takePicture() {  
  4.         mCamera.takePicture(thisnullthis);  
  5.     }  
  6.       
  7.     @Override  
  8.     public void onPictureTaken(byte[] data, Camera camera) {  
  9.     }  
  10.   
  11.   
  12.     @Override  
  13.     public void onShutter() {  
  14.     }  
  15. }  

          接下来,该做什么?我们的PictureCallback回调给我们数据,但是数据源在哪里?没有数据源,当然无法回调数据,这时候要用到一个好东西了——SurfaceView,查看google的文档你会发现,Surface,SurfaceView和SurfaceHolder是桃园结义三兄弟,Surface负责图像的绘制,是一个很底层的类,所以我们一般不直接使用它,而是通过SurfaceHolder来间接操作Surface,SurfaceView则是一个视图类,是直接呈现给用户的视图,承载用户的操作以及呈现Surface上绘制的内容。我们需要做的就是,先在视图层添加SurfaceView再说。

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:id="@+id/activity_main"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:paddingBottom="@dimen/activity_vertical_margin"  
  8.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  9.     android:paddingRight="@dimen/activity_horizontal_margin"  
  10.     android:paddingTop="@dimen/activity_vertical_margin"  
  11.     tools:context="com.yjp.takepicture.MainActivity">  
  12.   
  13.     <SurfaceView  
  14.         android:id="@+id/surfaceView"  
  15.         android:layout_width="match_parent"  
  16.         android:layout_height="match_parent" />  
  17.   
  18.     <Button  
  19.         android:id="@+id/takePictureButton"  
  20.         android:layout_width="wrap_content"  
  21.         android:layout_height="wrap_content"  
  22.         android:layout_centerInParent="true"  
  23.         android:layout_alignBottom="@id/surfaceView"  
  24.         android:text="@string/takePictureButtonText"/>  
  25. </RelativeLayout>  

代码中

  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     setContentView(R.layout.activity_main);  
  5.   
  6.     SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);  
  7.       
  8.     Button takePictureButton = (Button) findViewById(R.id.takePictureButton);  
  9.     takePictureButton.setOnClickListener(new View.OnClickListener() {  
  10.         @Override  
  11.         public void onClick(View v) {  
  12.             takePicture();  
  13.         }  
  14.     });  
  15. }  

        现在项目已经可以运行了,但是屏幕黑黑的,点击拍照按钮没有反应,没关系,下面就是添加代码的部分了。

        先让我们的SurfaceView能够看到图像,只要我们清楚两点,图像的来源是Camera,而图像的呈现要靠SurfaceView,如何把两者关联起来才是关键,前面有提到,Surface是负责绘制的,我们只要能把Camera采集的数据送给Surface,就能显示出摄像头采集到的数据了, 而我们不直接操作Surface,需要借助于SurfaceHolder才能操作Surface,那么我们就需要借助于SurfaceHolder将Camera和SurfaceView关联起来。

  1. public class MainActivity extends AppCompatActivity  
  2.         implements Camera.ShutterCallback, Camera.PictureCallback, SurfaceHolder.Callback {  
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.   
  9.         //配置SurfaceView  
  10.         //setType使用外来数据源  
  11.         //设置SurfaceHolder.Callback  
  12.         SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);  
  13.         SurfaceHolder surfaceHolder = surfaceView.getHolder();  
  14.         surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
  15.         surfaceHolder.addCallback(this);  
  16.   
  17.         ...  
  18.     }  
  19.   
  20.     ...  
  21.   
  22.     @Override  
  23.     public void surfaceCreated(SurfaceHolder holder) {  
  24.         try {  
  25.             //surface创建成功能够拿到回调的holder  
  26.             //holder中包含有成功创建的Surface  
  27.             //从而交给摄像机预览使用  
  28.             mCamera.setPreviewDisplay(holder);  
  29.         } catch (IOException e) {  
  30.             e.printStackTrace();  
  31.         }  
  32.     }  
  33.   
  34.     @Override  
  35.     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  36.         //surface的尺寸发生变化  
  37.         //配置预览参数,如分辨率等  
  38.         //这里使用的分辨率简单选取了支持的预览分辨率的第一项  
  39.         //网上可以查找对应的优选算法  
  40.         Camera.Parameters parameters = mCamera.getParameters();  
  41.         List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();  
  42.         Camera.Size selected = sizes.get(0);  
  43.         parameters.setPreviewSize(selected.width, selected.height);  
  44.         parameters.setPictureSize(selected.width, selected.height);  
  45.   
  46.         //给摄像机设置参数,开始预览  
  47.         mCamera.setParameters(parameters);  
  48.         mCamera.startPreview();  
  49.     }  
  50.   
  51.     @Override  
  52.     public void surfaceDestroyed(SurfaceHolder holder) {  
  53.     }  
  54. }  
运行程序,可以实现预览。接下来只剩下拍照了,只要实现前面提到的PictureCallback jpeg的onPictureTaken即可。

  1. @Override  
  2. public void onPictureTaken(byte[] data, Camera camera) {  
  3.     try {  
  4.         FileOutputStream out;  
  5.         String filename = new Date().getTime() + ".jpg";  
  6.         String filePathname = Environment.getExternalStorageDirectory() + "/"  
  7.                 + Environment.DIRECTORY_PICTURES + "/" + filename;  
  8.         out = new FileOutputStream(filePathname);  
  9.         out.write(data);  
  10.         out.flush();  
  11.         out.close();  
  12.   
  13.         //重新启动预览  
  14.         mCamera.startPreview();  
  15.     } catch (FileNotFoundException e) {  
  16.         e.printStackTrace();  
  17.     } catch (IOException e) {  
  18.         e.printStackTrace();  
  19.     }  
  20. }  
拍照完成时,相机会停止预览,记得恢复预览。
由于需要写入文件,在清单文件中添加存储访问权限

  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
  2. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>  
运行程序,点击拍照按钮,现在就会在对应的目录下生成照片了。
      最后,修改MainActivity横屏无标题栏

  1. <activity  
  2.     android:name=".MainActivity"  
  3.     android:screenOrientation="landscape"  
  4.     android:theme="@style/Theme.AppCompat.Light.NoActionBar">  
  5.     <intent-filter>  
  6.         <action android:name="android.intent.action.MAIN" />  
  7.   
  8.         <category android:name="android.intent.category.LAUNCHER" />  
  9.     </intent-filter>  
  10. </activity>  
至此,完成拍照功能。
       总结一下,拍照使用了Camera和SurfaceView,从而间接使用了SurfaceHolder和Surface,实现了Camera.ShutterCallback, Camera.PictureCallback, SurfaceHolder.Callback 三个接口,合理配置相机的接口调用顺序即可。
       下一篇文章将基于当前版本做一些重构,然后实现自动拍照和定时连拍。
源码已上传到 这里
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值