自定义相机 触摸聚焦,添加可控层
来源:互联网 发布:ajax的跨域请求数据 编辑:程序博客网 时间:2024/04/30 14:53
今天碰到了一个需求,项目中调用系统相机拍摄时要给定一个拍摄位置范围,想了想,就决定使用camera+SurfaceView来实现。
第一步,权限
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" />
首先给出我的拍照布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:keepScreenOn="true" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <SurfaceView android:id="@+id/my_surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" > </SurfaceView> </LinearLayout> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:padding="40dp" android:src="@drawable/photo_flame" /> <ImageView android:id="@+id/take_photo" android:layout_width="80dp" android:layout_height="80dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="@drawable/states_btn_capture" android:clickable="true" /></RelativeLayout>
主要有一个用于展示的SurfaceView,一个白色的边框,一个点击拍照的按钮
我总体的思路是在需要拍照是点击按钮,以startActivityForResult的方式启动这个拍照Activity,拍摄成功后回传回去。
好了,开始今天的旅程
step1,在Actvity的onCreate()生命周期方法内完成各种初始化
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test_camera);startOrientationChangeListener(); // 启动设备方向监听器mTakePhoto = (ImageView) findViewById(R.id.take_photo);mSurfaceView = (SurfaceView) findViewById(R.id.my_surfaceView);holder = mSurfaceView.getHolder();holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //必须设置,不然会报错holder.addCallback(this); // 回调接口}这里面有个设备方向监听的方法,主要是为了监听当前屏幕在拍摄时是横屏还是竖屏,方便我们旋转角度去保存图片
private final void startOrientationChangeListener() {mOrEventListener = new OrientationEventListener(this) {@Overridepublic void onOrientationChanged(int rotation) {if (((rotation >= 0) && (rotation <= 45)) || (rotation >= 315)|| ((rotation >= 135) && (rotation <= 225))) {// portraitmCurrentOrientation = true;} else if (((rotation > 45) && (rotation < 135))|| ((rotation > 225) && (rotation < 315))) {// landscapemCurrentOrientation = false;}}};mOrEventListener.enable();}step2 重新SurfaceHolder.Callback 的三个方法,简单完成我们的拍照功能
surfaceCreated(SurfaceHolder holder)
public void surfaceCreated(SurfaceHolder holder) {try {if (null == mCamera) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {mCamera = Camera.open(0);} else {mCamera = Camera.open();// 开启相机,不能放在构造函数中,不然不会显示画面.}}mCamera.setDisplayOrientation(90);} catch (Exception e) {}}完成我们camera的初始化,这里需要判断版本,因为低版本没有前置摄像头,还需要将自己的view旋转90度
surfaceChanged(SurfaceHolder holder, int format, int width, int height)
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {if (mCamera != null) {Parameters parameters = mCamera.getParameters();// 获取mCamera的参数对象Size largestSize = getBestSupportedSize(parameters.getSupportedPreviewSizes());if (largestSize != null) {parameters.setPreviewSize(largestSize.width, largestSize.height);// 设置预览图片尺寸parameters.setPictureSize(largestSize.width, largestSize.height);// 设置输出图片尺寸if (parameters.getSupportedFocusModes().contains(parameters.FOCUS_MODE_AUTO)) {parameters.setFocusMode(parameters.FOCUS_MODE_AUTO);}parameters.setPictureFormat(PixelFormat.JPEG); // 图片格式parameters.setExposureCompensation(0); // 曝光率mCamera.setParameters(parameters);try {mCamera.setPreviewDisplay(holder);//连接预览mCamera.startPreview();//开始预览} catch (Exception e) {if (mCamera != null) {mCamera.release();mCamera = null;}}}}}当它变化时,给camera设置各种配置,连接并开始预览
surfaceDestroyed(SurfaceHolder holder)
public void surfaceDestroyed(SurfaceHolder holder) {synchronized (this) {try {if (mCamera != null) {mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.release();mCamera = null;}} catch (Exception e) {}}}当它销毁时,记得释放资源,否则可能会导致再次调用相机时发现相机被占用
到这里就基本把重点说大半了,想要实现我们的功能,我们就要点击拍照按钮,取得图片成功后将图片地址回传回去就ok了。
step3,给我们的拍摄按钮绑定点击时间
public void onClick(View v) {// 点击拍照switch (v.getId()) {case R.id.take_photo:mCamera.takePicture(mShutter, null, mJpeg);mTakePhoto.setClickable(false);break;default:break;}}这里我设置了点击一次后就不能再点击了,防止多次点击,还有如果在libiary里面使用的话,因为无法使用id,那时候我们可能需要将我们的点击写成内部类的形式
mCamera.takePicture(mShutter, null, mJpeg);有三个参数,第一个是图像数据处理还未完成时的回调函数,音视频数据处理完成后的回调函数 ,图像数据处理完成后的回调函数
private Camera.ShutterCallback mShutter = new Camera.ShutterCallback() {@Overridepublic void onShutter() {// 一般显示进度条}};
private Camera.PictureCallback mJpeg = new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {// 保存图片mFileName = UUID.randomUUID().toString() + ".jpg";FileOutputStream out = null;try {out = openFileOutput(mFileName, Context.MODE_PRIVATE);byte[] newData = null;if (mCurrentOrientation) {// 竖屏时,旋转图片再保存Bitmap oldBitmap = BitmapFactory.decodeByteArray(data, 0,data.length);Matrix matrix = new Matrix();matrix.setRotate(90);Bitmap newBitmap = Bitmap.createBitmap(oldBitmap, 0, 0,oldBitmap.getWidth(), oldBitmap.getHeight(),matrix, true);ByteArrayOutputStream baos = new ByteArrayOutputStream();newBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);newData = baos.toByteArray();out.write(newData);} else {out.write(data);}} catch (IOException e) {e.printStackTrace();} finally {try {if (out != null)out.close();} catch (IOException e) {e.printStackTrace();}}closeCamera();Intent intent = new Intent();intent.putExtra("url", mFileName);setResult(RESULT_OK, intent);finish();}};在图片处理完成的函数中,我们需要根据拍摄时的横竖屏情况来调整我们保持图片的角度。
成功后释放camera资源,回传图片地址,结束当前的界面
public void closeCamera() {synchronized (this) {try {if (mCamera != null) {mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.release();mCamera = null;}} catch (Exception e) {}}}与销毁时处理方式相似,主要就是释放资源
下面做些优化,android存在物理键盘,所以我们需要处理物理返回事件
public boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {closeCamera();finish();return true;}return super.onKeyDown(keyCode, event);}
到这里,基本上就结束了。
我们还可以加上聚焦的功能,你可以采用定时聚焦,这里我采用的是,触摸屏幕聚焦,就是给surfaceView增加触摸事件,在回调方法中去聚焦
mSurfaceView.setOnTouchListener(this);
@Overridepublic boolean onTouch(View v, MotionEvent event) {autoFocus();return false;}
public void autoFocus() {if (mCamera != null) {synchronized (mCamera) {try {if (mCamera.getParameters().getSupportedFocusModes() != null&& mCamera.getParameters().getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {mCamera.autoFocus(new AutoFocusCallback() {public void onAutoFocus(boolean success,Camera camera) {}});} else {Toast.makeText(this, "无对焦功能", Toast.LENGTH_LONG).show();}} catch (Exception e) {e.printStackTrace();mCamera.stopPreview();mCamera.startPreview();Toast.makeText(this, "对焦失败", Toast.LENGTH_SHORT).show();}}}}
这里你需要判断你的手机是否支持聚焦的功能。好了,就这样。
相机camera.setParameters(parameters)方法出现java.lang.RuntimeException: setParameters failed
这个错误是和调用相机摄像头相关的。产生这个错误的原因主要在于代码控制分辨率的显示和真机测试分辨率不一样
一:解决办法 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);Display display = wm.getDefaultDisplay(); Camera.Parameters parameters = camera.getParameters();// 得到摄像头的参数parameters.setPreviewSize(display.getWidth(),display.getHeight());注释掉这两句parameters.setPictureSize(display.getHeight(),display.getWidth());注释掉这两句二:有可能你的真机是属于定制机,或者深度开发过,对camera对了不少的改动。camera.setParameters(parameters);//导致不能使用这个方法了,注释掉这一行吧。
在保持图片时,可能会报OOM异常,
Bitmap oldBitmap = BitmapFactory.decodeByteArray(data, 0,data.length);这句的问题,优化方法
public Bitmap byteToBitmap(byte[] imgByte) { InputStream input = null; Bitmap bitmap = null; BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 8; input = new ByteArrayInputStream(imgByte); SoftReference softRef = new SoftReference(BitmapFactory.decodeStream( input, null, options)); bitmap = (Bitmap) softRef.get(); if (imgByte != null) { imgByte = null; } try { if (input != null) { input.close(); } } catch (IOException e) { e.printStackTrace(); } return bitmap; }
下面给上里面的链接
http://download.csdn.net/detail/rui_yi/9548251
http://download.csdn.net/detail/rui_yi/9548251
- 自定义相机 触摸聚焦,添加可控层
- android自定义相机、连续(自动)聚焦、点击(触摸)聚焦、变焦、拍照后自定义裁剪、旋转
- Android自定义相机定点聚焦
- 自定义Camera,闪光灯,相机切换,相机聚焦
- 自定义相机添加闪光灯设置
- 相机聚焦,相机截屏
- Android 自定义相机 切换相机 参考线(辅助线) 闪光灯 缩放 自动聚焦 Demo
- android自定义相机添加自定义水印
- Android 自定义相机开发(支持前置,后置摄像头,可以自动聚焦,保存和显示图片)
- Android camera2 判断相机功能是否可控
- Caffe添加自定义的层
- cocos2d-x添加触摸层阻止后端事件
- Caffe添加自定义层-自定义loss
- Unity3D Vuforia Android 相机聚焦模式设置
- lytro 光场相机 重聚焦
- vuforia unity实现相机自动聚焦
- android 自定义View ----- 类似黄油相机添加文字
- 基于SurfaceView封装相机,并添加自定义水印效果
- jquery datatables API
- Swagger框架学习分享
- keras学习笔记2——Keras模块概述
- Java加密技术(一)——BASE64与单向加密算法MD5&SHA&MAC
- javaScript内存优化
- 自定义相机 触摸聚焦,添加可控层
- 入职三月
- 简化的说一说Java中的垃圾回收机制
- 在java中启动sh脚本
- could not bind listening IPv4 socket错误
- [Hadoop]那些年踩过的Hadoop坑
- iOS形变之CATransform3D
- 软件设计——开始设计前的准备
- 排序算法-冒泡排序(入门级别)