安卓开发——相机:拍照并处理图片
2016-02-01 12:03
267 查看
本次使用的是Camera API来实现的,并不是使用隐式Intent与照相机进行交互的。所以我们使用SurfaceView类和相机硬件来实现实时展示拍照界面,以及拍照后的图片处理。
相机是一种独占性资源:也就是说一次只能有一个activity可以调用相机。因此,我们在使用相机硬件资源时需要时刻注意,使用完资源后要释放资源!!!
SurfaceView实例可以用来充当相机的取景器。SurfaceView是一种特殊的视图,可直接将想要显示的内容渲染输出到设备上(也可以用来播放视频)。
PS:屏幕显示方向可以在activity中的android:screenOrientation标签定义:
1. landscape : 横屏
2. portrait : 竖屏
其中Camera操作主要包括以下三个方法:
1. public static Camera open(int);
2. public static Camera open();
3. public final void release();
open()可以打开一个Camera资源,含参数的open方法则是在API9以后加入的(推荐使用这个)。release()方法则用于释放获取的相机资源。一般情况下只有当activity视图为在前台可见时才需要使用相机,所以我们考虑在onResume()方法中申请使用相机,而当视图离开前台时,即在onPause()方法中释放获取的相机资源。
Surface:可以看成是原始像素数据的缓冲区。
SurfaceHolder:是和Surface对象连接的重要纽带。(可以理解为通过SurfaceHolder给Surface对象设置像素数据)。
Surface对象是有生命周期的,即当SurfaceView在前台显示时会生成Surface对象,当SurfaceView消失时,Surface对象也会被销毁。因为当Surface不存在时我们需要保证Surface中没有需要绘制的对象。所以我们可以在Surface创建后把相机连接到SurfaceHolder上,当Surface被销毁后,我们把SurfaceHolder和相机的连接取消。为了实现这个我们需要使用SurfaceView.Callback接口中的三个方法。
public final void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg);
ShutterCallback回调方法在相机捕获图片时调用。此时图像数据还没有完成。
PictureCallback raw,原始数据可用时调用的回调函数。
PicryreCallback jpeg,在jpeg版本的图像可用时调用的回调函数。
下面就是两个接口ShutterCallback,PictureCallbak的实现:
拍照功能的实现是在拍照按钮的监听器事件中触发的:
由于现在的图片的尺寸都是比较大的,一般我们在显示图片之前都会对图片进行处理。接下来就是一个类来适当的缩放图片尺寸来适合在ImageView中的显示。
然后就是把处理后的图片显示到ImageView上。
相机是一种独占性资源:也就是说一次只能有一个activity可以调用相机。因此,我们在使用相机硬件资源时需要时刻注意,使用完资源后要释放资源!!!
SurfaceView实例可以用来充当相机的取景器。SurfaceView是一种特殊的视图,可直接将想要显示的内容渲染输出到设备上(也可以用来播放视频)。
拍照
获取权限
要使用相机,首先我们就要获取相关的权限:(AndroidMenifest.xml)[code]<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.FLASHLIGHT"/> <uses-feature android:name="android.hardware.Camera"/> <uses-feature android:name="android.hardware.camera.autofocus" />
PS:屏幕显示方向可以在activity中的android:screenOrientation标签定义:
1. landscape : 横屏
2. portrait : 竖屏
[code]<activity android:name=".CameraActivity" android:screenOrientation="landscape"> </activity>
Camera API
这里使用的是android.hardware.Camera包中的Camera类来打开相机的。其中Camera操作主要包括以下三个方法:
1. public static Camera open(int);
2. public static Camera open();
3. public final void release();
open()可以打开一个Camera资源,含参数的open方法则是在API9以后加入的(推荐使用这个)。release()方法则用于释放获取的相机资源。一般情况下只有当activity视图为在前台可见时才需要使用相机,所以我们考虑在onResume()方法中申请使用相机,而当视图离开前台时,即在onPause()方法中释放获取的相机资源。
[code] @Override public void onResume() { super.onResume(); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) mCamera = Camera.open(0); else mCamera = Camera.open(); } @Override public void onPause() { super.onPause(); if(mCamera != null){ mCamera.release(); mCamera = null; } }
SurfaceView实现取景器
SurfaceView:是我们用来显示图像的视图组件。Surface:可以看成是原始像素数据的缓冲区。
SurfaceHolder:是和Surface对象连接的重要纽带。(可以理解为通过SurfaceHolder给Surface对象设置像素数据)。
Surface对象是有生命周期的,即当SurfaceView在前台显示时会生成Surface对象,当SurfaceView消失时,Surface对象也会被销毁。因为当Surface不存在时我们需要保证Surface中没有需要绘制的对象。所以我们可以在Surface创建后把相机连接到SurfaceHolder上,当Surface被销毁后,我们把SurfaceHolder和相机的连接取消。为了实现这个我们需要使用SurfaceView.Callback接口中的三个方法。
[code] mSurfaceView = (SurfaceView) v.findViewById(R.id.camera_surfaceView); SurfaceHolder holder = mSurfaceView.getHolder(); //holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.addCallback(new SurfaceHolder.Callback() { /* 当SurfaceView创建时候调用 */ @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { /* 设置当前surfaceview为相机的显示区域 */ try { if (mCamera != null) mCamera.setPreviewDisplay(surfaceHolder); } catch (IOException e) { Log.e(TAG_CAMERA, "surfaceView fail..."); e.printStackTrace(); } } /* 首次显示到屏幕是调用的方法 */ @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { if(mCamera == null) return; try { /* 开始绘制帧 */ mCamera.startPreview(); } catch (Exception e) { Log.e(TAG_CAMERA, "camera start preview fail..."); mCamera.release(); mCamera = null; e.printStackTrace(); } } /* 当SurfaceView从屏幕上移除时 */ @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { /* 设置相机不再在这个域显示 */ if (mCamera != null) /* 停止绘制帧 */ mCamera.stopPreview(); } });
实现相机的回调方法
捕获图像,并存为JPEG图片。public final void takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg);
ShutterCallback回调方法在相机捕获图片时调用。此时图像数据还没有完成。
PictureCallback raw,原始数据可用时调用的回调函数。
PicryreCallback jpeg,在jpeg版本的图像可用时调用的回调函数。
下面就是两个接口ShutterCallback,PictureCallbak的实现:
[code] private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() { @Override public void onShutter() { mProgressBarContainer.setVisibility(View.VISIBLE); } }; private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] bytes, Camera camera) { /* 随机生成一个文件名 */ String fileName = UUID.randomUUID().toString() + ".jpg"; /* 创建一个文件输出流,生成jpeg文件 */ FileOutputStream os = null; boolean success = true; try { os = getActivity().openFileOutput(fileName, Context.MODE_PRIVATE); os.write(bytes); } catch (IOException e) { success = false; e.printStackTrace(); } finally { try { if (os != null) os.close(); } catch (IOException e) { success = false; e.printStackTrace(); } } if(success) { //Log.d(TAG_CAMERA, "save picture " + fileName + " success."); /* 返回生成的文件名给父Activity */ Intent i = new Intent(); i.putExtra(EXTRA_PHOTO, fileName); getActivity().setResult(Activity.RESULT_OK, i); } else getActivity().setResult(Activity.RESULT_CANCELED); /* 关闭拍照界面 */ getActivity().finish(); } };
拍照功能的实现是在拍照按钮的监听器事件中触发的:
[code] mTakePhoto = (Button) v.findViewById(R.id.camera_take_photo); mTakePhoto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mCamera != null) /* 这里只处理生成jpeg图像后的事件 */ mCamera.takePicture(mShutterCallback, null, mJpegCallback); } });
图片处理与显示
图片显示可以使用ImageView视图:[code]<ImageView android:id="@+id/imageView" android:layout_width="80dp" android:layout_height="80dp" android:scaleType="centerInside" android:cropToPadding="true"/>
由于现在的图片的尺寸都是比较大的,一般我们在显示图片之前都会对图片进行处理。接下来就是一个类来适当的缩放图片尺寸来适合在ImageView中的显示。
[code] import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.view.Display; import android.widget.ImageView; public class PictureUtils { /* 获取适当尺寸的图片大小,path为图片文字路径 */ public static BitmapDrawable getScaledDrawble(Activity a, String path){ Display display = a.getWindowManager().getDefaultDisplay(); float desWidth = display.getWidth(); float desHeight = display.getHeight(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); float srcWidth = options.outWidth; float srcHeight = options.outHeight; int scaleSize = 1; if(srcWidth > desWidth || srcHeight > desHeight){ if(srcWidth > srcHeight) scaleSize = Math.round(srcHeight/desHeight); else scaleSize = Math.round(srcWidth/desWidth); } options = new BitmapFactory.Options(); options.inSampleSize = scaleSize; Bitmap bitmap = BitmapFactory.decodeFile(path, options); return new BitmapDrawable(a.getResources(), bitmap); } /* 回收ImageView对象中图像数据 */ public static void cleanImageView(ImageView imageView){ if(!(imageView.getDrawable() instanceof BitmapDrawable)) return; BitmapDrawable b = (BitmapDrawable) imageView.getDrawable(); b.getBitmap().recycle(); imageView.setImageDrawable(null); } }
然后就是把处理后的图片显示到ImageView上。
[code] public void showPhoto(){ String path = ...; BitmapDrawable b = PictureUtils.getScaledDrawble(getActivity(), path); mImageView.setImageDrawable(b); }
相关文章推荐
- 华为机试题:向升序单向链表中插入一个节点
- BLE-NRF51822教程10-手机动态修改设备名
- js 验证码 倒计时60秒
- 安卓开发——存储与加载本地文件
- [Spring实战系列](4)Spring IOC容器
- Objective-C语法之动态类型(isKindOfClass, isMemberOfClass,id)等
- BLE-NRF51822教程9—动态密码(配对码)
- Android获取到wifi的信息
- linux内核基础(系统调用,简明)
- hdu 1016 Prime Ring Problem(深度优先搜索)
- javascript实现右侧弹出“分享到”窗口效果
- 8步拥有免费QQ域名邮箱
- 【JavaSE】JavaSE基础知识巡回
- BLE-NRF51822教程8-动态广播
- 文本分析之数据收集
- QTableWidget 导出到表格
- 安卓开发——浮动上下文选项与上下文菜单栏的实现
- Gradle实现的两种简单的多渠道打包方法
- VM虚拟机下CentOS 6.5配置IP地址的三种方法
- 实例详解IOS开发之UIWebView