先看效果图:

GitHub Demo 地址 :https://github.com/liudeangit/CustomCamera
1.添加依赖
gradle.properties文件下加入android.enableJetifier=true可以混合支持库
android.enableJetifier=true
build.gradle下添加下载库
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.axs.camera"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
api 'com.github.RmondJone:SpringDiaLog:1.0.6'
api 'com.github.bumptech.glide:glide:3.7.0'
api 'com.yanzhenjie:permission:2.0.0-rc11'
}
AndroidManifest.xml下加入权限支持
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.axs.camera">
<!-- 增加网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 增加文件存储和访问摄像头的权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" /> <!-- 闪光灯权限 -->
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <!-- region 适配全面屏 -->
<meta-data
android:name="android.max_aspect"
android:value="2.4" /> <!-- 刘海屏适配 -->
<!--
由于目前市面上刘海屏各家一套,没有按照Android P官方进行适配,所以暂时不做刘海屏适配处理。
大部分厂家都已经对没有适配刘海屏的应用有自己的一套处理逻辑,我们的应用基本可以在大部分刘海屏手机上正常显示!
-->
<!-- 华为刘海区域展示 -->
<meta-data
android:name="android.notch_support"
android:value="true" /> <!-- 小米刘海区域展示 -->
<meta-data
android:name="notch.config"
android:value="portrait" />
<application
android:allowBackup="true"
android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Camera">
<activity android:name=".MainActivity3"></activity>
<activity android:name=".MainActivity2" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- <provider-->
<!-- android:name="androidx.core.content.FileProvider"-->
<!-- android:authorities="${applicationId}"-->
<!-- android:exported="false"-->
<!-- android:grantUriPermissions="true">-->
<!-- <meta-data-->
<!-- android:name="android.support.FILE_PROVIDER_PATHS"-->
<!-- android:resource="@xml/file_provider_paths"-->
<!-- tools:replace="android:resource" />-->
<!-- </provider>-->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.cameraalbumtests.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
</application>
</manifest>
res下新建xml包,包下面创建file_provider_paths文件
<?xml version ="1.0" encoding ="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path=""><!--path为空时表示将整个SD卡进行共享,这里会报错,不影响结果-->
</external-path>
</paths>
2.布局
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/gotoCamare"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="50dp"
android:onClick="gotoCamare"
android:text="点击拍照" />
</LinearLayout>
activity_main2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity2">
<FrameLayout
android:id="@+id/camera_preview_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!--顶部视图-->
<LinearLayout
android:id="@+id/up_layout"
android:layout_width="match_parent"
android:layout_height="80dp"
android:gravity="bottom"
android:orientation="horizontal"
android:padding="15dp">
<Button
android:id="@+id/cancle_button"
android:layout_width="90dp"
android:layout_height="60dp"
android:layout_centerVertical="true"
android:background="@null"
android:text="取消"
android:textColor="#0A0909"
android:textSize="20sp"/>
<ImageView
android:id="@+id/flash_button"
android:layout_width="50dp"
android:layout_marginLeft="200dp"
android:layout_height="40dp"
android:src="@mipmap/flash_close" />
</LinearLayout>
<!--底部拍照按钮-->
<RelativeLayout
android:id="@+id/ll_photo_layout"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:padding="15dp"
android:visibility="visible">
<ImageView
android:id="@+id/gallerys"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:src="@mipmap/photo" />
<ImageView
android:id="@+id/take_photo_button"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerInParent="true"
android:src="@mipmap/take_button" />
</RelativeLayout>
</RelativeLayout>
3.代码
MainActivity
package com.axs.camera;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import com.axs.camera.crop.view.CropImageView;
import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.Permission;
import java.io.File;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void gotoCamare(View view) {
PermissionUtils.applicationPermissions(MainActivity.this, new PermissionUtils.PermissionListener() {
@Override
public void onSuccess(Context context) {
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
MainActivity.this.startActivity(intent);
}
@Override
public void onFailed(Context context) {
if (AndPermission.hasAlwaysDeniedPermission(context, Permission.Group.CAMERA)
&& AndPermission.hasAlwaysDeniedPermission(context, Permission.Group.STORAGE)) {
AndPermission.with(context).runtime().setting().start();
}
Toast.makeText(context, context.getString(R.string.permission_camra_storage), Toast.LENGTH_SHORT);
}
}, Permission.Group.STORAGE, Permission.Group.CAMERA);
}
}
MainActivity2
package com.axs.camera;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.axs.camera.crop.view.CropImageView;
import com.newland.springdialog.AnimSpring;
import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.Permission;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class MainActivity2 extends AppCompatActivity implements View.OnClickListener{
public static final int CHOOSE_PHOTO = 2;
public static final String KEY_IMAGE_PATH = "imagePath";
private FrameLayout mPreviewLayout;
private RelativeLayout mPhotoLayout;
private LinearLayout upLayout;
private ImageView mFlashButton;
private ImageView mPhotoButton;
private OverCameraView mOverCameraView;
private Camera mCamera;
private Handler mHandler = new Handler();
private Runnable mRunnable;
private Button mCancleButton;
private boolean isFlashing;
private byte[] imageData;
private boolean isTakePhoto;
private boolean isFoucing;
private ImageView gallery;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
initView();
setOnclickListener();
}
private void initView() {
mCancleButton = findViewById(R.id.cancle_button);
mPreviewLayout = findViewById(R.id.camera_preview_layout);
mPhotoLayout = findViewById(R.id.ll_photo_layout);
mPhotoButton = findViewById(R.id.take_photo_button);
mFlashButton = findViewById(R.id.flash_button);
gallery = findViewById(R.id.gallerys);
upLayout = findViewById(R.id.up_layout);
}
@Override
protected void onResume() {
super.onResume();
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
CameraPreview preview = new CameraPreview(this, mCamera);
mOverCameraView = new OverCameraView(this);
mPreviewLayout.addView(preview);
mPreviewLayout.addView(mOverCameraView);
}
private void setOnclickListener() {
mCancleButton.setOnClickListener(this);
mFlashButton.setOnClickListener(this);
mPhotoButton.setOnClickListener(this);
gallery.setOnClickListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (!isFoucing) {
float x = event.getX();
float y = event.getY();
isFoucing = true;
if (mCamera != null && !isTakePhoto) {
mOverCameraView.setTouchFoucusRect(mCamera, autoFocusCallback, x, y);
}
mRunnable = () -> {
Toast.makeText(MainActivity2.this, "自动聚焦超时,请调整合适的位置拍摄!", Toast.LENGTH_SHORT);
isFoucing = false;
mOverCameraView.setFoucuing(false);
mOverCameraView.disDrawTouchFocusRect();
};
mHandler.postDelayed(mRunnable, 3000);
}
}
return super.onTouchEvent(event);
}
private Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
isFoucing = false;
mOverCameraView.setFoucuing(false);
mOverCameraView.disDrawTouchFocusRect();
mHandler.removeCallbacks(mRunnable);
}
};
private void takePhoto() {
isTakePhoto = true;
mCamera.takePicture(null, null, null, (data, camera1) -> {
upLayout.setVisibility(View.GONE);
mPhotoLayout.setVisibility(View.GONE);
imageData = data;
mCamera.stopPreview();
savePhoto();
});
}
private void switchFlash() {
isFlashing = !isFlashing;
mFlashButton.setImageResource(isFlashing ? R.mipmap.flash_open : R.mipmap.flash_close);
AnimSpring.getInstance(mFlashButton).startRotateAnim(120, 360);
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFlashMode(isFlashing ? Camera.Parameters.FLASH_MODE_TORCH : Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameters);
} catch (Exception e) {
Toast.makeText(this, "该设备不支持闪光灯", Toast.LENGTH_SHORT);
}
}
private void cancleSavePhoto() {
upLayout.setVisibility(View.VISIBLE);
mPhotoLayout.setVisibility(View.VISIBLE);
AnimSpring.getInstance(mPhotoLayout).startRotateAnim(120, 360);
mCamera.startPreview();
imageData = null;
isTakePhoto = false;
}
@Override
public void onClick(View v) {
int id = v.getId();
System.out.println(id);
if (id == R.id.cancle_button) {
finish();
} else if (id == R.id.take_photo_button) {
if (!isTakePhoto) {
takePhoto();
}
} else if (id == R.id.flash_button) {
switchFlash();
} else if (id == R.id.save_button) {
savePhoto();
} else if (id == R.id.cancle_save_button) {
cancleSavePhoto();
}else if(id == R.id.gallerys){
aa();
}
}
private void savePhoto() {
FileOutputStream fos = null;
String name = DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg";
FileOutputStream outputStream = null;
String printTxtPath = getApplicationContext().getFilesDir().getAbsolutePath();
File file = new File(printTxtPath);
if (!file.exists()) {
file.mkdirs();
}
String imagePath = printTxtPath + name;
File imageFile = new File(imagePath);
try {
fos = new FileOutputStream(imageFile);
fos.write(imageData);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
Bitmap retBitmap = BitmapFactory.decodeFile(imagePath);
retBitmap = BitmapUtils.setTakePicktrueOrientation(Camera.CameraInfo.CAMERA_FACING_BACK, retBitmap);
BitmapUtils.saveBitmap(retBitmap, imagePath);
Intent intent = new Intent(getApplicationContext(),MainActivity3.class);
intent.putExtra(KEY_IMAGE_PATH, imagePath);
startActivity(intent);
} catch (IOException e) {
setResult(RESULT_FIRST_USER);
e.printStackTrace();
}
}
finish();
}
}
public void aa(){
if (ContextCompat.checkSelfPermission(MainActivity2.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity2.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
openAlbum();
}
}
private void openAlbum() {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
openAlbum();
}else{
Toast.makeText(this,"You denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println("99999999999999999999999999999999999966666666666666");
if (resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= 19) {
handleImageOnKitKat(data);
} else {
handleImageBeforeKitKat(data);
}
}
}
public byte[] UriToByte(Uri uri){
Bitmap bitmap1 = null;
try {
bitmap1 = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
int size = bitmap1.getWidth() * bitmap1.getHeight() * 4;
ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
bitmap1.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] imagedata1 = baos.toByteArray();
return imagedata1;
}
@TargetApi(19)
private void handleImageOnKitKat(Intent data){
String imagePath = null;
Uri uri = data.getData();
if(DocumentsContract.isDocumentUri(this,uri)){
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())){
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId));
imagePath = getImagePath(contentUri,null);
}
}else if("content".equalsIgnoreCase(uri.getScheme())){
imagePath = getImagePath(uri,null);
}else if("file".equalsIgnoreCase(uri.getScheme())){
imagePath = uri.getPath();
}
displayImage(imagePath);
}
private void handleImageBeforeKitKat(Intent data){
Uri uri = data.getData();
String imagePath = getImagePath(uri,null);
displayImage(imagePath);
}
private String getImagePath(Uri uri,String selection){
String path = null;
Cursor cursor = getContentResolver().query(uri,null,selection,null,null);
if(cursor != null){
if (cursor.moveToFirst()){
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
private void displayImage(String imagePath) {
if (imagePath != null) {
Intent intent = new Intent(MainActivity2.this,MainActivity3.class);
intent.putExtra(KEY_IMAGE_PATH, imagePath);
startActivity(intent);
} else {
Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
}
}
}
工具类
package com.axs.camera;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.collection.SparseArrayCompat;
public class AspectRatio implements Comparable<AspectRatio>, Parcelable {
private final static SparseArrayCompat<SparseArrayCompat<AspectRatio>> sCache
= new SparseArrayCompat<>(16);
private final int mX;
private final int mY;
public static AspectRatio of(int x, int y) {
int gcd = gcd(x, y);
x /= gcd;
y /= gcd;
SparseArrayCompat<AspectRatio> arrayX = sCache.get(x);
if (arrayX == null) {
AspectRatio ratio = new AspectRatio(x, y);
arrayX = new SparseArrayCompat<>();
arrayX.put(y, ratio);
sCache.put(x, arrayX);
return ratio;
} else {
AspectRatio ratio = arrayX.get(y);
if (ratio == null) {
ratio = new AspectRatio(x, y);
arrayX.put(y, ratio);
}
return ratio;
}
}
public static AspectRatio parse(String s) {
int position = s.indexOf(':');
if (position == -1) {
throw new IllegalArgumentException("Malformed aspect ratio: " + s);
}
try {
int x = Integer.parseInt(s.substring(0, position));
int y = Integer.parseInt(s.substring(position + 1));
return AspectRatio.of(x, y);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Malformed aspect ratio: " + s, e);
}
}
private AspectRatio(int x, int y) {
mX = x;
mY = y;
}
public int getX() {
return mX;
}
public int getY() {
return mY;
}
public boolean matches(com.axs.camera.Size size) {
int gcd = gcd(size.getWidth(), size.getHeight());
int x = size.getWidth() / gcd;
int y = size.getHeight() / gcd;
return mX == x && mY == y;
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (o instanceof AspectRatio) {
AspectRatio ratio = (AspectRatio) o;
return mX == ratio.mX && mY == ratio.mY;
}
return false;
}
@Override
public String toString() {
return mX + ":" + mY;
}
public float toFloat() {
return (float) mX / mY;
}
@Override
public int hashCode() {
return mY ^ ((mX << (Integer.SIZE / 2)) | (mX >>> (Integer.SIZE / 2)));
}
@Override
public int compareTo(AspectRatio another) {
if (equals(another)) {
return 0;
} else if (toFloat() - another.toFloat() > 0) {
return 1;
}
return -1;
}
public AspectRatio inverse() {
return AspectRatio.of(mY, mX);
}
private static int gcd(int a, int b) {
while (b != 0) {
int c = b;
b = a % b;
a = c;
}
return a;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mX);
dest.writeInt(mY);
}
public static final Creator<AspectRatio> CREATOR
= new Creator<AspectRatio>() {
@Override
public AspectRatio createFromParcel(Parcel source) {
int x = source.readInt();
int y = source.readInt();
return AspectRatio.of(x, y);
}
@Override
public AspectRatio[] newArray(int size) {
return new AspectRatio[size];
}
};
}
package com.axs.camera;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.hardware.Camera;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class BitmapUtils {
public static Bitmap setTakePicktrueOrientation(int id, Bitmap bitmap) {
if (bitmap.getWidth() < bitmap.getHeight()) {
return bitmap;
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(id, info);
bitmap = rotaingImageView(id, info.orientation, bitmap);
return bitmap;
}
private static Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
if (id == 1) {
matrix.postScale(-1, 1);
}
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
public static boolean saveBitmap(Bitmap bitmap, String path) {
try {
File file = new File(path);
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
FileOutputStream fos = new FileOutputStream(file);
boolean b = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
return b;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
package com.axs.camera;
import android.app.Activity;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
import java.util.SortedSet;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private boolean isPreview;
private Context context;
private final com.axs.camera.SizeMap mPreviewSizes = new com.axs.camera.SizeMap();
private final com.axs.camera.SizeMap mPictureSizes = new com.axs.camera.SizeMap();
private int mDisplayOrientation;
private com.axs.camera.AspectRatio mAspectRatio;
public CameraPreview(Context context, Camera mCamera) {
super(context);
this.context = context;
this.mCamera = mCamera;
this.mHolder = getHolder();
this.mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mDisplayOrientation = ((Activity) context).getWindowManager().getDefaultDisplay().getRotation();
mAspectRatio = com.axs.camera.AspectRatio.of(9, 16);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mAspectRatio = getDeviceAspectRatio((Activity) context);
mCamera.setDisplayOrientation(getDisplayOrientation());
Camera.Parameters parameters = mCamera.getParameters();
mPreviewSizes.clear();
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
int width = Math.min(size.width, size.height);
int heigth = Math.max(size.width, size.height);
mPreviewSizes.add(new com.axs.camera.Size(width, heigth));
}
mPictureSizes.clear();
for (Camera.Size size : parameters.getSupportedPictureSizes()) {
int width = Math.min(size.width, size.height);
int heigth = Math.max(size.width, size.height);
mPictureSizes.add(new com.axs.camera.Size(width, heigth));
}
com.axs.camera.Size previewSize = chooseOptimalSize(mPreviewSizes.sizes(mAspectRatio));
com.axs.camera.Size pictureSize = mPictureSizes.sizes(mAspectRatio).last();
parameters.setPreviewSize(Math.max(previewSize.getWidth(), previewSize.getHeight()), Math.min(previewSize.getWidth(), previewSize.getHeight()));
parameters.setPictureSize(Math.max(pictureSize.getWidth(), pictureSize.getHeight()), Math.min(pictureSize.getWidth(), pictureSize.getHeight()));
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setRotation(getDisplayOrientation());
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
isPreview = true;
} catch (Exception e) {
Log.e("CameraPreview", "相机预览错误: " + e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (holder.getSurface() == null) {
return;
}
mCamera.stopPreview();
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
if (isPreview) {
mCamera.stopPreview();
mCamera.release();
}
}
}
private com.axs.camera.AspectRatio getDeviceAspectRatio(Activity activity) {
int width = activity.getWindow().getDecorView().getWidth();
int height = activity.getWindow().getDecorView().getHeight();
return com.axs.camera.AspectRatio.of(Math.min(width, height), Math.max(width, height));
}
@SuppressWarnings("SuspiciousNameCombination")
private com.axs.camera.Size chooseOptimalSize(SortedSet<com.axs.camera.Size> sizes) {
int desiredWidth;
int desiredHeight;
final int surfaceWidth = getWidth();
final int surfaceHeight = getHeight();
if (isLandscape(mDisplayOrientation)) {
desiredWidth = surfaceHeight;
desiredHeight = surfaceWidth;
} else {
desiredWidth = surfaceWidth;
desiredHeight = surfaceHeight;
}
com.axs.camera.Size result = new com.axs.camera.Size(desiredWidth, desiredHeight);
if (sizes != null && !sizes.isEmpty()) {
for (com.axs.camera.Size size : sizes) {
if (desiredWidth <= size.getWidth() && desiredHeight <= size.getHeight()) {
return size;
}
result = size;
}
}
return result;
}
private boolean isLandscape(int orientationDegrees) {
return (orientationDegrees == Surface.ROTATION_90 ||
orientationDegrees == Surface.ROTATION_270);
}
private int getDisplayOrientation() {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info);
int orientation;
int degrees = 0;
if (mDisplayOrientation == Surface.ROTATION_0) {
degrees = 0;
} else if (mDisplayOrientation == Surface.ROTATION_90) {
degrees = 90;
} else if (mDisplayOrientation == Surface.ROTATION_180) {
degrees = 180;
} else if (mDisplayOrientation == Surface.ROTATION_270) {
degrees = 270;
}
orientation = (degrees + 45) / 90 * 90;
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation - orientation + 360) % 360;
} else {
result = (info.orientation + orientation) % 360;
}
return result;
}
}
package com.axs.camera;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.util.Log;
import android.view.WindowManager;
import androidx.appcompat.widget.AppCompatImageView;
import java.util.ArrayList;
import java.util.List;
public class OverCameraView extends AppCompatImageView {
private Context context;
private Rect touchFocusRect;
private Paint touchFocusPaint;
private boolean isFoucuing;
public OverCameraView(Context context) {
this(context, null, 0);
}
public OverCameraView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public OverCameraView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
this.context = context;
touchFocusPaint = new Paint();
touchFocusPaint.setColor(Color.GREEN);
touchFocusPaint.setStyle(Paint.Style.STROKE);
touchFocusPaint.setStrokeWidth(3);
}
public boolean isFoucuing() {
return isFoucuing;
}
public void setFoucuing(boolean foucuing) {
isFoucuing = foucuing;
}
public void setTouchFoucusRect(Camera camera, Camera.AutoFocusCallback autoFocusCallback, float x, float y) {
touchFocusRect = new Rect((int) (x - 100), (int) (y - 100), (int) (x + 100), (int) (y + 100));
int left = touchFocusRect.left * 2000 / getWindowWidth(context) - 1000;
int top = touchFocusRect.top * 2000 / getWindowHeight(context) - 1000;
int right = touchFocusRect.right * 2000 / getWindowWidth(context) - 1000;
int bottom = touchFocusRect.bottom * 2000 / getWindowHeight(context) - 1000;
left = left < -1000 ? -1000 : left;
top = top < -1000 ? -1000 : top;
right = right > 1000 ? 1000 : right;
bottom = bottom > 1000 ? 1000 : bottom;
final Rect targetFocusRect = new Rect(left, top, right, bottom);
doTouchFocus(camera, autoFocusCallback, targetFocusRect);
postInvalidate();
}
public void doTouchFocus(Camera camera, Camera.AutoFocusCallback autoFocusCallback, final Rect tfocusRect) {
if (camera == null || isFoucuing) {
return;
}
try {
final List<Camera.Area> focusList = new ArrayList<>();
Camera.Area focusArea = new Camera.Area(tfocusRect, 1000);
focusList.add(focusArea);
Camera.Parameters para = camera.getParameters();
para.setFocusAreas(focusList);
para.setMeteringAreas(focusList);
para.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
camera.cancelAutoFocus();
camera.setParameters(para);
camera.autoFocus(autoFocusCallback);
isFoucuing = true;
} catch (Exception e) {
Log.e("设置相机参数异常", e.getMessage());
}
}
public void disDrawTouchFocusRect() {
touchFocusRect = null;
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
drawTouchFocusRect(canvas);
super.onDraw(canvas);
}
@SuppressWarnings("deprecation")
public static int getWindowHeight(Context cxt) {
WindowManager wm = (WindowManager) cxt
.getSystemService(Context.WINDOW_SERVICE);
return wm.getDefaultDisplay().getHeight();
}
@SuppressWarnings("deprecation")
public static int getWindowWidth(Context cxt) {
WindowManager wm = (WindowManager) cxt
.getSystemService(Context.WINDOW_SERVICE);
return wm.getDefaultDisplay().getWidth();
}
private void drawTouchFocusRect(Canvas canvas) {
if (null != touchFocusRect) {
canvas.drawRect(touchFocusRect.left - 2, touchFocusRect.bottom, touchFocusRect.left + 20, touchFocusRect.bottom + 2, touchFocusPaint);
canvas.drawRect(touchFocusRect.left - 2, touchFocusRect.bottom - 20, touchFocusRect.left, touchFocusRect.bottom, touchFocusPaint);
canvas.drawRect(touchFocusRect.left - 2, touchFocusRect.top - 2, touchFocusRect.left + 20, touchFocusRect.top, touchFocusPaint);
canvas.drawRect(touchFocusRect.left - 2, touchFocusRect.top, touchFocusRect.left, touchFocusRect.top + 20, touchFocusPaint);
canvas.drawRect(touchFocusRect.right - 20, touchFocusRect.top - 2, touchFocusRect.right + 2, touchFocusRect.top, touchFocusPaint);
canvas.drawRect(touchFocusRect.right, touchFocusRect.top, touchFocusRect.right + 2, touchFocusRect.top + 20, touchFocusPaint);
canvas.drawRect(touchFocusRect.right - 20, touchFocusRect.bottom, touchFocusRect.right + 2, touchFocusRect.bottom + 2, touchFocusPaint);
canvas.drawRect(touchFocusRect.right, touchFocusRect.bottom - 20, touchFocusRect.right + 2, touchFocusRect.bottom, touchFocusPaint);
}
}
}
package com.axs.camera;
import android.content.Context;
import android.os.Build;
import com.yanzhenjie.permission.AndPermission;
public class PermissionUtils {
public interface PermissionListener {
void onSuccess(Context context);
void onFailed(Context context);
}
public static void applicationPermissions(Context context, PermissionListener listener, String[]... permissions) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
if (!AndPermission.hasPermissions(context, permissions)) {
AndPermission.with(context)
.runtime()
.permission(permissions)
.rationale((mContext, data, executor) -> {
executor.execute();
})
.onGranted((permission) -> {
listener.onSuccess(context);
})
.onDenied((permission) -> {
listener.onFailed(context);
})
.start();
} else {
listener.onSuccess(context);
}
} else {
listener.onSuccess(context);
}
}
public static void applicationPermissions(Context context, PermissionListener listener, String... permissions) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (!AndPermission.hasPermissions(context, permissions)) {
AndPermission.with(context)
.runtime()
.permission(permissions)
.rationale((mContext, data, executor) -> {
executor.execute();
})
.onGranted((permission) -> {
listener.onSuccess(context);
})
.onDenied((permission) -> {
listener.onFailed(context);
})
.start();
} else {
listener.onSuccess(context);
}
} else {
listener.onSuccess(context);
}
}
}
package com.axs.camera;
public class Size implements Comparable<Size> {
private final int mWidth;
private final int mHeight;
public Size(int width, int height) {
mWidth = width;
mHeight = height;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (o instanceof Size) {
Size size = (Size) o;
return mWidth == size.mWidth && mHeight == size.mHeight;
}
return false;
}
@Override
public String toString() {
return mWidth + "x" + mHeight;
}
@Override
public int hashCode() {
return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
}
@Override
public int compareTo(Size another) {
return mWidth * mHeight - another.mWidth * another.mHeight;
}
}
package com.axs.camera;
import androidx.collection.ArrayMap;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
public class SizeMap {
private final ArrayMap<AspectRatio, SortedSet<Size>> mRatios = new ArrayMap<>();
public boolean add(Size size) {
for (AspectRatio ratio : mRatios.keySet()) {
if (ratio.matches(size)) {
final SortedSet<Size> sizes = mRatios.get(ratio);
if (sizes.contains(size)) {
return false;
} else {
sizes.add(size);
return true;
}
}
}
SortedSet<Size> sizes = new TreeSet<>();
sizes.add(size);
mRatios.put(AspectRatio.of(size.getWidth(), size.getHeight()), sizes);
return true;
}
public void remove(AspectRatio ratio) {
mRatios.remove(ratio);
}
Set<AspectRatio> ratios() {
return mRatios.keySet();
}
SortedSet<Size> sizes(AspectRatio ratio) {
if (mRatios.get(ratio) != null) {
return mRatios.get(ratio);
}
AspectRatio retRatio = ratio;
float diff = 1;
for (AspectRatio size : ratios()) {
if (Math.abs(ratio.toFloat() - size.toFloat()) < diff) {
retRatio = size;
diff = Math.abs(ratio.toFloat() - size.toFloat());
}
}
return mRatios.get(retRatio);
}
void clear() {
mRatios.clear();
}
boolean isEmpty() {
return mRatios.isEmpty();
}
}
|