Android Camera2 拍照(二)——使用TextureView
原文:Android Camera2 拍照(二)——使用TextureView
上一篇博文简单介绍了使用Camera2 API拍摄照片,并使用SurfaceView作为预览界面。实际上,相对于SurfaceView, TextureView更适合用于视频和拍摄照片。SurfaceView也有它的使用场合,这将在另外一篇中阐述。本文将使用TextureView作为预览界面,再次向大家展示Camera2 API的简单应用。
1,定义TextureView作为预览界面
在布局文件中加入TextureView控件,然后实现其监听事件
textureView = (TextureView) findViewById(R.id.textureView);
然后我们可以在OnResume()方法中设置监听SurefaceTexture的事件
textureView.setSurfaceTextureListener(textureListener);
当SurefaceTexture准备好后会回调SurfaceTextureListener 的onSurfaceTextureAvailable()方法
private TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//当SurefaceTexture可用的时候,设置相机参数并打开相机
setupCamera(width, height);
openCamera();
} @Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
} @Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
};
2,设置相机参数
private void setupCamera(int width, int height) {
//获取摄像头的管理者CameraManager
CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
try {
//遍历所有摄像头
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//此处默认打开后置摄像头
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
continue;
//获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
assert map != null;
//根据TextureView的尺寸设置预览尺寸
mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
//获取相机支持的最大拍照尺寸
mCaptureSize = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth());
}
});
//此ImageReader用于拍照所需
setupImageReader();
mCameraId = cameraId;
break;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//选择sizeMap中大于并且最接近width和height的size
private Size getOptimalSize(Size[] sizeMap, int width, int height) {
List<Size> sizeList = new ArrayList<>();
for (Size option : sizeMap) {
if (width > height) {
if (option.getWidth() > width && option.getHeight() > height) {
sizeList.add(option);
}
} else {
if (option.getWidth() > height && option.getHeight() > width) {
sizeList.add(option);
}
}
}
if (sizeList.size() > 0) {
return Collections.min(sizeList, new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
}
});
}
return sizeMap[0];
}
private void setupImageReader() {
//2代表ImageReader中最多可以获取两帧图像流
mImageReader = ImageReader.newInstance(mCaptureSize.getWidth(), mCaptureSize.getHeight(),
ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
final Image image = reader.acquireNextImage();
mCameraHandler.post(new imageSaver(image));
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);//由缓冲区存入字节数组
final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap != null) {
ivShow.setImageBitmap(bitmap);
}
}
});
}
}, mCameraHandler);
}
public static class imageSaver implements Runnable { private Image mImage; public imageSaver(Image image) {
mImage = image;
} @Override
public void run() {
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.capacity()];
buffer.get(data);
String path = Environment.getExternalStorageDirectory() + "/DCIM/361camera/";
File mImageFile = new File(path);
if (!mImageFile.exists()) {
boolean ret = mImageFile.mkdirs();
assert (ret);
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String fileName = path + "IMG_" + timeStamp + ".jpg";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileName);
fos.write(data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
mImage.close();
}
}
}
3,开启相机
private void openCamera() {
mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
try {
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//申请WRITE_EXTERNAL_STORAGE权限
requestPermissions(new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_CODE);
//return;
} else {
mCameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
实现StateCallback 接口,当相机打开后会回调onOpened方法,在这个方法里面开启预览
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
startPreview();
} @Override
public void onDisconnected(CameraDevice camera) {
camera.close();
mCameraDevice = null;
} @Override
public void onError(CameraDevice camera, int error) {
camera.close();
mCameraDevice = null;
}
};
4,开启相机预览
private void startPreview() {
SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface previewSurface = new Surface(mSurfaceTexture);
try {
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestBuilder.addTarget(previewSurface);
mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
mCaptureRequest = mCaptureRequestBuilder.build();
mCameraCaptureSession = session;
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
} @Override
public void onConfigureFailed(CameraCaptureSession session) { }
}, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
5,实现PreviewCallback
首先创建一个ImageReader,并监听它的事件(见上面的代码setupImageReader())。然后开启预览之前,设置ImageReader为输出Surface(见上面setupCamera()的代码)。
6,拍照
public void takePicture() {
lockFocus();
} private void lockFocus() {
try {
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), mCaptureCallback, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
源代码地址:https://github.com/gengqifu/361Camera。欢迎顺手star一下~~~
Android Camera2 拍照(二)——使用TextureView的更多相关文章
- Android Camera2 拍照(三)——切换摄像头,延时拍摄和闪光模式
原文:Android Camera2 拍照(三)--切换摄像头,延时拍摄和闪光模式 一.切换摄像头 在前后摄像头之间切换,首先需要关闭之前打开的摄像头,关闭preview,之后重新打开新的摄像头,重新 ...
- Android Camera2 拍照(四)——对焦模式
原文:Android Camera2 拍照(四)--对焦模式 本篇将重点介绍使用Camera2 API进行手动对焦的设置,以及在手动对焦与自动对焦模式之间切换. 一.手动对焦响应事件 首先我们要实现点 ...
- Android Camera2拍照(一)——使用SurfaceView
原文:Android Camera2拍照(一)--使用SurfaceView Camera2 API简介 Android 从5.0(21)开始,引入了新的Camera API Camera2,原来的a ...
- Android Camera2 拍照入门学习
原文:Android Camera2 拍照入门学习 学习资料: 肾虚将军android camera2 详解说明 极客学院android.hardware.camera2 使用指南 Android 5 ...
- 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo
Google自Android4.0出了TextureView,为什么推出呢?就是为了弥补Surfaceview的不足,另外一方面也是为了平衡GlSurfaceView,当然这是本人揣度的.关于Text ...
- Android Camera2 预览,拍照,人脸检测并实时展现
https://www.jianshu.com/p/5414ba2b5508 背景 最近需要做一个人脸检测并实时预览的功能.就是边检测人脸,边在预览界面上框出来. 当然本人并不是专门做 ...
- Android Camera2采集摄像头原始数据并手动预览
Android Camera2采集摄像头原始数据并手动预览 最近研究了一下android摄像头开发相关的技术,也看了Google提供的Camera2Basic调用示例,以及网上一部分代码,但都是在Te ...
- Android : Camera2/HAL3 框架分析
一.Android O上的Treble机制: 在 Android O 中,系统启动时,会启动一个 CameraProvider 服务,它是从 cameraserver 进程中分离出来,作为一个独立进程 ...
- android camera2 Api(转载)
现在的手机一般都会提供相机功能,有些相机的镜头甚至支持1000万以上像素,有些甚至支持光学变焦,这些手机已经变成了专业数码相机.为了充分利用手机上的相机功能,Android应用可以控制拍照和录制视频. ...
随机推荐
- 【心情】codeforces涨分啦!
虽然只有10分. 第二次比赛!
- 【u010】银河英雄传说
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开 ...
- spring mvc controller接收请求值及controller之间跳转及传值
spring接收请求参数: 1,使用HttpServletRequest获取 @RequestMapping("/login.do") public String login(Ht ...
- hibernate基本配置与简单增删改查
ORM(Object Relation Mapping)是对象关系映射,是一个思想,它的作用是在关系数据库与对象之间做一个自动映射,将数据库中的表格映射到一个类,也就是持久化类,数据表中每行映射为对象 ...
- redis举例调用两种方式方式
在以下的代码演示样例中.将给出两种最为经常使用的Redis命令操作方式,既普通调用方式和基于管线的调用方式. 注:在阅读代码时请留意凝视. 1 #include <stdio.h> ...
- POJ 2418-Hardwood Species(map)
Hardwood Species Time Limit: 10000MS Memory Limit: 65536K Total Submissions: 18770 Accepted: 740 ...
- iOS 多线程的使用
iOS 多线程 先看一篇阮一峰写关于进程和线程的文章,快速了解线程的相关概念. 随着现在计算机硬件的发展,多核心.高频率的cpu越来越普及,为了充分发挥cpu的性能,在不通的环境下实现cpu的利用最大 ...
- UVA 1428 - Ping pong(树状数组)
UVA 1428 - Ping pong 题目链接 题意:给定一些人,从左到右,每一个人有一个技能值,如今要举办比赛,必须满足位置从左往右3个人.而且技能值从小到大或从大到小,问有几种举办形式 思路: ...
- 关于javascript中的深拷贝问题
一直在尝试为javascript找一个快捷可靠的对象深拷贝的方法,昨天突发奇想,把对象push到一个空数组里,然后对改数组通过concat()或slice()进行拷贝,然后取出数组的第一个元素复制给变 ...
- ios中 微信点击 某个元素 该元素会闪一下
-webkit-user-select: none;-webkit-tap-highlight-color: rgba(200,200,200,0);