1、创建一个textureView基础类
public class AutoTexturePreviewView extends FrameLayout {
public TextureView textureView;
private int videoWidth = 0;
private int videoHeight = 0;
public static int previewWidth = 0;
private int previewHeight = 0;
private static int scale = 2;
public static float circleRadius;
public static float circleX;
public static float circleY;
public AutoTexturePreviewView(Context context) {
super(context);
init();
}
public AutoTexturePreviewView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AutoTexturePreviewView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private Handler handler = new Handler(Looper.getMainLooper());
private void init() {
setWillNotDraw(false);
textureView = new TextureView(getContext());
addView(textureView);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
previewWidth = getWidth();
previewHeight = getHeight();
if (videoWidth == 0 || videoHeight == 0 || previewWidth == 0 || previewHeight == 0) {
return;
}
if (previewWidth * videoHeight > previewHeight * videoWidth) {
int scaledChildHeight = videoHeight * previewWidth / videoWidth;
textureView.layout(0, (previewHeight - scaledChildHeight) / scale,
previewWidth, (previewHeight + scaledChildHeight) / scale);
} else {
int scaledChildWidth = videoWidth * previewHeight / videoHeight;
textureView.layout((previewWidth - scaledChildWidth) / scale, 0,
(previewWidth + scaledChildWidth) / scale, previewHeight);
}
}
public TextureView getTextureView() {
return textureView;
}
public void setPreviewSize(int width, int height) {
if (this.videoWidth == width && this.videoHeight == height) {
return;
}
this.videoWidth = width;
this.videoHeight = height;
handler.post(new Runnable() {
@Override
public void run() {
requestLayout();
}
});
}
//画一个圆框,用于放置人脸
@Override
protected void onDraw(Canvas canvas) {
Path path = new Path();
// 设置裁剪的圆心坐标,半径
path.addCircle(getWidth() / 2, getHeight() / 2 - 70, getWidth() / 2, Path.Direction.CCW);
// 裁剪画布,并设置其填充方式
// canvas.clipPath(path, Region.Op.REPLACE);
if (Build.VERSION.SDK_INT >= 28) {
canvas.clipPath(path);
} else {
canvas.clipPath(path, Region.Op.REPLACE);
}
// 圆的半径
circleRadius = getWidth() / 2;
// 圆心的X坐标
circleX = (getRight() - getLeft()) / 2;
// 圆心的Y坐标
circleY = (getBottom() - getTop()) / 2 - 70;
super.onDraw(canvas);
}
}
2、在xml中使用这个布局
3、camera设置
public class CameraPreviewManager implements TextureView.SurfaceTextureListener {
AutoTexturePreviewView mTextureView;
private SurfaceTexture mSurfaceTexture;
private int previewWidth;
private int previewHeight;
private int videoWidth;
private int videoHeight;
private Camera mCamera;
private CameraDataCallback mCameraDataCallback;
private static volatile CameraPreviewManager instance = null;
public static CameraPreviewManager getInstance() {
synchronized (CameraPreviewManager.class) {
if (instance == null) {
instance = new CameraPreviewManager();
}
}
return instance;
}
/**
* 开启预览
*
* @param context
* @param textureView
*/
public void startPreview(Context context, AutoTexturePreviewView textureView, int width,int height, CameraDataCallback cameraDataCallback) {
Log.e(TAG, "开启预览模式");
Context mContext = context;
this.mCameraDataCallback = cameraDataCallback;
mTextureView = textureView;
this.previewWidth = width;
this.previewHeight = height;
mSurfaceTexture = mTextureView.getTextureView().getSurfaceTexture();
mTextureView.getTextureView().setSurfaceTextureListener(this);
}
/**
* 拍照
*/
public void take() {
if (mCamera != null) {
mCamera.takePicture(new Camera.ShutterCallback() {
@Override
public void onShutter() {
}
}, null, mPictureCallback);
}
}
Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
if (mCamera != null) {
mCamera.stopPreview();
new FileSaver(data).save();
}
}
};
private class FileSaver implements Runnable {
private byte[] buffer;
public FileSaver(byte[] buffer) {
this.buffer = buffer;
}
public void save() {
new Thread(this).start();
}
@Override
public void run() {
try {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "zhangphil.png");
file.createNewFile();
FileOutputStream os = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(os);
Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
Bitmap bitmap1 = rotateMyBitmap(bitmap);
bitmap1.compress(Bitmap.CompressFormat.PNG, 100, bos);
bos.flush();
bos.close();
os.close();
Log.d("照片已保存", file.getAbsolutePath());
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//保存后查看图片发生了旋转,需要旋转后再保存
private Bitmap rotateMyBitmap(Bitmap bmp) {
Matrix matrix = new Matrix();
matrix.postRotate(-90);
Bitmap rotatedBitMap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
return rotatedBitMap;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int i, int i1) {
Log.e(TAG, "--surfaceTexture--SurfaceTextureAvailable");
mSurfaceTexture = texture;
mSurfaceCreated = true;
textureWidth = i;
textureHeight = i1;
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int i, int i1) {
Log.e(TAG, "--surfaceTexture--TextureSizeChanged");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
Log.e(TAG, "--surfaceTexture--destroyed");
mSurfaceCreated = false;
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
// Log.e(TAG, "--surfaceTexture--Updated");
}
/**
* 关闭预览
*/
public void stopPreview() {
if (mCamera != null) {
try {
mCamera.setPreviewTexture(null);
mSurfaceCreated = false;
mTextureView = null;
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
} catch (Exception e) {
Log.e("qing", "camera destory error");
e.printStackTrace();
}
}
}
/**
* 开启摄像头
*/
public void openCamera() {
try {
if (mCamera == null) {
cameraId = cameraFacing;
mCamera = Camera.open(cameraId);
Log.e(TAG, "initCamera---open camera");
}
// 摄像头图像预览角度
int cameraRotation = 90;
switch (cameraFacing) {
case CAMERA_FACING_FRONT: {
mCamera.setDisplayOrientation(cameraRotation);
break;
}
case CAMERA_FACING_BACK: {
mCamera.setDisplayOrientation(cameraRotation);
break;
}
case CAMERA_USB: {
mCamera.setDisplayOrientation(cameraRotation);
break;
}
default:
break;
}
int isRgbRevert = 0;
if (isRgbRevert == 1) {
mTextureView.setRotationY(180);
} else {
mTextureView.setRotationY(0);
}
// 旋转90度或者270,需要调整宽高
mTextureView.setPreviewSize(previewHeight, previewWidth);
Camera.Parameters params = mCamera.getParameters();
List sizeList = params.getSupportedPreviewSizes(); // 获取所有支持的camera尺寸
final Camera.Size optionSize = getOptimalPreviewSize(sizeList, previewWidth,
previewHeight); // 获取一个最为适配的camera.size
if (optionSize.width == previewWidth && optionSize.height == previewHeight) {
videoWidth = previewWidth;
videoHeight = previewHeight;
} else {
videoWidth = optionSize.width;
videoHeight = optionSize.height;
}
params.setPreviewSize(videoWidth, videoHeight);
mCamera.setParameters(params);
try {
mCamera.setPreviewTexture(mSurfaceTexture);
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] bytes, Camera camera) {
if (mCameraDataCallback != null) {
mCameraDataCallback.onGetCameraData(bytes, camera,
videoWidth, videoHeight);
}
}
});
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
}
} catch (RuntimeException e) {
Log.e(TAG, e.getMessage());
}
}
/**
* 解决预览变形问题
*
* @param sizes
* @param w
* @param h
* @return
*/
private Camera.Size getOptimalPreviewSize(List sizes, int w, int h) {
final double aspectTolerance = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) {
return null;
}
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > aspectTolerance) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
4、使用定义的camera
class CameraFragment: BaseFragment(), FetchCallback {
private var mAutoCameraPreviewView: AutoTexturePreviewView? = null
private var imageData: ByteArray? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_camera, container, false).apply {
mAutoCameraPreviewView = findViewById(R.id.auto_camera_preview_view)
}
}
private fun startTestOpenDebugRegisterFunction() {
CameraPreviewManager.getInstance().startPreview(context,
mAutoCameraPreviewView,
640,
480,
object : CameraDataCallback {
override fun onGetCameraData(
data: ByteArray?,
camera: android.hardware.Camera?,
width: Int,
height: Int
) {
imageData = data
}
})
}
override fun onResume() {
super.onResume()
//只能在这个生命周期调用,不然会报错,找不到id
startTestOpenDebugRegisterFunction()
}
//在不保存图片的相框下,直接展示需要进行Yuv转码显示,不然图片不能显示出来
fun getImgBase64(buffer: ByteArray): String {
val yuvimage = YuvImage(buffer, ImageFormat.NV21, 640, 480, null) //20、20分别是图的宽度与高度
val baos = ByteArrayOutputStream()
yuvimage.compressToJpeg(Rect(0, 0, 640, 480), 100, baos)//80--JPG图片的质量[0-100],100最高
val jdata = baos.toByteArray()
val bitmap = BitmapFactory.decodeByteArray(jdata, 0, jdata.size)
val matrix = Matrix()
matrix.postRotate(90f)
val bitmap1: Bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
val bos = ByteArrayOutputStream()
bitmap1.compress(Bitmap.CompressFormat.PNG, 100, bos)
val bb: ByteArray = bos.toByteArray()
return Base64.encodeToString(bb, Base64.NO_WRAP)
}
}