package win.smartown.android.library.certificateCamera; import android.app.Activity; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.hardware.Camera; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class CameraActivity extends Activity implements View.OnClickListener { /** * 拍摄类型-身份证正面 */ public final static int TYPE_IDCARD_FRONT = 1; /** * 拍摄类型-身份证反面 */ public final static int TYPE_IDCARD_BACK = 2; /** * 拍摄类型-竖版营业执照 */ public final static int TYPE_COMPANY_PORTRAIT = 3; /** * 拍摄类型-横版营业执照 */ public final static int TYPE_COMPANY_LANDSCAPE = 4; public final static int REQUEST_CODE = 0X13; public final static int RESULT_CODE = 0X14; /** * @param type {@link #TYPE_IDCARD_FRONT} * {@link #TYPE_IDCARD_BACK} * {@link #TYPE_COMPANY_PORTRAIT} * {@link #TYPE_COMPANY_LANDSCAPE} */ public static void openCertificateCamera(Activity activity, int type) { Intent intent = new Intent(activity, CameraActivity.class); intent.putExtra("type", type); activity.startActivityForResult(intent, REQUEST_CODE); } /** * @return 结果文件路径 */ public static String getResult(Intent data) { if (data != null) { return data.getStringExtra("result"); } return ""; } private CameraPreview cameraPreview; private View containerView; private ImageView cropView; private ImageView flashImageView; private View optionView; private View resultView; private int type; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); type = getIntent().getIntExtra("type", 0); if (type == TYPE_COMPANY_PORTRAIT) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } setContentView(R.layout.activity_camera); cameraPreview = (CameraPreview) findViewById(R.id.camera_surface); //获取屏幕最小边,设置为cameraPreview较窄的一边 float screenMinSize = Math.min(getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); //根据screenMinSize,计算出cameraPreview的较宽的一边,长宽比为标准的16:9 float maxSize = screenMinSize / 9.0f * 16.0f; RelativeLayout.LayoutParams layoutParams; if (type == TYPE_COMPANY_PORTRAIT) { layoutParams = new RelativeLayout.LayoutParams((int) screenMinSize, (int) maxSize); } else { layoutParams = new RelativeLayout.LayoutParams((int) maxSize, (int) screenMinSize); } layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); cameraPreview.setLayoutParams(layoutParams); containerView = findViewById(R.id.camera_crop_container); cropView = (ImageView) findViewById(R.id.camera_crop); if (type == TYPE_COMPANY_PORTRAIT) { float width = (int) (screenMinSize * 0.8); float height = (int) (width * 43.0f / 30.0f); LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) height); LinearLayout.LayoutParams cropParams = new LinearLayout.LayoutParams((int) width, (int) height); containerView.setLayoutParams(containerParams); cropView.setLayoutParams(cropParams); } else if (type == TYPE_COMPANY_LANDSCAPE) { float height = (int) (screenMinSize * 0.8); float width = (int) (height * 43.0f / 30.0f); LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams((int) width, ViewGroup.LayoutParams.MATCH_PARENT); LinearLayout.LayoutParams cropParams = new LinearLayout.LayoutParams((int) width, (int) height); containerView.setLayoutParams(containerParams); cropView.setLayoutParams(cropParams); } else { float height = (int) (screenMinSize * 0.75); float width = (int) (height * 75.0f / 47.0f); LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams((int) width, ViewGroup.LayoutParams.MATCH_PARENT); LinearLayout.LayoutParams cropParams = new LinearLayout.LayoutParams((int) width, (int) height); containerView.setLayoutParams(containerParams); cropView.setLayoutParams(cropParams); } switch (type) { case TYPE_IDCARD_FRONT: cropView.setImageResource(R.mipmap.camera_idcard_front); break; case TYPE_IDCARD_BACK: cropView.setImageResource(R.mipmap.camera_idcard_back); break; case TYPE_COMPANY_PORTRAIT: cropView.setImageResource(R.mipmap.camera_company); break; case TYPE_COMPANY_LANDSCAPE: cropView.setImageResource(R.mipmap.camera_company_landscape); break; } flashImageView = (ImageView) findViewById(R.id.camera_flash); optionView = findViewById(R.id.camera_option); resultView = findViewById(R.id.camera_result); cameraPreview.setOnClickListener(this); findViewById(R.id.camera_close).setOnClickListener(this); findViewById(R.id.camera_take).setOnClickListener(this); flashImageView.setOnClickListener(this); findViewById(R.id.camera_result_ok).setOnClickListener(this); findViewById(R.id.camera_result_cancel).setOnClickListener(this); } @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.camera_surface) { cameraPreview.focus(); } else if (id == R.id.camera_close) { finish(); } else if (id == R.id.camera_take) { takePhoto(); } else if (id == R.id.camera_flash) { boolean isFlashOn = cameraPreview.switchFlashLight(); flashImageView.setImageResource(isFlashOn ? R.mipmap.camera_flash_on : R.mipmap.camera_flash_off); } else if (id == R.id.camera_result_ok) { goBack(); } else if (id == R.id.camera_result_cancel) { optionView.setVisibility(View.VISIBLE); cameraPreview.setEnabled(true); resultView.setVisibility(View.GONE); cameraPreview.startPreview(); } } private void takePhoto() { optionView.setVisibility(View.GONE); cameraPreview.setEnabled(false); cameraPreview.takePhoto(new Camera.PictureCallback() { @Override public void onPictureTaken(final byte[] data, Camera camera) { camera.stopPreview(); //子线程处理图片,防止ANR new Thread(new Runnable() { @Override public void run() { try { File originalFile = getOriginalFile(); FileOutputStream originalFileOutputStream = new FileOutputStream(originalFile); originalFileOutputStream.write(data); originalFileOutputStream.close(); Bitmap bitmap = BitmapFactory.decodeFile(originalFile.getPath()); //计算裁剪位置 float left, top, right, bottom; // if (type == TYPE_COMPANY_PORTRAIT) { // left = (float) cropView.getLeft() / (float) cameraPreview.getWidth(); // top = ((float) containerView.getTop() - (float) cameraPreview.getTop()) / (float) cameraPreview.getHeight(); // right = (float) cropView.getRight() / (float) cameraPreview.getWidth(); // bottom = (float) containerView.getBottom() / (float) cameraPreview.getHeight(); // } else { left = ((float) containerView.getLeft() - (float) cameraPreview.getLeft()) / (float) cameraPreview.getWidth(); top = (float) cropView.getTop() / (float) cameraPreview.getHeight(); right = (float) containerView.getRight() / (float) cameraPreview.getWidth(); bottom = (float) cropView.getBottom() / (float) cameraPreview.getHeight(); // } //裁剪及保存到文件 Bitmap cropBitmap = Bitmap.createBitmap(bitmap, (int) (left * (float) bitmap.getWidth()), (int) (top * (float) bitmap.getHeight()), (int) ((right - left) * (float) bitmap.getWidth()), (int) ((bottom - top) * (float) bitmap.getHeight())); final File cropFile = getCropFile(); cropBitmap.isMutable(); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(cropFile)); cropBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos); bos.flush(); bos.close(); runOnUiThread(new Runnable() { @Override public void run() { resultView.setVisibility(View.VISIBLE); } }); return; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } runOnUiThread(new Runnable() { @Override public void run() { optionView.setVisibility(View.VISIBLE); cameraPreview.setEnabled(true); } }); } }).start(); } }); } /** * @return 拍摄图片原始文件 */ private File getOriginalFile() { switch (type) { case TYPE_IDCARD_FRONT: return new File(getExternalCacheDir(), "idCardFront.jpg"); case TYPE_IDCARD_BACK: return new File(getExternalCacheDir(), "idCardBack.jpg"); case TYPE_COMPANY_PORTRAIT: case TYPE_COMPANY_LANDSCAPE: return new File(getExternalCacheDir(), "companyInfo.jpg"); } return new File(getExternalCacheDir(), "picture.jpg"); } /** * @return 拍摄图片裁剪文件 */ private File getCropFile() { switch (type) { case TYPE_IDCARD_FRONT: return new File(getExternalCacheDir(), "idCardFrontCrop.jpg"); case TYPE_IDCARD_BACK: return new File(getExternalCacheDir(), "idCardBackCrop.jpg"); case TYPE_COMPANY_PORTRAIT: case TYPE_COMPANY_LANDSCAPE: return new File(getExternalCacheDir(), "companyInfoCrop.jpg"); } return new File(getExternalCacheDir(), "pictureCrop.jpg"); } /** * 点击对勾,使用拍照结果,返回对应图片路径 */ private void goBack() { Intent intent = new Intent(); intent.putExtra("result", getCropFile().getPath()); setResult(RESULT_CODE, intent); finish(); } }
package win.smartown.android.library.certificateCamera; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Configuration; import android.hardware.Camera; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.util.List; public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static String TAG = CameraPreview.class.getName(); private Camera camera; public CameraPreview(Context context) { super(context); init(); } public CameraPreview(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { SurfaceHolder surfaceHolder = getHolder(); surfaceHolder.addCallback(this); surfaceHolder.setKeepScreenOn(true); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { camera = CameraUtils.openCamera(); if (camera != null) { try { camera.setPreviewDisplay(holder); Camera.Parameters parameters = camera.getParameters(); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { //竖屏拍照时,需要设置旋转90度,否者看到的相机预览方向和界面方向不相同 camera.setDisplayOrientation(90); parameters.setRotation(90); } else { camera.setDisplayOrientation(0); parameters.setRotation(0); } Camera.Size bestSize = getBestSize(parameters.getSupportedPreviewSizes()); if (bestSize != null) { parameters.setPreviewSize(bestSize.width, bestSize.height); parameters.setPictureSize(bestSize.width, bestSize.height); } else { parameters.setPreviewSize(1920, 1080); parameters.setPictureSize(1920, 1080); } camera.setParameters(parameters); camera.startPreview(); focus(); } catch (Exception e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); try { Camera.Parameters parameters = camera.getParameters(); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { //竖屏拍照时,需要设置旋转90度,否者看到的相机预览方向和界面方向不相同 camera.setDisplayOrientation(90); parameters.setRotation(90); } else { camera.setDisplayOrientation(0); parameters.setRotation(0); } camera.setParameters(parameters); camera.startPreview(); focus(); } catch (Exception e1) { e.printStackTrace(); camera = null; } } } } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { //因为设置了固定屏幕方向,所以在实际使用中不会触发这个方法 } public void surfaceDestroyed(SurfaceHolder holder) { //回收释放资源 release(); } /** * Android相机的预览尺寸都是4:3或者16:9,这里遍历所有支持的预览尺寸,得到16:9的最大尺寸,保证成像清晰度 * * @param sizes * @return 最佳尺寸 */ private Camera.Size getBestSize(List<Camera.Size> sizes) { Camera.Size bestSize = null; for (Camera.Size size : sizes) { if ((float) size.width / (float) size.height == 16.0f / 9.0f) { if (bestSize == null) { bestSize = size; } else { if (size.width > bestSize.width) { bestSize = size; } } } } return bestSize; } /** * 释放资源 */ private void release() { if (camera != null) { camera.stopPreview(); camera.release(); camera = null; } } /** * 对焦,在CameraActivity中触摸对焦 */ public void focus() { if (camera != null) { camera.autoFocus(null); } } /** * 开关闪光灯 * * @return 闪光灯是否开启 */ public boolean switchFlashLight() { if (camera != null) { Camera.Parameters parameters = camera.getParameters(); if (parameters.getFlashMode().equals(Camera.Parameters.FLASH_MODE_OFF)) { parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); camera.setParameters(parameters); return true; } else { parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); camera.setParameters(parameters); return false; } } return false; } /** * 拍摄照片 * * @param pictureCallback 在pictureCallback处理拍照回调 */ public void takePhoto(Camera.PictureCallback pictureCallback) { if (camera != null) { camera.takePicture(null, null, pictureCallback); } } public void startPreview() { if (camera != null) { camera.startPreview(); } } }
package win.smartown.android.library.certificateCamera; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.Camera; public class CameraUtils { /** * Check if this device has a camera */ public static boolean hasCamera(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // this device has a camera return true; } else { // no camera on this device return false; } } /** * A safe way to get an instance of the Camera object. */ public static Camera openCamera() { Camera c = null; try { c = Camera.open(); // attempt to get a Camera instance } catch (Exception e) { // Camera is not available (in use or does not exist) } return c; // returns null if camera is unavailable } }
<?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" android:background="#000"> <win.smartown.android.library.certificateCamera.CameraPreview android:id="@+id/camera_surface" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="@color/preview_mock" android:gravity="center" android:text="Sentuh layar untuk fokus" android:textColor="#afff" android:textSize="16dp" /> <LinearLayout android:id="@+id/camera_crop_container" android:layout_width="0dp" android:layout_height="0dp" android:orientation="horizontal"> <View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@color/preview_mock" /> <ImageView android:id="@+id/camera_crop" android:layout_width="0dp" android:layout_height="0dp" android:scaleType="fitXY" /> <View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@color/preview_mock" /> </LinearLayout> </LinearLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="136dp" android:background="@color/preview_mock"> <LinearLayout android:id="@+id/camera_option" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="horizontal"> <ImageView android:id="@+id/camera_close" android:layout_width="40dp" android:layout_height="40dp" android:src="@mipmap/camera_close" /> <ImageView android:id="@+id/camera_take" android:layout_width="72dp" android:layout_height="72dp" android:layout_margin="32dp" android:src="@mipmap/camera_take" /> <ImageView android:id="@+id/camera_flash" android:layout_width="40dp" android:layout_height="40dp" android:src="@mipmap/camera_flash_off" /> </LinearLayout> <LinearLayout android:id="@+id/camera_result" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="horizontal" android:visibility="gone"> <ImageView android:id="@+id/camera_result_cancel" android:layout_width="40dp" android:layout_height="40dp" android:src="@mipmap/camera_result_cancel" /> <ImageView android:id="@+id/camera_result_ok" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginLeft="80dp" android:src="@mipmap/camera_result_ok" /> </LinearLayout> </FrameLayout> </LinearLayout> </RelativeLayout>
<?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" android:background="#000"> <win.smartown.android.library.certificateCamera.CameraPreview android:id="@+id/camera_surface" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal"> <View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@color/preview_mock" /> <LinearLayout android:id="@+id/camera_crop_container" android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="@color/preview_mock" /> <ImageView android:id="@+id/camera_crop" android:layout_width="0dp" android:layout_height="0dp" android:scaleType="fitXY" /> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="@color/preview_mock" android:gravity="center" android:text="@string/touch_to_focus" android:textColor="#afff" android:textSize="16dp" /> </LinearLayout> </LinearLayout> <FrameLayout android:layout_width="136dp" android:layout_height="match_parent" android:background="@color/preview_mock"> <LinearLayout android:id="@+id/camera_option" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <ImageView android:id="@+id/camera_flash" android:layout_width="40dp" android:layout_height="40dp" android:src="@mipmap/camera_flash_off" /> <ImageView android:id="@+id/camera_take" android:layout_width="72dp" android:layout_height="72dp" android:layout_margin="32dp" android:src="@mipmap/camera_take" /> <ImageView android:id="@+id/camera_close" android:layout_width="40dp" android:layout_height="40dp" android:src="@mipmap/camera_close" /> </LinearLayout> <LinearLayout android:id="@+id/camera_result" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:visibility="gone"> <ImageView android:id="@+id/camera_result_ok" android:layout_width="40dp" android:layout_height="40dp" android:src="@mipmap/camera_result_ok" /> <ImageView android:id="@+id/camera_result_cancel" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginTop="80dp" android:src="@mipmap/camera_result_cancel" /> </LinearLayout> </FrameLayout> </LinearLayout> </RelativeLayout>