[置顶] android 自定义相机Camera2
2017-05-03 16:27
330 查看
转载请标明来自:http://blog.csdn.net/qq_38416326/article/details/71124946
上一篇文章我们已经运用Camera自定义了一个相机,今天我们就用Camera2自定义一个相机。Camera2是android5.0新增的api,Camera2与Camera差别比较大,采用了全新的模式,功能更加强大。今天这个例子就是Camera2拍照TextureView上面进行预览,把之前的SurfaceView代替了。
一、打开摄像头
1、TextureView设置监听
2、在监听中,可用状态时打开摄像头
3、打开摄像头(camera2采用的是CameraManager)
4、设置摄像头的一些特性
二、进行拍照预览
1、监听摄像头
2、在摄像头打开是进行画面的预览
3、进行预览的设置和处理
三、拍照
1、进行拍照的处理和设置
2、监听拍照结果(成功后恢复预览)
四、拍照的图片保存到本地相册(用的是ImageReader进行接收图片)
五、完整的代码
1、activity代码
2、xml代码
3.权限
六、相关资料
1、效果图
2、源码和apk下载
apk下载:http://download.csdn.net/detail/qq_38416326/9833646
源码下载:http://download.csdn.net/detail/qq_38416326/9833637
https://github.com/sunshanglei/OneSelfCamera
上一篇文章我们已经运用Camera自定义了一个相机,今天我们就用Camera2自定义一个相机。Camera2是android5.0新增的api,Camera2与Camera差别比较大,采用了全新的模式,功能更加强大。今天这个例子就是Camera2拍照TextureView上面进行预览,把之前的SurfaceView代替了。
一、打开摄像头
1、TextureView设置监听
//设置TextureView监听 tv.setSurfaceTextureListener(surfaceTextureListener);
2、在监听中,可用状态时打开摄像头
/**TextureView的监听*/ private TextureView.SurfaceTextureListener surfaceTextureListener= new TextureView.SurfaceTextureListener() { //可用 @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { MainActivity.this.width=width; MainActivity.this.height=height; openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } //释放 @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { stopCamera(); return true; } //更新 @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } };
3、打开摄像头(camera2采用的是CameraManager)
/**打开摄像头*/ private void openCamera() { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); //设置摄像头特性 setCameraCharacteristics(manager); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { //提示用户开户权限 String[] perms = {"android.permission.CAMERA"}; ActivityCompat.requestPermissions(MainActivity.this,perms, RESULT_CODE_CAMERA); }else { manager.openCamera(mCameraId, stateCallback, null); } } catch (CameraAccessException e){ e.printStackTrace(); } }
4、设置摄像头的一些特性
/**设置摄像头的参数*/ private void setCameraCharacteristics(CameraManager manager) { try { // 获取指定摄像头的特性 CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId); // 获取摄像头支持的配置属性 StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); // 获取摄像头支持的最大尺寸 Size largest = Collections.max( Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),new CompareSizesByArea()); // 创建一个ImageReader对象,用于获取摄像头的图像数据 imageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2); //设置获取图片的监听 imageReader.setOnImageAvailableListener(imageAvailableListener,null); // 获取最佳的预览尺寸 previewSize = chooseOptimalSize(map.getOutputSizes( SurfaceTexture.class), width, height, largest); } catch (CameraAccessException e) { e.printStackTrace(); } catch (NullPointerException e) { } } private static Size chooseOptimalSize(Size[] choices , int width, int height, Size aspectRatio) { // 收集摄像头支持的大过预览Surface的分辨率 List<Size> bigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { if (option.getHeight() == option.getWidth() * h / w && option.getWidth() >= width && option.getHeight() >= height) { bigEnough.add(option); } } // 如果找到多个预览尺寸,获取其中面积最小的 if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); } else { //没有合适的预览尺寸 return choices[0]; } } // 为Size定义一个比较器Comparator static class CompareSizesByArea implements Comparator<Size> { @Override public int compare(Size lhs, Size rhs) { // 强转为long保证不会发生溢出 return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } }
二、进行拍照预览
1、监听摄像头
manager.openCamera(mCameraId, stateCallback, null);
2、在摄像头打开是进行画面的预览
/**摄像头状态的监听*/ private CameraDevice.StateCallback stateCallback = new CameraDevice. StateCallback() { // 摄像头被打开时触发该方法 @Override public void onOpened(CameraDevice cameraDevice){ MainActivity.this.cameraDevice = cameraDevice; // 开始预览 takePreview(); } // 摄像头断开连接时触发该方法 @Override public void onDisconnected(CameraDevice cameraDevice) { MainActivity.this.cameraDevice.close(); MainActivity.this.cameraDevice = null; } // 打开摄像头出现错误时触发该方法 @Override public void onError(CameraDevice cameraDevice, int error) { cameraDevice.close(); } };
3、进行预览的设置和处理
/** * 开始预览 */ private void takePreview() { SurfaceTexture mSurfaceTexture = tv.getSurfaceTexture(); //设置TextureView的缓冲区大小 mSurfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); //获取Surface显示预览数据 Surface mSurface = new Surface(mSurfaceTexture); try { //创建预览请求 mCaptureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 设置自动对焦模式 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); //设置Surface作为预览数据的显示界面 mCaptureRequestBuilder.addTarget(mSurface); //创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行 cameraDevice.createCaptureSession(Arrays.asList(mSurface,imageReader.getSurface()),new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { //开始预览 mCaptureRequest = mCaptureRequestBuilder.build(); mPreviewSession = session; //设置反复捕获数据的请求,这样预览界面就会一直有数据显示 mPreviewSession.setRepeatingRequest(mCaptureRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } }
三、拍照
1、进行拍照的处理和设置
/**拍照*/ private void takePicture() { try { if (cameraDevice == null) { return; } // 创建拍照请求 captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 设置自动对焦模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 将imageReader的surface设为目标 captureRequestBuilder.addTarget(imageReader.getSurface()); // 获取设备方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根据设备方向计算设置照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION , ORIENTATIONS.get(rotation)); // 停止连续取景 mPreviewSession.stopRepeating(); //拍照 CaptureRequest captureRequest = captureRequestBuilder.build(); //设置拍照监听 mPreviewSession.capture(captureRequest,captureCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); }
2、监听拍照结果(成功后恢复预览)
/**监听拍照结果*/ private CameraCaptureSession.CaptureCallback captureCallback= new CameraCaptureSession.CaptureCallback() { // 拍照成功 @Override public void onCaptureCompleted(CameraCaptureSession session,CaptureRequest request,TotalCaptureResult result) { // 重设自动对焦模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); // 设置自动曝光模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); try { //重新进行预览 mPreviewSession.setRepeatingRequest(mCaptureRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) { super.onCaptureFailed(session, request, failure); } };
四、拍照的图片保存到本地相册(用的是ImageReader进行接收图片)
/**监听拍照的图片*/ private ImageReader.OnImageAvailableListener imageAvailableListener= new ImageReader.OnImageAvailableListener() { // 当照片数据可用时激发该方法 @Override public void onImageAvailable(ImageReader reader) { //先验证手机是否有sdcard String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { Toast.makeText(getApplicationContext(), "你的sd卡不可用。", Toast.LENGTH_SHORT).show(); return; } // 获取捕获的照片数据 Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); //手机拍照都是存到这个路径 String filePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/"; String picturePath = System.currentTimeMillis() + ".jpg"; File file = new File(filePath, picturePath); try { //存到本地相册 FileOutputStream fileOutputStream = new FileOutputStream(file); fileOutputStream.write(data); fileOutputStream.close(); //显示图片 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options); iv.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { image.close(); } } };
五、完整的代码
1、activity代码
package com.sunshanglei.camera.oneselfcamera;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* use 自定义相机Camera2
* author 孙尚磊
* create time 2017-4-25
*/
public class MainActivity extends AppCompatActivity{
private TextureView tv;
private Button btn;
private String mCameraId = "0";//摄像头id(通常0代表后置摄像头,1代表前置摄像头)
private final int RESULT_CODE_CAMERA=1;//判断是否有拍照权限的标识码
private CameraDevice cameraDevice;
private CameraCaptureSession mPreviewSession;
private CaptureRequest.Builder mCaptureRequestBuilder,captureRequestBuilder;
private CaptureRequest mCaptureRequest;
private ImageReader imageReader;
private int height=0,width=0;
private Size previewSize;
private ImageView iv;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static
{
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextureView) findViewById(R.id.tv);
btn = (Button) findViewById(R.id.btn);
iv= (ImageView) findViewById(R.id.iv);
//拍照
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
takePicture();
}
});
//设置TextureView监听 tv.setSurfaceTextureListener(surfaceTextureListener);
}
@Override
protected void onPause() {
super.onPause();
if(cameraDevice!=null) {
stopCamera();
}
}
@Override
protected void onResume() {
super.onResume();
startCamera();
}
/**TextureView的监听*/
private TextureView.SurfaceTextureListener surfaceTextureListener= new TextureView.SurfaceTextureListener() {
//可用
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
MainActivity.this.width=width;
MainActivity.this.height=height;
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
//释放
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
stopCamera();
return true;
}
//更新
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
/**打开摄像头*/ private void openCamera() { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); //设置摄像头特性 setCameraCharacteristics(manager); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { //提示用户开户权限 String[] perms = {"android.permission.CAMERA"}; ActivityCompat.requestPermissions(MainActivity.this,perms, RESULT_CODE_CAMERA); }else { manager.openCamera(mCameraId, stateCallback, null); } } catch (CameraAccessException e){ e.printStackTrace(); } }
/**设置摄像头的参数*/ private void setCameraCharacteristics(CameraManager manager) { try { // 获取指定摄像头的特性 CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraId); // 获取摄像头支持的配置属性 StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); // 获取摄像头支持的最大尺寸 Size largest = Collections.max( Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),new CompareSizesByArea()); // 创建一个ImageReader对象,用于获取摄像头的图像数据 imageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2); //设置获取图片的监听 imageReader.setOnImageAvailableListener(imageAvailableListener,null); // 获取最佳的预览尺寸 previewSize = chooseOptimalSize(map.getOutputSizes( SurfaceTexture.class), width, height, largest); } catch (CameraAccessException e) { e.printStackTrace(); } catch (NullPointerException e) { } } private static Size chooseOptimalSize(Size[] choices , int width, int height, Size aspectRatio) { // 收集摄像头支持的大过预览Surface的分辨率 List<Size> bigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { if (option.getHeight() == option.getWidth() * h / w && option.getWidth() >= width && option.getHeight() >= height) { bigEnough.add(option); } } // 如果找到多个预览尺寸,获取其中面积最小的 if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); } else { //没有合适的预览尺寸 return choices[0]; } } // 为Size定义一个比较器Comparator static class CompareSizesByArea implements Comparator<Size> { @Override public int compare(Size lhs, Size rhs) { // 强转为long保证不会发生溢出 return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } }
/**摄像头状态的监听*/ private CameraDevice.StateCallback stateCallback = new CameraDevice. StateCallback() { // 摄像头被打开时触发该方法 @Override public void onOpened(CameraDevice cameraDevice){ MainActivity.this.cameraDevice = cameraDevice; // 开始预览 takePreview(); } // 摄像头断开连接时触发该方法 @Override public void onDisconnected(CameraDevice cameraDevice) { MainActivity.this.cameraDevice.close(); MainActivity.this.cameraDevice = null; } // 打开摄像头出现错误时触发该方法 @Override public void onError(CameraDevice cameraDevice, int error) { cameraDevice.close(); } };
/** * 开始预览 */ private void takePreview() { SurfaceTexture mSurfaceTexture = tv.getSurfaceTexture(); //设置TextureView的缓冲区大小 mSurfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); //获取Surface显示预览数据 Surface mSurface = new Surface(mSurfaceTexture); try { //创建预览请求 mCaptureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 设置自动对焦模式 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); //设置Surface作为预览数据的显示界面 mCaptureRequestBuilder.addTarget(mSurface); //创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行 cameraDevice.createCaptureSession(Arrays.asList(mSurface,imageReader.getSurface()),new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { //开始预览 mCaptureRequest = mCaptureRequestBuilder.build(); mPreviewSession = session; //设置反复捕获数据的请求,这样预览界面就会一直有数据显示 mPreviewSession.setRepeatingRequest(mCaptureRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } }
/**拍照*/ private void takePicture() { try { if (cameraDevice == null) { return; } // 创建拍照请求 captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 设置自动对焦模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 将imageReader的surface设为目标 captureRequestBuilder.addTarget(imageReader.getSurface()); // 获取设备方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根据设备方向计算设置照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION , ORIENTATIONS.get(rotation)); // 停止连续取景 mPreviewSession.stopRepeating(); //拍照 CaptureRequest captureRequest = captureRequestBuilder.build(); //设置拍照监听 mPreviewSession.capture(captureRequest,captureCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); }
}
/**监听拍照结果*/ private CameraCaptureSession.CaptureCallback captureCallback= new CameraCaptureSession.CaptureCallback() { // 拍照成功 @Override public void onCaptureCompleted(CameraCaptureSession session,CaptureRequest request,TotalCaptureResult result) { // 重设自动对焦模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); // 设置自动曝光模式 captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); try { //重新进行预览 mPreviewSession.setRepeatingRequest(mCaptureRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) { super.onCaptureFailed(session, request, failure); } };
/**监听拍照的图片*/ private ImageReader.OnImageAvailableListener imageAvailableListener= new ImageReader.OnImageAvailableListener() { // 当照片数据可用时激发该方法 @Override public void onImageAvailable(ImageReader reader) { //先验证手机是否有sdcard String status = Environment.getExternalStorageState(); if (!status.equals(Environment.MEDIA_MOUNTED)) { Toast.makeText(getApplicationContext(), "你的sd卡不可用。", Toast.LENGTH_SHORT).show(); return; } // 获取捕获的照片数据 Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); //手机拍照都是存到这个路径 String filePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/"; String picturePath = System.currentTimeMillis() + ".jpg"; File file = new File(filePath, picturePath); try { //存到本地相册 FileOutputStream fileOutputStream = new FileOutputStream(file); fileOutputStream.write(data); fileOutputStream.close(); //显示图片 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options); iv.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { image.close(); } } };
@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
switch(permsRequestCode){
case RESULT_CODE_CAMERA:
boolean cameraAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
if(cameraAccepted){
//授权成功之后,调用系统相机进行拍照操作等
openCamera();
}else{
//用户授权拒绝之后,友情提示一下就可以了
Toast.makeText(MainActivity.this,"请开启应用拍照权限",Toast.LENGTH_SHORT).show();
}
break;
}
}
/**启动拍照*/
private void startCamera(){
if (tv.isAvailable()) {
if(cameraDevice==null) {
openCamera();
}
} else {
tv.setSurfaceTextureListener(surfaceTextureListener);
}
}
/**
* 停止拍照释放资源*/
private void stopCamera(){
if(cameraDevice!=null){
cameraDevice.close();
cameraDevice=null;
}
}
}
2、xml代码
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextureView android:id="@+id/tv" android:layout_above="@+id/ll_bottom" android:layout_width="match_parent" android:layout_height="match_parent" /> <RelativeLayout android:id="@+id/ll_bottom" android:layout_width="match_parent" android:layout_height="80dp" android:layout_alignParentBottom="true"> <ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="70dp" android:layout_centerVertical="true" android:layout_marginLeft="10dp" android:src="@mipmap/ic_launcher"/> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="10dp" android:text="拍照"/> </RelativeLayout> </RelativeLayout>
3.权限
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
六、相关资料
1、效果图
2、源码和apk下载
apk下载:http://download.csdn.net/detail/qq_38416326/9833646
源码下载:http://download.csdn.net/detail/qq_38416326/9833637
https://github.com/sunshanglei/OneSelfCamera
相关文章推荐
- Android 用 camera2 API 自定义相机
- Android使用Camera2打造自定义相机
- Android 用 camera2 API 自定义相机
- [置顶] Android仿最新微信自定义相机(长按拍摄,轻点拍照)
- [置顶] android自定义相机(带边框和按钮)
- Android Camera2 自定义相机
- [置顶] android 自定义相机Camera
- Android Camera2自定义相机
- Android 用 camera2 API 自定义相机
- 使用android.hardware.camera2打造新的自定义相机
- Android 用 camera2 API 自定义相机
- [置顶] Android 自定义相机黑屏
- Android多媒体开发 Pro Android Media 第二章 创建自定义相机应用 2
- Android 自定义相机Demo源码
- 【重头学习Android】关于自定义相机后照相旋转问题终极解决方案
- Android自定义相机的实现以及Camera框架分析
- android自定义View之自定义可置顶ScrollView,View滑动原理简析
- android自定义相机添加自定义水印
- Android关闭自定义相机拍照声音
- Android自定义相机,带边框截图