原文: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的更多相关文章

  1. Android Camera2 拍照(三)——切换摄像头,延时拍摄和闪光模式

    原文:Android Camera2 拍照(三)--切换摄像头,延时拍摄和闪光模式 一.切换摄像头 在前后摄像头之间切换,首先需要关闭之前打开的摄像头,关闭preview,之后重新打开新的摄像头,重新 ...

  2. Android Camera2 拍照(四)——对焦模式

    原文:Android Camera2 拍照(四)--对焦模式 本篇将重点介绍使用Camera2 API进行手动对焦的设置,以及在手动对焦与自动对焦模式之间切换. 一.手动对焦响应事件 首先我们要实现点 ...

  3. Android Camera2拍照(一)——使用SurfaceView

    原文:Android Camera2拍照(一)--使用SurfaceView Camera2 API简介 Android 从5.0(21)开始,引入了新的Camera API Camera2,原来的a ...

  4. Android Camera2 拍照入门学习

    原文:Android Camera2 拍照入门学习 学习资料: 肾虚将军android camera2 详解说明 极客学院android.hardware.camera2 使用指南 Android 5 ...

  5. 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo

    Google自Android4.0出了TextureView,为什么推出呢?就是为了弥补Surfaceview的不足,另外一方面也是为了平衡GlSurfaceView,当然这是本人揣度的.关于Text ...

  6. Android Camera2 预览,拍照,人脸检测并实时展现

    https://www.jianshu.com/p/5414ba2b5508 背景     最近需要做一个人脸检测并实时预览的功能.就是边检测人脸,边在预览界面上框出来.     当然本人并不是专门做 ...

  7. Android Camera2采集摄像头原始数据并手动预览

    Android Camera2采集摄像头原始数据并手动预览 最近研究了一下android摄像头开发相关的技术,也看了Google提供的Camera2Basic调用示例,以及网上一部分代码,但都是在Te ...

  8. Android : Camera2/HAL3 框架分析

    一.Android O上的Treble机制: 在 Android O 中,系统启动时,会启动一个 CameraProvider 服务,它是从 cameraserver 进程中分离出来,作为一个独立进程 ...

  9. android camera2 Api(转载)

    现在的手机一般都会提供相机功能,有些相机的镜头甚至支持1000万以上像素,有些甚至支持光学变焦,这些手机已经变成了专业数码相机.为了充分利用手机上的相机功能,Android应用可以控制拍照和录制视频. ...

随机推荐

  1. spring mvc 解决csrf跨站请求攻击

    http://www.dewen.net.cn/q/935/spring+mvc+%E8%A7%A3%E5%86%B3csrf%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82% ...

  2. freemarker自己定义标签报错(七)

    1.错误描写叙述 六月 09, 2014 11:11:09 下午 freemarker.log.JDK14LoggerFactory$JDK14Logger error 严重: Template pr ...

  3. linux下创建一个可执行脚本

    1.touch hello.sh   2.vim hello.sh   键入i   插入#!/bin/sh      echo hello world;  键入:      esc      :   ...

  4. centos / Linux 服务环境下安装 Redis 5.0.3

    原文:centos / Linux 服务环境下安装 Redis 5.0.3 1.首先进入你要安装的目录 cd /usr/local 2.下载目前最新稳定版本 Redis 5.0.3 wget http ...

  5. Mysql存储过程中使用cursor

    一.表 学生表 CREATE TABLE `t_student` (    `stuNum` int(11) NOT NULL auto_increment,    `stuName` varchar ...

  6. 毕设一:python 爬取苏宁的商品评论

    毕设需要大量的商品评论,网上找的数据比较旧了,自己动手 代理池用的proxypool,github:https://github.com/jhao104/proxy_pool ua:fake_user ...

  7. UUID不失精度,长度改进

    在使用到uuid的时候,往往头疼于它的长度(如1bfe50d8-544e-4e8a-95b8-199ceff15268),于是乎就有了改写uuid的各种方法 1.去除"-"的uui ...

  8. Unity3d 配置OpenCV(EmguCV) 周围环境

    最近毕业.我们一直在研究如何Unity正在使用EmguCV,最后,有些吃老本的今天. 我的环境:Unity3d 4.3.1f             libemgucv-windows-univers ...

  9. Xor - Trie树

    题目描述 求一棵带边权的树的一条最大 Xor 路径的值.这里的"路径"不一定从根到叶子结点,中间一段路径只要满足条件也可以. 输入格式 第一行,一个整数 N ,表示一颗树有 N 个 ...

  10. 【b603】作业调度方案

    Time Limit: 1 second Memory Limit: 50 MB [问题描述] 我们现在要利用m台机器加工n个工件,每个工件都有m道工序,每道工序都在不同的指定的机器上完成.每个工件的 ...