转载:http://blog.csdn.net/yjp19871013/article/details/53431358
前段时间工作很忙,忙了也好,开始思考自己到底应该如何工作,如何生活。前段时间的项目主要围绕Android,MFC,Winform开展,每个项目基本上都进行过一轮架构调整,所以对这些方面都有了比较深刻的理解,最近闲下来了,有时间,把前段时间项目的东西做一些整理,方便查阅,也提供一个大家共同学习的平台。
首先从Android的开始吧,比较热门嘛,主要是使用了Android多媒体开发的各个部分(Camera,Surface,MediaPlayer,MediaRecorder,MediaCodec等),知识也算系统了,基本都用到了,所以考虑写成一个系列,先从基本的拍照走起。讲原理贴代码想必大家也受够了,不如换一种轻松的方式,中间融合上开发的流程结合重构,能够更好地掌握技能。跳过创建项目等等,直接开始创作。
Android拍照,就要使用相机Camera,核心的接口
- android.hardware.Camera.takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)
这里有一个小发现,如果将shutter设置为null,将不会发出拍照的“咔嚓”声,有兴趣的可以尝试一下。
现在知道了要使用的接口,还有许多准备工作要做,但是好不容易查到的接口该如何是好,先封装函数吧,留着用(相机记得要释放)
- public class MainActivity extends AppCompatActivity {
- private Camera mCamera;
- ...
- @Override
- protected void onStart() {
- super.onStart();
- mCamera = Camera.open();
- }
- @Override
- protected void onStop() {
- if (mCamera != null) {
- mCamera.release();
- }
- super.onStop();
- }
- ...
- private void takePicture() {
- mCamera.takePicture(null, null, null);
- }
- }
- <!--suppress DeprecatedClassUsageInspection -->
- <uses-feature android:name="android.hardware.Camera" />
- <uses-permission android:name="android.permission.CAMERA" />
现在有点空虚了?不知道下一步该做什么了?好吧,那就先把回调填充上,shutter是要添加的,个人比较喜欢那种声音,jpeg也是需要的,我想把图片保存为jpeg格式,添加接口实现
- public class MainActivity extends AppCompatActivity implements Camera.ShutterCallback, Camera.PictureCallback {
- ...
- private void takePicture() {
- mCamera.takePicture(this, null, this);
- }
- @Override
- public void onPictureTaken(byte[] data, Camera camera) {
- }
- @Override
- public void onShutter() {
- }
- }
接下来,该做什么?我们的PictureCallback回调给我们数据,但是数据源在哪里?没有数据源,当然无法回调数据,这时候要用到一个好东西了——SurfaceView,查看google的文档你会发现,Surface,SurfaceView和SurfaceHolder是桃园结义三兄弟,Surface负责图像的绘制,是一个很底层的类,所以我们一般不直接使用它,而是通过SurfaceHolder来间接操作Surface,SurfaceView则是一个视图类,是直接呈现给用户的视图,承载用户的操作以及呈现Surface上绘制的内容。我们需要做的就是,先在视图层添加SurfaceView再说。
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.yjp.takepicture.MainActivity">
- <SurfaceView
- android:id="@+id/surfaceView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <Button
- android:id="@+id/takePictureButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:layout_alignBottom="@id/surfaceView"
- android:text="@string/takePictureButtonText"/>
- </RelativeLayout>
代码中
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
- Button takePictureButton = (Button) findViewById(R.id.takePictureButton);
- takePictureButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- takePicture();
- }
- });
- }
现在项目已经可以运行了,但是屏幕黑黑的,点击拍照按钮没有反应,没关系,下面就是添加代码的部分了。
先让我们的SurfaceView能够看到图像,只要我们清楚两点,图像的来源是Camera,而图像的呈现要靠SurfaceView,如何把两者关联起来才是关键,前面有提到,Surface是负责绘制的,我们只要能把Camera采集的数据送给Surface,就能显示出摄像头采集到的数据了, 而我们不直接操作Surface,需要借助于SurfaceHolder才能操作Surface,那么我们就需要借助于SurfaceHolder将Camera和SurfaceView关联起来。
- public class MainActivity extends AppCompatActivity
- implements Camera.ShutterCallback, Camera.PictureCallback, SurfaceHolder.Callback {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- //配置SurfaceView
- //setType使用外来数据源
- //设置SurfaceHolder.Callback
- SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
- SurfaceHolder surfaceHolder = surfaceView.getHolder();
- surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- surfaceHolder.addCallback(this);
- ...
- }
- ...
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- try {
- //surface创建成功能够拿到回调的holder
- //holder中包含有成功创建的Surface
- //从而交给摄像机预览使用
- mCamera.setPreviewDisplay(holder);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- //surface的尺寸发生变化
- //配置预览参数,如分辨率等
- //这里使用的分辨率简单选取了支持的预览分辨率的第一项
- //网上可以查找对应的优选算法
- Camera.Parameters parameters = mCamera.getParameters();
- List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
- Camera.Size selected = sizes.get(0);
- parameters.setPreviewSize(selected.width, selected.height);
- parameters.setPictureSize(selected.width, selected.height);
- //给摄像机设置参数,开始预览
- mCamera.setParameters(parameters);
- mCamera.startPreview();
- }
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- }
- }
- @Override
- public void onPictureTaken(byte[] data, Camera camera) {
- try {
- FileOutputStream out;
- String filename = new Date().getTime() + ".jpg";
- String filePathname = Environment.getExternalStorageDirectory() + "/"
- + Environment.DIRECTORY_PICTURES + "/" + filename;
- out = new FileOutputStream(filePathname);
- out.write(data);
- out.flush();
- out.close();
- //重新启动预览
- mCamera.startPreview();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
由于需要写入文件,在清单文件中添加存储访问权限
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
最后,修改MainActivity横屏无标题栏
- <activity
- android:name=".MainActivity"
- android:screenOrientation="landscape"
- android:theme="@style/Theme.AppCompat.Light.NoActionBar">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
总结一下,拍照使用了Camera和SurfaceView,从而间接使用了SurfaceHolder和Surface,实现了Camera.ShutterCallback, Camera.PictureCallback, SurfaceHolder.Callback 三个接口,合理配置相机的接口调用顺序即可。
下一篇文章将基于当前版本做一些重构,然后实现自动拍照和定时连拍。
源码已上传到 这里