arrayList) {}
160 | }).setPermissions(new String[]{Manifest.permission.CAMERA})
161 | .check();
162 | }
163 | return true;
164 | }
165 |
166 | @Override
167 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
168 | super.onActivityResult(requestCode, resultCode, data);
169 | if (resultCode != RESULT_OK && resultCode != 200) return;
170 | if (requestCode == App.TAKE_PHOTO_CUSTOM) {
171 | mFile = new File(data.getStringExtra("file"));
172 | Observable.just(mFile)
173 | //将File解码为Bitmap
174 | .map(file -> BitmapUtils.compressToResolution(file, 1920 * 1080))
175 | //裁剪Bitmap
176 | .map(BitmapUtils::crop)
177 | //将Bitmap写入文件
178 | .map(bitmap -> BitmapUtils.writeBitmapToFile(bitmap, "mFile"))
179 | .subscribeOn(Schedulers.io())
180 | .observeOn(AndroidSchedulers.mainThread())
181 | .subscribe(file -> {
182 | mFile = file;
183 | Uri uri = Uri.parse("file://" + mFile.toString());
184 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
185 | //清除该Uri的Fresco缓存. 若不清除,对于相同文件名的图片,Fresco会直接使用缓存而使得Drawee得不到更新.
186 | imagePipeline.evictFromMemoryCache(uri);
187 | imagePipeline.evictFromDiskCache(uri);
188 | FrescoUtils.load("file://" + mFile.toString()).resize(240, 164).into(mImageView);
189 | mBtnTakePicture.setText("重新拍照");
190 | mHasSelectedOnce = true;
191 | });
192 | } else if (requestCode == App.TAKE_PHOTO_SYSTEM) {
193 | mFile = CommonUtils.createImageFile("mFile");
194 | Observable.just(mFile)
195 | //读入File,压缩为指定大小的Bitmap
196 | .map(file -> BitmapUtils.compressToResolution(file, 1920 * 1080))
197 | //系统相机拍出的照片方向可能是竖的,这里判断如果是竖的,就旋转90度变为横向
198 | .map(BitmapUtils::rotate)
199 | //将Bitmap写入文件
200 | .map(bitmap -> BitmapUtils.writeBitmapToFile(bitmap, "mFile"))
201 | .subscribeOn(Schedulers.io())
202 | .observeOn(AndroidSchedulers.mainThread())
203 | .subscribe(file -> {
204 | mFile = file;
205 | //删除fresco的缓存
206 | Uri uri = Uri.parse("file://" + mFile.toString());
207 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
208 | imagePipeline.evictFromMemoryCache(uri);
209 | imagePipeline.evictFromDiskCache(uri);
210 | FrescoUtils.load("file://" + mFile.toString()).resize(240, 164).into(mImageView);
211 | mBtnTakePicture.setText("重新拍照");
212 | mHasSelectedOnce = true;
213 | });
214 | }
215 | }
216 |
217 | @Override
218 | protected int getContentViewResId() {
219 | return R.layout.activity_main;
220 | }
221 |
222 | @Override
223 | protected void preInitData() {
224 | registerForContextMenu(mBtnTakePicture);
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xdandroid/hellocamera2/Camera2Activity.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.hellocamera2;
2 |
3 | import android.content.*;
4 | import android.graphics.*;
5 | import android.hardware.camera2.*;
6 | import android.hardware.camera2.params.*;
7 | import android.media.*;
8 | import android.os.*;
9 | import android.support.annotation.*;
10 | import android.util.*;
11 | import android.util.Size;
12 | import android.view.*;
13 | import android.widget.*;
14 |
15 | import com.jakewharton.rxbinding2.view.*;
16 | import com.xdandroid.hellocamera2.app.*;
17 | import com.xdandroid.hellocamera2.util.*;
18 |
19 | import java.io.*;
20 | import java.nio.*;
21 | import java.util.*;
22 | import java.util.concurrent.*;
23 |
24 | import butterknife.*;
25 | import io.reactivex.android.schedulers.*;
26 |
27 | /**
28 | * Camera2 API. Android Lollipop 及以后版本的 Android 使用 Camera2 API.
29 | *
30 | * 从https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/
31 | * com/example/android/camera2basic/Camera2BasicFragment.java拷贝而来.
32 | *
33 | * 进行了一些修改, 以文档注释的形式写出.
34 | */
35 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
36 | public class Camera2Activity extends BaseCameraActivity {
37 |
38 | @BindView(R.id.texture_camera_preview) TextureView mTextureView;
39 | @BindView(R.id.iv_camera_button) ImageView mIvCameraButton;
40 | @BindView(R.id.tv_camera_hint) TextView mTvCameraHint;
41 | @BindView(R.id.view_camera_dark0) View mViewDark0;
42 | @BindView(R.id.view_camera_dark1) LinearLayout mViewDark1;
43 |
44 | /**
45 | * finish()是否已调用过
46 | */
47 | volatile boolean mFinishCalled;
48 |
49 | /**
50 | * 最大允许的拍照尺寸(像素数)
51 | */
52 | long mMaxPicturePixels;
53 |
54 | /**
55 | * Conversion from screen rotation to JPEG orientation.
56 | */
57 | static final SparseIntArray ORIENTATIONS = new SparseIntArray();
58 |
59 | static {
60 | ORIENTATIONS.append(Surface.ROTATION_0, 90);
61 | ORIENTATIONS.append(Surface.ROTATION_90, 0);
62 | ORIENTATIONS.append(Surface.ROTATION_180, 270);
63 | ORIENTATIONS.append(Surface.ROTATION_270, 180);
64 | }
65 |
66 | /**
67 | * Tag for the {@link Log}.
68 | */
69 | static final String TAG = "Camera2BasicFragment";
70 |
71 | /**
72 | * Camera state: Showing camera preview.
73 | */
74 | static final int STATE_PREVIEW = 0;
75 |
76 | /**
77 | * Camera state: Waiting for the focus to be locked.
78 | */
79 | static final int STATE_WAITING_LOCK = 1;
80 |
81 | /**
82 | * Camera state: Waiting for the exposure to be precapture state.
83 | */
84 | static final int STATE_WAITING_PRECAPTURE = 2;
85 |
86 | /**
87 | * Camera state: Waiting for the exposure state to be something other than precapture.
88 | */
89 | static final int STATE_WAITING_NON_PRECAPTURE = 3;
90 |
91 | /**
92 | * Camera state: Picture was taken.
93 | */
94 | static final int STATE_PICTURE_TAKEN = 4;
95 |
96 | /**
97 | * Max preview width that is guaranteed by Camera2 API
98 | */
99 | static final int MAX_PREVIEW_WIDTH = 1920;
100 |
101 | /**
102 | * Max preview height that is guaranteed by Camera2 API
103 | */
104 | static final int MAX_PREVIEW_HEIGHT = 1080;
105 |
106 | /**
107 | * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
108 | * {@link TextureView}.
109 | */
110 | TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
111 | public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {openCamera(width, height);}
112 | public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {configureTransform(width, height);}
113 | public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {return true;}
114 | public void onSurfaceTextureUpdated(SurfaceTexture texture) {}
115 | };
116 |
117 | /**
118 | * ID of the current {@link CameraDevice}.
119 | */
120 | String mCameraId;
121 |
122 | /**
123 | * A {@link CameraCaptureSession } for camera preview.
124 | */
125 | CameraCaptureSession mCaptureSession;
126 |
127 | /**
128 | * A reference to the opened {@link CameraDevice}.
129 | */
130 | CameraDevice mCameraDevice;
131 |
132 | /**
133 | * The {@link Size} of camera preview.
134 | */
135 | Size mPreviewSize;
136 |
137 | /**
138 | * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
139 | */
140 | CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
141 |
142 | @Override
143 | public void onOpened(@NonNull CameraDevice cameraDevice) {
144 | // This method is called when the camera is opened. We start camera preview here.
145 | mCameraOpenCloseLock.release();
146 | mCameraDevice = cameraDevice;
147 | createCameraPreviewSession();
148 | }
149 |
150 | @Override
151 | public void onDisconnected(@NonNull CameraDevice cameraDevice) {
152 | mCameraOpenCloseLock.release();
153 | cameraDevice.close();
154 | mCameraDevice = null;
155 | }
156 |
157 | @Override
158 | public void onError(@NonNull CameraDevice cameraDevice, int error) {
159 | mCameraOpenCloseLock.release();
160 | cameraDevice.close();
161 | mCameraDevice = null;
162 | Toast.makeText(App.sApp, "相机开启失败,再试一次吧", Toast.LENGTH_LONG).show();
163 | mFinishCalled = true;
164 | finish();
165 | }
166 |
167 | };
168 |
169 | /**
170 | * An additional thread for running tasks that shouldn't block the UI.
171 | */
172 | HandlerThread mBackgroundThread;
173 |
174 | /**
175 | * A {@link Handler} for running tasks in the background.
176 | */
177 | Handler mBackgroundHandler;
178 |
179 | /**
180 | * An {@link ImageReader} that handles still image capture.
181 | */
182 | ImageReader mImageReader;
183 |
184 | /**
185 | * This is the output file for our picture.
186 | */
187 | File mFile;
188 |
189 | /**
190 | * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
191 | * still image is ready to be saved.
192 | */
193 | ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
194 | @Override
195 | public void onImageAvailable(ImageReader reader) {
196 | mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
197 | }
198 |
199 | };
200 |
201 | /**
202 | * {@link CaptureRequest.Builder} for the camera preview
203 | */
204 | CaptureRequest.Builder mPreviewRequestBuilder;
205 |
206 | /**
207 | * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
208 | */
209 | CaptureRequest mPreviewRequest;
210 |
211 | /**
212 | * The current state of camera state for taking pictures.
213 | *
214 | * @see #mCaptureCallback
215 | */
216 | int mState = STATE_PREVIEW;
217 |
218 | /**
219 | * A {@link Semaphore} to prevent the sApp from exiting before closing the camera.
220 | */
221 | Semaphore mCameraOpenCloseLock = new Semaphore(1);
222 |
223 | /**
224 | * Whether the current camera device supports Flash or not.
225 | */
226 | boolean mFlashSupported;
227 |
228 | /**
229 | * Orientation of the camera sensor
230 | */
231 | int mSensorOrientation;
232 |
233 | /**
234 | * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
235 | */
236 | CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
237 |
238 | void process(CaptureResult result) {
239 | switch (mState) {
240 | case STATE_PREVIEW: {
241 | // We have nothing to do when the camera preview is working normally.
242 | break;
243 | }
244 | case STATE_WAITING_LOCK: {
245 | Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
246 | /**
247 | * 判断可以立即拍摄的autoFocusState增加到4种.
248 | */
249 | if (afState == null) {
250 | captureStillPicture();
251 | } else if (CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState ||
252 | CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
253 | CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
254 | // CONTROL_AE_STATE can be null on some devices
255 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
256 | /**
257 | * 判断可以立即拍摄的autoExposureState增加到4种.
258 | */
259 | if (aeState == null ||
260 | aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
261 | aeState == CaptureResult.CONTROL_AE_STATE_LOCKED ||
262 | aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) {
263 | mState = STATE_PICTURE_TAKEN;
264 | captureStillPicture();
265 | } else {
266 | runPrecaptureSequence();
267 | }
268 | }
269 | break;
270 | }
271 | case STATE_WAITING_PRECAPTURE: {
272 | // CONTROL_AE_STATE can be null on some devices
273 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
274 | if (aeState == null ||
275 | aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
276 | aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
277 | mState = STATE_WAITING_NON_PRECAPTURE;
278 | }
279 | break;
280 | }
281 | case STATE_WAITING_NON_PRECAPTURE: {
282 | // CONTROL_AE_STATE can be null on some devices
283 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
284 | if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
285 | mState = STATE_PICTURE_TAKEN;
286 | captureStillPicture();
287 | }
288 | break;
289 | }
290 | }
291 | }
292 |
293 | @Override
294 | public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
295 | process(partialResult);
296 | }
297 |
298 | @Override
299 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
300 | process(result);
301 | }
302 |
303 | };
304 |
305 | /**
306 | * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that
307 | * is at least as large as the respective texture view size, and that is at most as large as the
308 | * respective max size, and whose aspect ratio matches with the specified value. If such size
309 | * doesn't exist, choose the largest one that is at most as large as the respective max size,
310 | * and whose aspect ratio matches with the specified value.
311 | *
312 | * @param choices The list of sizes that the camera supports for the intended output
313 | * class
314 | * @param textureViewWidth The width of the texture view relative to sensor coordinate
315 | * @param textureViewHeight The height of the texture view relative to sensor coordinate
316 | * @param maxWidth The maximum width that can be chosen
317 | * @param maxHeight The maximum height that can be chosen
318 | * @param aspectRatio The aspect ratio
319 | * @return The optimal {@code Size}, or an arbitrary one if none were big enough
320 | */
321 | static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
322 | int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
323 |
324 | // Collect the supported resolutions that are at least as big as the preview Surface
325 | List bigEnough = new ArrayList<>();
326 | // Collect the supported resolutions that are smaller than the preview Surface
327 | List notBigEnough = new ArrayList<>();
328 | int w = aspectRatio.getWidth();
329 | int h = aspectRatio.getHeight();
330 | double minRatio = ((double) w) / ((double) h) * 0.95;
331 | double maxRatio = ((double) w) / ((double) h) * 1.05;
332 | for (Size option : choices) {
333 | double ratio = ((double) option.getWidth()) / ((double) option.getHeight());
334 | if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
335 | /**
336 | * 现在允许宽高比相对于16:9有正负5%的误差.
337 | */
338 | ratio >= minRatio && ratio <= maxRatio) {
339 | if (option.getWidth() >= textureViewWidth && option.getHeight() >= textureViewHeight) {
340 | bigEnough.add(option);
341 | } else {
342 | notBigEnough.add(option);
343 | }
344 | }
345 | }
346 |
347 | // Pick the smallest of those big enough. If there is no one big enough, pick the
348 | // largest of those not big enough.
349 | if (bigEnough.size() > 0) {
350 | return Collections.min(bigEnough, new CompareSizesByArea());
351 | } else if (notBigEnough.size() > 0) {
352 | return Collections.max(notBigEnough, new CompareSizesByArea());
353 | } else {
354 | Log.e(TAG, "Couldn't find any suitable preview size");
355 | return choices[0];
356 | }
357 | }
358 |
359 | /**
360 | * Sets up member variables related to camera.
361 | *
362 | * @param width The width of available size for camera preview
363 | * @param height The height of available size for camera preview
364 | */
365 | @SuppressWarnings({"ConstantConditions", "SuspiciousNameCombination"})
366 | void setUpCameraOutputs(int width, int height) {
367 | CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
368 | try {
369 | for (String cameraId : manager.getCameraIdList()) {
370 | CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
371 |
372 | // We don't use a front facing camera in this sample.
373 | Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
374 | if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) continue;
375 |
376 | StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
377 | if (map == null) continue;
378 |
379 | // For still image captures, we use the largest available size.
380 | /*Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea());*/
381 | /**
382 | * 替换了寻找最大尺寸的算法.
383 | * 从OutputSizes中找到满足16:9比例,且像素数不超过3840*2160的最大Size.
384 | * 若找不到,则选择满足16:9比例的最大Size(像素数可能超过3840*2160),若仍找不到,返回最大Size。
385 | */
386 | Size largest = Camera2Utils.findBestSize(map.getOutputSizes(ImageFormat.JPEG), mMaxPicturePixels);
387 | mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, /*maxImages*/2);
388 | mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
389 |
390 | // Find out if we need to swap dimension to get the preview size relative to sensor
391 | // coordinate.
392 | int displayRotation = getWindowManager().getDefaultDisplay().getRotation();
393 | mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
394 | boolean swappedDimensions = false;
395 | switch (displayRotation) {
396 | case Surface.ROTATION_0:
397 | case Surface.ROTATION_180:
398 | if (mSensorOrientation == 90 || mSensorOrientation == 270) swappedDimensions = true;
399 | break;
400 | case Surface.ROTATION_90:
401 | case Surface.ROTATION_270:
402 | if (mSensorOrientation == 0 || mSensorOrientation == 180) swappedDimensions = true;
403 | break;
404 | default:
405 | Log.e(TAG, "Display rotation is invalid: " + displayRotation);
406 | }
407 |
408 | Point displaySize = new Point();
409 | getWindowManager().getDefaultDisplay().getSize(displaySize);
410 | int rotatedPreviewWidth = width;
411 | int rotatedPreviewHeight = height;
412 | int maxPreviewWidth = displaySize.x;
413 | int maxPreviewHeight = displaySize.y;
414 |
415 | if (swappedDimensions) {
416 | rotatedPreviewWidth = height;
417 | rotatedPreviewHeight = width;
418 | maxPreviewWidth = displaySize.y;
419 | maxPreviewHeight = displaySize.x;
420 | }
421 |
422 | if (maxPreviewWidth > MAX_PREVIEW_WIDTH) maxPreviewWidth = MAX_PREVIEW_WIDTH;
423 |
424 | if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) maxPreviewHeight = MAX_PREVIEW_HEIGHT;
425 |
426 | // Danger, W.R.! Attempting to use too large a preview size could exceed the camera
427 | // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
428 | // garbage capture data.
429 | mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
430 | rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
431 | maxPreviewHeight, largest);
432 |
433 | // Check if the flash is supported.
434 | Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
435 | mFlashSupported = available == null ? false : available;
436 |
437 | mCameraId = cameraId;
438 | return;
439 | }
440 | } catch (Exception e) {
441 | e.printStackTrace();
442 | }
443 | }
444 |
445 | /**
446 | * Opens the camera.
447 | */
448 | void openCamera(int width, int height) {
449 | setUpCameraOutputs(width, height);
450 | configureTransform(width, height);
451 | CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
452 | try {
453 | if (!mCameraOpenCloseLock.tryAcquire(4, TimeUnit.SECONDS)) throw new RuntimeException("Time out waiting to lock camera opening.");
454 | manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
455 | } catch (Exception e) {
456 | e.printStackTrace();
457 | Toast.makeText(App.sApp, "相机开启失败,再试一次吧", Toast.LENGTH_LONG).show();
458 | mFinishCalled = true;
459 | finish();
460 | }
461 | }
462 |
463 | /**
464 | * Closes the current {@link CameraDevice}.
465 | */
466 | void closeCamera() {
467 | try {
468 | mCameraOpenCloseLock.acquire();
469 | if (null != mCaptureSession) {
470 | mCaptureSession.close();
471 | mCaptureSession = null;
472 | }
473 | if (null != mCameraDevice) {
474 | mCameraDevice.close();
475 | mCameraDevice = null;
476 | }
477 | if (null != mImageReader) {
478 | mImageReader.close();
479 | mImageReader = null;
480 | }
481 | } catch (Exception e) {
482 | e.printStackTrace();
483 | } finally {
484 | mCameraOpenCloseLock.release();
485 | }
486 | }
487 |
488 | /**
489 | * Starts a background thread and its {@link Handler}.
490 | */
491 | void startBackgroundThread() {
492 | mBackgroundThread = new HandlerThread("CameraBackground");
493 | mBackgroundThread.start();
494 | mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
495 | }
496 |
497 | /**
498 | * Stops the background thread and its {@link Handler}.
499 | */
500 | void stopBackgroundThread() {
501 | mBackgroundThread.quitSafely();
502 | try {
503 | mBackgroundThread.join();
504 | mBackgroundThread = null;
505 | mBackgroundHandler = null;
506 | } catch (Exception e) {
507 | e.printStackTrace();
508 | }
509 | }
510 |
511 | /**
512 | * Creates a new {@link CameraCaptureSession} for camera preview.
513 | */
514 | void createCameraPreviewSession() {
515 | try {
516 | SurfaceTexture texture = mTextureView.getSurfaceTexture();
517 | assert texture != null;
518 |
519 | // We configure the size of default buffer to be the size of camera preview we want.
520 | texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
521 |
522 | // This is the output Surface we need to start preview.
523 | Surface surface = new Surface(texture);
524 |
525 | // We set up a CaptureRequest.Builder with the output Surface.
526 | mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
527 | mPreviewRequestBuilder.addTarget(surface);
528 |
529 | // Here, we create a CameraCaptureSession for camera preview.
530 | mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
531 | @Override
532 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
533 | // The camera is already closed
534 | if (null == mCameraDevice) return;
535 |
536 | // When the session is ready, we start displaying the preview.
537 | mCaptureSession = cameraCaptureSession;
538 | try {
539 | // Auto focus should be continuous for camera preview.
540 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
541 | // Flash is automatically enabled when necessary.
542 | setAutoFlash(mPreviewRequestBuilder);
543 |
544 | // Finally, we start displaying the camera preview.
545 | mPreviewRequest = mPreviewRequestBuilder.build();
546 | mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
547 | } catch (Exception e) {
548 | e.printStackTrace();
549 | Toast.makeText(App.sApp, "开启相机预览失败,再试一次吧", Toast.LENGTH_LONG).show();
550 | mFinishCalled = true;
551 | finish();
552 | }
553 | }
554 |
555 | @Override
556 | public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
557 | Toast.makeText(App.sApp, "开启相机预览失败,再试一次吧", Toast.LENGTH_LONG).show();
558 | mFinishCalled = true;
559 | finish();
560 | }
561 | }, null);
562 | } catch (Exception e) {
563 | e.printStackTrace();
564 | Toast.makeText(App.sApp, "开启相机预览失败,再试一次吧", Toast.LENGTH_LONG).show();
565 | mFinishCalled = true;
566 | finish();
567 | }
568 | }
569 |
570 | /**
571 | * Configures the necessary {@link Matrix} transformation to `mTextureView`.
572 | * This method should be called after the camera preview size is determined in
573 | * setUpCameraOutputs and also the size of `mTextureView` is fixed.
574 | *
575 | * @param viewWidth The width of `mTextureView`
576 | * @param viewHeight The height of `mTextureView`
577 | */
578 | void configureTransform(int viewWidth, int viewHeight) {
579 | if (null == mTextureView || null == mPreviewSize) return;
580 | int rotation = getWindowManager().getDefaultDisplay().getRotation();
581 | Matrix matrix = new Matrix();
582 | RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
583 | RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
584 | float centerX = viewRect.centerX();
585 | float centerY = viewRect.centerY();
586 | if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
587 | bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
588 | matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
589 | float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth());
590 | matrix.postScale(scale, scale, centerX, centerY);
591 | matrix.postRotate(90 * (rotation - 2), centerX, centerY);
592 | } else if (Surface.ROTATION_180 == rotation) {
593 | matrix.postRotate(180, centerX, centerY);
594 | }
595 | mTextureView.setTransform(matrix);
596 | }
597 |
598 | /**
599 | * Initiate a still image capture.
600 | */
601 | void takePicture() {
602 | lockFocus();
603 | }
604 |
605 | /**
606 | * Lock the focus as the first step for a still image capture.
607 | */
608 | void lockFocus() {
609 | try {
610 | // This is how to tell the camera to lock focus.
611 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
612 | // Tell #mCaptureCallback to wait for the lock.
613 | mState = STATE_WAITING_LOCK;
614 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
615 | } catch (Exception e) {
616 | e.printStackTrace();
617 | }
618 | }
619 |
620 | /**
621 | * Run the precapture sequence for capturing a still image. This method should be called when
622 | * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}.
623 | */
624 | void runPrecaptureSequence() {
625 | try {
626 | // This is how to tell the camera to trigger.
627 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
628 | // Tell #mCaptureCallback to wait for the precapture sequence to be set.
629 | mState = STATE_WAITING_PRECAPTURE;
630 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
631 | } catch (Exception e) {
632 | e.printStackTrace();
633 | }
634 | }
635 |
636 | /**
637 | * Capture a still picture. This method should be called when we get a response in
638 | * {@link #mCaptureCallback} from both {@link #lockFocus()}.
639 | */
640 | void captureStillPicture() {
641 | try {
642 | if (null == mCameraDevice) return;
643 | // This is the CaptureRequest.Builder that we use to take a picture.
644 | final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
645 | captureBuilder.addTarget(mImageReader.getSurface());
646 |
647 | // Use the same AE and AF modes as the preview.
648 | captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
649 | setAutoFlash(captureBuilder);
650 |
651 | // Orientation
652 | int rotation = getWindowManager().getDefaultDisplay().getRotation();
653 | captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
654 |
655 | CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() {
656 | @Override
657 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
658 | Log.d(TAG, mFile.toString());
659 | unlockFocus();
660 | }
661 | };
662 |
663 | mCaptureSession.stopRepeating();
664 | mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
665 | } catch (Exception e) {
666 | e.printStackTrace();
667 | }
668 | }
669 |
670 | /**
671 | * Retrieves the JPEG orientation from the specified screen rotation.
672 | *
673 | * @param rotation The screen rotation.
674 | * @return The JPEG orientation (one of 0, 90, 270, and 360)
675 | */
676 | int getOrientation(int rotation) {
677 | // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
678 | // We have to take that into account and rotate JPEG properly.
679 | // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS.
680 | // For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
681 | return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
682 | }
683 |
684 | /**
685 | * Unlock the focus. This method should be called when still image capture sequence is
686 | * finished.
687 | */
688 | void unlockFocus() {
689 | try {
690 | // Reset the auto-focus trigger
691 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
692 | setAutoFlash(mPreviewRequestBuilder);
693 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
694 | // After this, the camera will go back to the normal state of preview.
695 | mState = STATE_PREVIEW;
696 | mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
697 | } catch (Exception e) {
698 | e.printStackTrace();
699 | }
700 | }
701 |
702 | void setAutoFlash(CaptureRequest.Builder requestBuilder) {
703 | /**
704 | * 若相机支持自动开启/关闭闪光灯,则使用. 否则闪光灯总是关闭的.
705 | */
706 | if (mFlashSupported) requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
707 | }
708 |
709 | /**
710 | * Saves a JPEG {@link Image} into the specified {@link File}.
711 | */
712 | class ImageSaver implements Runnable {
713 |
714 | /**
715 | * The JPEG image
716 | */
717 | final Image mImage;
718 | /**
719 | * The file we save the image into.
720 | */
721 | final File mFile;
722 |
723 | ImageSaver(Image image, File file) {
724 | mImage = image;
725 | mFile = file;
726 | }
727 |
728 | @SuppressWarnings("ResultOfMethodCallIgnored")
729 | @Override
730 | public void run() {
731 | ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
732 | byte[] bytes = new byte[buffer.remaining()];
733 | buffer.get(bytes);
734 | try {
735 | if (mFile.exists()) mFile.delete();
736 | FileOutputStream output = new FileOutputStream(mFile);
737 | output.write(bytes);
738 | try {mImage.close();} catch (Exception ignored) {}
739 | try {output.close();} catch (Exception ignored) {}
740 | /**
741 | * 拍照完成后返回MainActivity.
742 | */
743 | App.mHandler.post(() -> {
744 | setResult(200, getIntent().putExtra("file", mFile.toString()));
745 | mFinishCalled = true;
746 | finish();
747 | });
748 | } catch (Exception e) {
749 | e.printStackTrace();
750 | }
751 | }
752 | }
753 |
754 | /**
755 | * Compares two {@code Size}s based on their areas.
756 | */
757 | static class CompareSizesByArea implements Comparator {
758 |
759 | @Override
760 | public int compare(Size lhs, Size rhs) {
761 | // We cast here to ensure the multiplications won't overflow
762 | return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
763 | }
764 |
765 | }
766 |
767 | @Override
768 | protected int getContentViewResId() {
769 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
770 | return R.layout.activity_camera2;
771 | }
772 |
773 | @Override
774 | protected void preInitData() {
775 | mFile = new File(getIntent().getStringExtra("file"));
776 | mTvCameraHint.setText(getIntent().getStringExtra("hint"));
777 | if (getIntent().getBooleanExtra("hideBounds", false)) {
778 | mViewDark0.setVisibility(View.INVISIBLE);
779 | mViewDark1.setVisibility(View.INVISIBLE);
780 | }
781 | mMaxPicturePixels = getIntent().getIntExtra("maxPicturePixels", 3840 * 2160);
782 | RxView.clicks(mIvCameraButton)
783 | /**
784 | * 防止手抖连续多次点击造成错误
785 | */
786 | .throttleFirst(2, TimeUnit.SECONDS)
787 | .observeOn(AndroidSchedulers.mainThread())
788 | .subscribe(aVoid -> takePicture());
789 | }
790 |
791 | @Override
792 | public void onResume() {
793 | super.onResume();
794 | startBackgroundThread();
795 |
796 | // When the screen is turned off and turned back on, the SurfaceTexture is already
797 | // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
798 | // a camera and start preview from here (otherwise, we wait until the surface is ready in
799 | // the SurfaceTextureListener).
800 | if (mTextureView.isAvailable()) {
801 | openCamera(mTextureView.getWidth(), mTextureView.getHeight());
802 | } else {
803 | mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
804 | }
805 | }
806 |
807 | @Override
808 | public void onBackPressed() {
809 | mFinishCalled = true;
810 | finish();
811 | }
812 |
813 | @Override
814 | protected void onPause() {
815 | super.onPause();
816 | closeCamera();
817 | stopBackgroundThread();
818 | if (!mFinishCalled) finish();
819 | }
820 | }
--------------------------------------------------------------------------------