CameraActivity.class
public class CameraActivity extends AppCompatActivity implements SurfaceHolder.Callback,View.OnClickListener,View.OnTouchListener{
private static final String TAG = "CameraActivity";
private SurfaceHolder mHolder;
private SurfaceView mSurfaceView;
private Button mBtnSearch;
private Button mBtnTakePhoto;
private View focusIndex;
private ImageView flashBtn;
private int mCurrentCameraId = 0; // 1是前置 0是后置
private Camera mCamera;
private CameraDoHelper mCameraHelper;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
initView();
initData();
}
private void initView() {
focusIndex = findViewById(R.id.focus_index);
flashBtn = (ImageView) findViewById(R.id.flash_view);
mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
//mCameraGrid = (CameraGrid) findViewById(R.id.camera_grid);
mBtnSearch = (Button) findViewById(R.id.search);
mBtnTakePhoto = (Button) findViewById(R.id.takephoto);
mSurfaceView.setOnTouchListener(this);
}
private void initData() {
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setKeepScreenOn(true);
}
@Override
protected void onResume() {
super.onResume();
// int numCams = Camera.getNumberOfCameras();
// if (numCams > 0) {
// try {
// mCurrentCameraId = 0;
// mCamera = Camera.open(mCurrentCameraId);
// mCamera.startPreview();
// } catch (RuntimeException ex) {
// Toast.makeText(getApplicationContext(), "未发现相机", Toast.LENGTH_LONG).show();
// }
// }
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.i(TAG,"surfaceCreated");
int numCams = Camera.getNumberOfCameras();
if (numCams > 0) {
try {
mCurrentCameraId = 0;
mCamera = Camera.open(mCurrentCameraId);
} catch (RuntimeException ex) {
Toast.makeText(getApplicationContext(), "未发现相机", Toast.LENGTH_LONG).show();
}
}
if (mCamera != null){
CameraDoHelper.getInstance(this).initCamera(mCamera,mCurrentCameraId);
}
}
//这个方法至少会在surfaceCreated之后调用。
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) {
Log.i(TAG,"surfaceChanged");
if (surfaceHolder.getSurface() == null) {
return;
}
if (mCamera != null) {
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
CameraDoHelper.getInstance(this).reAutoFocus(mCamera);
} catch (IOException e) {
e.printStackTrace();
if (mCamera != null){
mCamera.release();
mCamera = null;
}
}
}
}
//另一个Activity覆盖当前的Activity时,会调用这个方法。再回来时,就会调用surfaceCreated方法
//点击退出当前Activity时,先于Ondestory
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.i(TAG,"surfaceDestroyed");
if (mCamera != null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
/*case R.id.camera_back:
setResult(0);
finish();
break;*/
case R.id.camera_flip_view:
//转化前置或者后置摄像头
switchCamera();
break;
case R.id.flash_view:
turnLight();
break;
case R.id.action_button:
CameraDoHelper.getInstance(this).takePhoto(mCamera);
break;
case R.id.search: //处理选中状态
mBtnSearch.setSelected(true);
mBtnTakePhoto.setSelected(false);
break;
case R.id.takephoto: //处理选中状态
mBtnTakePhoto.setSelected(true);
mBtnSearch.setSelected(false);
break;
}
}
// 切换前后置摄像头
public void switchCamera() {
mCurrentCameraId = (mCurrentCameraId + 1) % Camera.getNumberOfCameras();
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
try {
mCamera = Camera.open(mCurrentCameraId);
mCamera.setPreviewDisplay(mHolder);
CameraDoHelper.getInstance(this).initCamera(mCamera,mCurrentCameraId);
mCamera.startPreview();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "未发现相机", Toast.LENGTH_LONG).show();
}
}
/**
* 闪光灯开关 开->关->自动
*
*/
private void turnLight() {
if (mCamera == null || mCamera.getParameters() == null
|| mCamera.getParameters().getSupportedFlashModes() == null) {
return;
}
Camera.Parameters parameters = mCamera.getParameters();
String flashMode = mCamera.getParameters().getFlashMode();
List<String> supportedModes = mCamera.getParameters()
.getSupportedFlashModes();
if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)
&& supportedModes.contains(Camera.Parameters.FLASH_MODE_ON)) {// 关闭状态
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
mCamera.setParameters(parameters);
flashBtn.setImageResource(R.mipmap.camera_flash_on);
} else if (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {// 开启状态
if (supportedModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
flashBtn.setImageResource(R.mipmap.camera_flash_auto);
mCamera.setParameters(parameters);
} else if (supportedModes
.contains(Camera.Parameters.FLASH_MODE_OFF)) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
flashBtn.setImageResource(R.mipmap.camera_flash_off);
mCamera.setParameters(parameters);
}
} else if (Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)
&& supportedModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameters);
flashBtn.setImageResource(R.mipmap.camera_flash_off);
}
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
CameraDoHelper.getInstance(this).pointFocus(motionEvent);
}
} catch (Exception e) {
e.printStackTrace();
}
RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(
focusIndex.getLayoutParams());
layout.setMargins((int) motionEvent.getX() - 60, (int) motionEvent.getY() - 60, 0,0);
focusIndex.setLayoutParams(layout);
focusIndex.setVisibility(View.VISIBLE);
ScaleAnimation sa = new ScaleAnimation(3f, 1f, 3f, 1f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
sa.setDuration(800);
focusIndex.startAnimation(sa);
handler.postAtTime(new Runnable() {
@Override
public void run() {
focusIndex.setVisibility(View.INVISIBLE);
}
}, 800);
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG,"onDestroy");
}
}
CameraDoHelper.class
public class CameraDoHelper implements Camera.AutoFocusCallback {
private static final String TAG = "CameraDoHelper";
public static Camera.Size mPreviewSize;
public static Camera.Size adapterSize;
private int screenWidth;
private int screenHeight;
private boolean isSupportAutoFocus = false;
private Context mContext;
private int mCurrentCameraId = 0; // 1是前置 0是后置
private Camera mCamera;
/**
* 最小预览界面的分辨率
*/
private static final int MIN_PREVIEW_PIXELS = 480 * 320;
/**
* 最大宽高比差
*/
private static final double MAX_ASPECT_DISTORTION = 0.15;
private static CameraDoHelper mHelper;
public static synchronized CameraDoHelper getInstance(Context context){
if (mHelper == null){
synchronized (CameraDoHelper.class){
if (mHelper == null)
mHelper = new CameraDoHelper(context);
}
}
return mHelper;
}
private CameraDoHelper(Context context){
mContext = context;
DisplayMetrics dm = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
//判断是否支持自动聚焦
isSupportAutoFocus = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA_AUTOFOCUS);
}
public void initCamera(Camera camera,int cameraId){
if (camera != null) {
mCurrentCameraId = cameraId;
mCamera = camera;
Camera.Parameters params = camera.getParameters();
mPreviewSize = findBestPreviewResolution(camera);
adapterSize = findBestPictureResolution(camera);
if (adapterSize != null) {
params.setPictureSize(adapterSize.width, adapterSize.height);
}
if (mPreviewSize != null) {
params.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
}
params.setPictureFormat(PixelFormat.JPEG);
List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
setDispaly();
//setCameraDisplayOrientation((Activity) mContext, mCurrentCameraId, mCamera);
camera.setParameters(params);
}
}
//控制图像的正确显示方向
/**
* 将当前相机应用设为垂直Activity应用的时候,就需要调整一下显示的角度
*/
private void setDispaly() {
if (Build.VERSION.SDK_INT >= 8) {
setDisplayOrientation(mCamera, 90);
} else {
mCamera.getParameters().setRotation(90);
}
}
//实现的图像的正确显示
private void setDisplayOrientation(Camera camera, int i) {
Method downPolymorphic;
try {
downPolymorphic = camera.getClass().getMethod("setDisplayOrientation",
new Class[]{int.class});
if (downPolymorphic != null) {
downPolymorphic.invoke(camera, new Object[]{i});
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
/**
* 找出最适合的预览界面分辨率
*
* @return
*/
private Camera.Size findBestPreviewResolution(Camera camera) {
Camera.Parameters cameraParameters = camera.getParameters();
Camera.Size defaultPreviewResolution = cameraParameters.getPreviewSize();
List<Camera.Size> rawSupportedSizes = cameraParameters.getSupportedPreviewSizes();
if (rawSupportedSizes == null) {
Log.i(TAG,"rawSupportedSizes == null ");
return defaultPreviewResolution;
}
// 按照分辨率从大到小排序
List<Camera.Size> supportedPreviewResolutions = new ArrayList<Camera.Size>(rawSupportedSizes);
Collections.sort(supportedPreviewResolutions, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size a, Camera.Size b) {
int aPixels = a.height * a.width;
int bPixels = b.height * b.width;
if (bPixels < aPixels) {
return -1;
}
if (bPixels > aPixels) {
return 1;
}
return 0;
}
});
StringBuilder previewResolutionSb = new StringBuilder();
for (Camera.Size supportedPreviewResolution : supportedPreviewResolutions) {
previewResolutionSb.append(supportedPreviewResolution.width).append('x').append(supportedPreviewResolution.height)
.append(' ');
}
Log.i(TAG,"supportedPreview : "+previewResolutionSb);
// 移除不符合条件的分辨率
double screenAspectRatio = (double) screenWidth
/ screenHeight;
Iterator<Camera.Size> it = supportedPreviewResolutions.iterator();
while (it.hasNext()) {
Camera.Size supportedPreviewResolution = it.next();
int width = supportedPreviewResolution.width;
int height = supportedPreviewResolution.height;
// 移除低于下限的分辨率,尽可能取高分辨率
if (width * height < MIN_PREVIEW_PIXELS) {
it.remove();
continue;
}
// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率
// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height
// 因此这里要先交换然preview宽高比后在比较
boolean isCandidatePortrait = width > height;
int maybeFlippedWidth = isCandidatePortrait ? height : width;
int maybeFlippedHeight = isCandidatePortrait ? width : height;
double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
double distortion = Math.abs(aspectRatio - screenAspectRatio);
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
// 找到与屏幕分辨率完全匹配的预览界面分辨率直接返回
if (maybeFlippedWidth == screenWidth
&& maybeFlippedHeight == screenHeight) {
return supportedPreviewResolution;
}
}
// 如果没有找到合适的,并且还有候选的像素,则设置其中最大比例的,对于配置比较低的机器不太合适
if (!supportedPreviewResolutions.isEmpty()) {
Camera.Size largestPreview = supportedPreviewResolutions.get(0);
return largestPreview;
}
// 没有找到合适的,就返回默认的
return defaultPreviewResolution;
}
private Camera.Size findBestPictureResolution(Camera camera) {
Camera.Parameters cameraParameters = camera.getParameters();
List<Camera.Size> supportedPicResolutions = cameraParameters.getSupportedPictureSizes(); // 至少会返回一个值
StringBuilder picResolutionSb = new StringBuilder();
for (Camera.Size supportedPicResolution : supportedPicResolutions) {
picResolutionSb.append(supportedPicResolution.width).append('x')
.append(supportedPicResolution.height).append(" ");
}
Camera.Size defaultPictureResolution = cameraParameters.getPictureSize();
// 排序
List<Camera.Size> sortedSupportedPicResolutions = new ArrayList<Camera.Size>(
supportedPicResolutions);
Collections.sort(sortedSupportedPicResolutions, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size a, Camera.Size b) {
int aPixels = a.height * a.width;
int bPixels = b.height * b.width;
if (bPixels < aPixels) {
return -1;
}
if (bPixels > aPixels) {
return 1;
}
return 0;
}
});
// 移除不符合条件的分辨率
double screenAspectRatio = screenWidth
/ (double) screenHeight;
Iterator<Camera.Size> it = sortedSupportedPicResolutions.iterator();
while (it.hasNext()) {
Camera.Size supportedPreviewResolution = it.next();
int width = supportedPreviewResolution.width;
int height = supportedPreviewResolution.height;
// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率
// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height
// 因此这里要先交换然后在比较宽高比
boolean isCandidatePortrait = width > height;
int maybeFlippedWidth = isCandidatePortrait ? height : width;
int maybeFlippedHeight = isCandidatePortrait ? width : height;
double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
double distortion = Math.abs(aspectRatio - screenAspectRatio);
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
}
// 如果没有找到合适的,并且还有候选的像素,对于照片,则取其中最大比例的,而不是选择与屏幕分辨率相同的
if (!sortedSupportedPicResolutions.isEmpty()) {
return sortedSupportedPicResolutions.get(0);
}
// 没有找到合适的,就返回默认的
return defaultPictureResolution;
}
/**
* 自动对焦
* @param camera
*/
public void reAutoFocus(Camera camera) {
if (isSupportAutoFocus) {
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
}
});
}
}
public void takePhoto(Camera camera) {
try {
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
} catch (Throwable t) {
t.printStackTrace();
Toast.makeText(mContext, "拍照失败,请重试!", Toast.LENGTH_LONG)
.show();
try {
camera.startPreview();
} catch (Throwable e) {
}
}
}
Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() {
public void onShutter() {
}
};
Camera.PictureCallback rawCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
}
};
Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
new SaveImageTask(data,mContext,mCurrentCameraId).execute();
resetCamera(camera);
}
};
private void resetCamera( Camera camera) {
camera.startPreview();
initCamera(camera,mCurrentCameraId);
}
//定点对焦的代码
public void pointFocus(MotionEvent event) {
mCamera.cancelAutoFocus();
Camera.Parameters parameters = mCamera.getParameters();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
//showPoint(x, y);
focusOnTouch(event);
}
mCamera.setParameters(parameters);
autoFocus(mCamera);
}
//实现自动对焦
public void autoFocus(final Camera camera) {
new Thread() {
@Override
public void run() {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (camera == null) {
return;
}
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
initCamera(camera,mCurrentCameraId);//实现相机的参数初始化
}
}
});
}
};
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void focusOnTouch(MotionEvent event) {
Rect focusRect = calculateTapArea(event.getRawX(), event.getRawY(), 1f);
Rect meteringRect = calculateTapArea(event.getRawX(), event.getRawY(), 1.5f);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
if (parameters.getMaxNumFocusAreas() > 0) {
List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
focusAreas.add(new Camera.Area(focusRect, 1000));
parameters.setFocusAreas(focusAreas);
}
if (parameters.getMaxNumMeteringAreas() > 0) {
List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
meteringAreas.add(new Camera.Area(meteringRect, 1000));
parameters.setMeteringAreas(meteringAreas);
}
mCamera.setParameters(parameters);
mCamera.autoFocus(this);
}
@Override
public void onAutoFocus(boolean success, Camera camera) {
Log.d(TAG, "onAutoFocus : " + success);
}
/**
* Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000.
*/
private Rect calculateTapArea(float x, float y, float coefficient) {
float focusAreaSize = 300;
int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();
int centerX = (int) (x / getResolution().width * 2000 - 1000);
int centerY = (int) (y / getResolution().height * 2000 - 1000);
int left = clamp(centerX - areaSize / 2, -1000, 1000);
int right = clamp(left + areaSize, -1000, 1000);
int top = clamp(centerY - areaSize / 2, -1000, 1000);
int bottom = clamp(top + areaSize, -1000, 1000);
return new Rect(left, top, right, bottom);
}
private int clamp(int x, int min, int max) {
if (x > max) {
return max;
}
if (x < min) {
return min;
}
return x;
}
public Camera.Size getResolution() {
Camera.Parameters params = mCamera.getParameters();
Camera.Size s = params.getPreviewSize();
return s;
}
}
activity_camera.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_camera"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.goertek.cameraapp.CameraActivity">
<!-- 预览画布 -->
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 闪光灯、前置摄像头、后置摄像头、聚焦 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!--<org.gaochun.camera.CameraGrid-->
<!--android:id="@+id/camera_grid"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--android:layout_alignParentTop="true" />-->
<View
android:id="@+id/focus_index"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/camera_focus"
android:visibility="invisible" />
<ImageView
android:id="@+id/flash_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:onClick="onClick"
android:padding="15dp"
android:scaleType="centerCrop"
android:src="@mipmap/camera_flash_off" />
<ImageView
android:id="@+id/camera_flip_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:onClick="onClick"
android:padding="15dp"
android:scaleType="centerCrop"
android:src="@mipmap/camera_flip" />
<!-- 底部按钮 -->
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:background="#a0000000"
android:padding="5dp" >
<Button
android:id="@+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:background="@null"
android:drawablePadding="3dp"
android:drawableTop="@drawable/ic_search_selector"
android:onClick="onClick"
android:text="搜图"
android:textColor="@drawable/row_selector_text" />
<ImageView
android:id="@+id/action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:clickable="true"
android:onClick="onClick"
android:src="@drawable/btn_shutter_photo" />
<Button
android:id="@+id/takephoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="30dp"
android:background="@null"
android:drawablePadding="3dp"
android:drawableTop="@drawable/ic_takephoto_selector"
android:onClick="onClick"
android:text="拍照"
android:textColor="@drawable/row_selector_text" />
</RelativeLayout>
</RelativeLayout>
</FrameLayout>