編輯:關於Android編程
Android 5.0(21)之後,android.hardware.Camera被廢棄(下面稱為Camera1),還有一個android.graphics.Camera,這個android.graphics.Camera不是用來照相的,是用來處理圖像的,可以做出3D的圖像效果之類的,之前的Camera1則由android.hardware.Camera2來代替
Camera2支持RAW輸出,可以調節曝光,對焦模式,快門等,功能比原先Camera強大
使用步驟:
這幾個步驟從瘋狂Android講義中學到
使用SurfaceView進行取景的預覽,點擊屏幕進行拍照,用ImageView來展示拍的照片
想買關於操作系統和C的書看,知乎很多人推薦這兩本,就買了,感覺確實不錯
布局文件:
Activity代碼:
public class CameraActivity extends AppCompatActivity implements View.OnClickListener { private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private Camera mCamera; private ImageView iv_show; private int viewWidth, viewHeight;//mSurfaceView的寬和高 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_camera2); initView(); } /** * 初始化控件 */ private void initView() { iv_show = (ImageView) findViewById(R.id.iv_show_camera2_activity); //mSurfaceView mSurfaceView = (SurfaceView) findViewById(R.id.surface_view_camera2_activity); mSurfaceHolder = mSurfaceView.getHolder(); // mSurfaceView 不需要自己的緩沖區 mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // mSurfaceView添加回調 mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { //SurfaceView創建 // 初始化Camera initCamera(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView銷毀 // 釋放Camera資源 if (mCamera != null) { mCamera.stopPreview(); mCamera.release(); } } }); //設置點擊監聽 mSurfaceView.setOnClickListener(this); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (mSurfaceView != null) { viewWidth = mSurfaceView.getWidth(); viewHeight = mSurfaceView.getHeight(); } } /** * SurfaceHolder 回調接口方法 */ private void initCamera() { mCamera = Camera.open();//默認開啟後置 mCamera.setDisplayOrientation(90);//攝像頭進行旋轉90° if (mCamera != null) { try { Camera.Parameters parameters = mCamera.getParameters(); //設置預覽照片的大小 parameters.setPreviewFpsRange(viewWidth, viewHeight); //設置相機預覽照片幀數 parameters.setPreviewFpsRange(4, 10); //設置圖片格式 parameters.setPictureFormat(ImageFormat.JPEG); //設置圖片的質量 parameters.set("jpeg-quality", 90); //設置照片的大小 parameters.setPictureSize(viewWidth, viewHeight); //通過SurfaceView顯示預覽 mCamera.setPreviewDisplay(mSurfaceHolder); //開始預覽 mCamera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } } /** * 點擊回調方法 */ @Override public void onClick(View v) { if (mCamera == null) return; //自動對焦後拍照 mCamera.autoFocus(autoFocusCallback); } /** * 自動對焦 對焦成功後 就進行拍照 */ Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { if (success) {//對焦成功 camera.takePicture(new Camera.ShutterCallback() {//按下快門 @Override public void onShutter() { //按下快門瞬間的操作 } }, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) {//是否保存原始圖片的信息 } }, pictureCallback); } } }; /** * 獲取圖片 */ Camera.PictureCallback pictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { final Bitmap resource = BitmapFactory.decodeByteArray(data, 0, data.length); if (resource == null) { Toast.makeText(CameraActivity.this, "拍照失敗", Toast.LENGTH_SHORT).show(); } final Matrix matrix = new Matrix(); matrix.setRotate(90); final Bitmap bitmap = Bitmap.createBitmap(resource, 0, 0, resource.getWidth(), resource.getHeight(), matrix, true); if (bitmap != null && iv_show != null && iv_show.getVisibility() == View.GONE) { mCamera.stopPreview(); iv_show.setVisibility(View.VISIBLE); mSurfaceView.setVisibility(View.GONE); Toast.makeText(CameraActivity.this, "拍照", Toast.LENGTH_SHORT).show(); iv_show.setImageBitmap(bitmap); } } }; }
權限:
在獲得圖片後,想要顯示的效果是照片是豎直顯示,resource顯示的卻是逆時針旋轉了90°,照片是橫著的,就使用matrix.setRotate(90)進行旋轉
這裡引用了管道的概念將安卓設備和攝像頭之間聯通起來,系統向攝像頭發送 Capture 請求,而攝像頭會返回 CameraMetadata。這一切建立在一個叫作 CameraCaptureSession 的會話中。
CameraManaer 攝像頭管理器,用於檢測攝像頭,打開系統攝像頭,調用CameraManager.getCameraCharacteristics(String)可以獲取指定攝像頭的相關特性CameraCharacteristics 攝像頭的特性CameraDevice 攝像頭,類似android.hardware.Camera也就是Camera1的CameraCameraCaptureSession 這個對象控制攝像頭的預覽或者拍照,setRepeatingRequest()開啟預覽,capture()拍照,CameraCaptureSession提供了StateCallback、CaptureCallback兩個接口來監聽CameraCaptureSession的創建和拍照過程。CameraRequest和CameraRequest.Builder,預覽或者拍照時,都需要一個CameraRequest對象。CameraRequest表示一次捕獲請求,用來對z照片的各種參數設置,比如對焦模式、曝光模式等。CameraRequest.Builder用來生成CameraRequest對象。以上從極客學院android.hardware.camera2 使用指南摘抄
以上從腎虛將軍的android camera2 詳解說明摘抄
使用的依然是SurfaceView來進行展示預覽
主要思路:
獲得攝像頭管理器CameraManager mCameraManager,mCameraManager.openCamera()來打開攝像頭指定要打開的攝像頭,並創建openCamera()所需要的CameraDevice.StateCallback stateCallback在CameraDevice.StateCallback stateCallback中調用takePreview(),這個方法中,使用CaptureRequest.Builder創建預覽需要的CameraRequest,並初始化了CameraCaptureSession,最後調用了setRepeatingRequest(previewRequest, null, childHandler)進行了預覽點擊屏幕,調用takePicture(),這個方法內,最終調用了capture(mCaptureRequest, null, childHandler)在new ImageReader.OnImageAvailableListener(){}回調方法中,將拍照拿到的圖片進行展示代碼:
public class Camera2Activity extends AppCompatActivity implements View.OnClickListener { private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); ///為了使照片豎直顯示 static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private ImageView iv_show; private CameraManager mCameraManager;//攝像頭管理器 private Handler childHandler, mainHandler; private String mCameraID;//攝像頭Id 0 為後 1 為前 private ImageReader mImageReader; private CameraCaptureSession mCameraCaptureSession; private CameraDevice mCameraDevice; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_camera2); initVIew(); } /** * 初始化 */ private void initVIew() { iv_show = (ImageView) findViewById(R.id.iv_show_camera2_activity); //mSurfaceView mSurfaceView = (SurfaceView) findViewById(R.id.surface_view_camera2_activity); mSurfaceView.setOnClickListener(this); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.setKeepScreenOn(true); // mSurfaceView添加回調 mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { //SurfaceView創建 // 初始化Camera initCamera2(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView銷毀 // 釋放Camera資源 if (null != mCameraDevice) { mCameraDevice.close(); Camera2Activity.this.mCameraDevice = null; } } }); } /** * 初始化Camera2 */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void initCamera2() { HandlerThread handlerThread = new HandlerThread("Camera2"); handlerThread.start(); childHandler = new Handler(handlerThread.getLooper()); mainHandler = new Handler(getMainLooper()); mCameraID = "" + CameraCharacteristics.LENS_FACING_FRONT;//後攝像頭 mImageReader = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG,1); mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在這裡處理拍照得到的臨時照片 例如,寫入本地 @Override public void onImageAvailable(ImageReader reader) { mCameraDevice.close(); mSurfaceView.setVisibility(View.GONE); iv_show.setVisibility(View.VISIBLE); // 拿到拍照照片數據 Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes);//由緩沖區存入字節數組 final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); if (bitmap != null) { iv_show.setImageBitmap(bitmap); } } }, mainHandler); //獲取攝像頭管理 mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } //打開攝像頭 mCameraManager.openCamera(mCameraID, stateCallback, mainHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 攝像頭創建監聽 */ private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) {//打開攝像頭 mCameraDevice = camera; //開啟預覽 takePreview(); } @Override public void onDisconnected(CameraDevice camera) {//關閉攝像頭 if (null != mCameraDevice) { mCameraDevice.close(); Camera2Activity.this.mCameraDevice = null; } } @Override public void onError(CameraDevice camera, int error) {//發生錯誤 Toast.makeText(Camera2Activity.this, "攝像頭開啟失敗", Toast.LENGTH_SHORT).show(); } }; /** * 開始預覽 */ private void takePreview() { try { // 創建預覽需要的CaptureRequest.Builder final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 將SurfaceView的surface作為CaptureRequest.Builder的目標 previewRequestBuilder.addTarget(mSurfaceHolder.getSurface()); // 創建CameraCaptureSession,該對象負責管理處理預覽請求和拍照請求 mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③ { @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { if (null == mCameraDevice) return; // 當攝像頭已經准備好時,開始顯示預覽 mCameraCaptureSession = cameraCaptureSession; try { // 自動對焦 previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 打開閃光燈 previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 顯示預覽 CaptureRequest previewRequest = previewRequestBuilder.build(); mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { Toast.makeText(Camera2Activity.this, "配置失敗", Toast.LENGTH_SHORT).show(); } }, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 點擊事件 */ @Override public void onClick(View v) { takePicture(); } /** * 拍照 */ private void takePicture() { if (mCameraDevice == null) return; // 創建拍照需要的CaptureRequest.Builder final CaptureRequest.Builder captureRequestBuilder; try { captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 將imageReader的surface作為CaptureRequest.Builder的目標 captureRequestBuilder.addTarget(mImageReader.getSurface()); // 自動對焦 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 自動曝光 captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 獲取手機方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根據設備方向計算設置照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); //拍照 CaptureRequest mCaptureRequest = captureRequestBuilder.build(); mCameraCaptureSession.capture(mCaptureRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } }
布局代碼以及權限與Camera1中一樣,效果一樣
預覽時,是將mSurfaceHolder.getSurface()作為目標
顯示拍照結果時,是將mImageReader.getSurface()作為目標
Camera2的功能很強大,暫時也只是學習了最基本的思路
住的地方,沒有桌子,於是坐地上,趴在床上敲代碼,腰疼。逛淘寶買桌子去
感謝極客學院和腎虛將軍的學習資料
承香墨影Android--ColorMatrix改變圖片顏色 前言 本篇博客講解如何通過改變圖片像素點RGB的值的方式,在Android中改變圖片
我們通常在TextView文本中設置文字。可是如何設置圖文混排呢?我就在這裡寫一個例子 。我們需要用到一點簡單的HTML知識在TextView中預訂了一些類似HTML的標
[java] view plaincopyprint? //序列化 intent.toURI(); //反序列 化使用: Inte
近期因為項目需要,點擊系統設置-》語言和輸入法-》選擇語言-》應用內語言跟著切換的實現,廢話不多說,直接接入主題以我的為例:我的需求是實現簡體中文,和繁體字的切換1.你需