乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      android.hardware.camera2詳解(實(shí)時(shí)更新,未完待續(xù)...)

       lifei_szdz 2019-02-22

      看到有些讀者對(duì)博客提出的疑問(wèn),在此真誠(chéng)說(shuō)一句實(shí)在抱歉,本人是一名在校非科班出身大學(xué)生,之前 由于是期末季,所以非常忙,沒(méi)有空更新,一月二十幾號(hào)才放假,非常抱歉,評(píng)論所訴內(nèi)容應(yīng)該都已解決,只是我沒(méi)有更新,近期將會(huì)更新!再次致歉!

      前言:原有的android.hardware.camera已經(jīng)被google廢除,改用功能更強(qiáng)大的camera2,camera2的復(fù)雜度遠(yuǎn)遠(yuǎn)超過(guò)camera,所以寫(xiě)這篇博客之前也是經(jīng)歷了各種心態(tài)爆炸。非常費(fèi)解,谷歌官網(wǎng)竟然沒(méi)有給出一個(gè)好的demo,導(dǎo)致我只能去網(wǎng)上各處搜博客,其間大多數(shù)博客真的讓我惱火,不是寫(xiě)的有問(wèn)題,就是介紹不全,還有就是完全不解釋?zhuān)液苡憛掃@種不負(fù)責(zé)的博客,對(duì)我來(lái)說(shuō),簡(jiǎn)直就是扯淡!(實(shí)在忍不住,所以爆了點(diǎn)粗口,望諒解)。出于以上原因,我會(huì)盡我最大努力,寫(xiě)出一篇邏輯清晰,易于理解,代碼完整的博客。

      一,讓我們先來(lái)了解一下其大致原理和使用流程
      先看兩張圖:

      可以看到圖中有個(gè)pipeline,這是camera2引入的概念,在我看來(lái),這個(gè)單詞給定的很恰當(dāng)。通過(guò)在Android Device(代碼中的一個(gè)對(duì)象CameraDevice)和Camera Device(照相的硬件設(shè)備)搭建一個(gè)通道,來(lái)進(jìn)行兩者信息的交流,Android Device向Camera Device發(fā)出請(qǐng)求——CaptureRequest,這個(gè)CaptureRequest可以設(shè)置各種我們想要設(shè)置的相機(jī)屬性——自動(dòng)聚焦,閃光燈等等,而Camera Device拍攝對(duì)應(yīng)影像,收集圖像信息,通過(guò)CameraMatadata傳遞給Android Device,從而使Android程序端可以獲取到照片數(shù)據(jù),進(jìn)行對(duì)應(yīng)的處理。
      另外,我們還注意到的上面有個(gè)箭頭指向Surface,Android端將Camera Device傳遞過(guò)來(lái)的圖像信息投射到Surface上,實(shí)現(xiàn)預(yù)覽的功能。

      這張圖展示各個(gè)類(lèi)之間的關(guān)系,讓我們來(lái)分別介紹一下:

      CameraManager:管理Camera的類(lèi),通過(guò)這個(gè)類(lèi)我們可以打開(kāi)Camera——對(duì)應(yīng)方法為CameraManager.openCamera;獲取各種Camera參數(shù)——對(duì)應(yīng)方法為CameraManager.getCameraCharacteristics。這里我們先大概了解就行,稍后都會(huì)介紹。
      其中打開(kāi)一個(gè)Camera時(shí),會(huì)有對(duì)應(yīng)的回調(diào)接口——CameraDevice.StateCallback,處理相機(jī)打開(kāi)失敗,成功后者錯(cuò)誤的情況。

      CameraCharacteristics:含有Camera的各種參數(shù),如閃光燈,自動(dòng)對(duì)焦,自動(dòng)曝光等等。

      CameraDevice:java代碼中代表Camera的對(duì)象,可以關(guān)閉相機(jī),向相機(jī)硬件端發(fā)出請(qǐng)求等等。

      CameraCaptureSession:session直譯為”會(huì)議“,這是一個(gè)很形象的詞,其實(shí)就是上文提到的pipeline,程序中通過(guò)創(chuàng)造一個(gè)CameraCaptureSession,在安卓端和相機(jī)硬件端建立管道,從而可以獲取拍攝的圖片信息。
      在創(chuàng)造一個(gè)會(huì)議時(shí),會(huì)回調(diào)兩個(gè)接口——
      StateCallback:處理session建立成功和失敗的情況,通常在這里會(huì)進(jìn)行預(yù)覽的一些初始化設(shè)置。
      CaptureCallback:捕獲圖像成功、失敗、進(jìn)行時(shí)等情況的處理。

      CameraRequest:安卓端相機(jī)參數(shù)的設(shè)定請(qǐng)求,會(huì)在創(chuàng)建session時(shí)被當(dāng)作參數(shù)。

      CameraMetadata:控制相機(jī)和帶有相機(jī)參數(shù)的基礎(chǔ)類(lèi),它的子類(lèi)是:
      CameraCharacteristics,CaptureRequest,CaptureResult。

      CaptureResult:從圖像傳感器捕獲單個(gè)圖像的結(jié)果的子集。包含捕獲硬件(傳感器,鏡頭,閃存),處理流水線(xiàn),控制算法和輸出緩沖區(qū)的最終配置的一個(gè)子集。概念有點(diǎn)難懂,可以去官網(wǎng)仔細(xì)了解下。


      這張圖很重要,大家要仔細(xì)看看,稍后的代碼的邏輯結(jié)構(gòu)就是依照這個(gè)而來(lái)的,不一定要全部看懂,可以結(jié)合下面的代碼流程去理解。
      有了這些基礎(chǔ)知識(shí)儲(chǔ)備,讓我們來(lái)打造一個(gè)最簡(jiǎn)單的android.hardware.camera2 demo吧!
      二,最簡(jiǎn)單的camera2 demo

      (一)添加權(quán)限,以及部分依賴(lài)
      在app/gradle中添加依賴(lài):

       compile 'de.hdodenhof:circleimageview:2.1.0'

      這是一個(gè)具有將任何形狀圖片變?yōu)閳A形功能的開(kāi)源View,圖片中左下角的圓形圖片就是調(diào)用了這個(gè)控件。
      在AndroidManifest.xml添加相關(guān)權(quán)限:

          <uses-permission android:name="android.permission.CAMERA" />
          <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
          <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

      這里的讀取和讀入功能以后擴(kuò)充的代碼要用到。
      (二)為你的相機(jī)創(chuàng)建個(gè)性布局
      activity_main.xml

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout
          xmlns:android="http://schemas./apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical"
      >
          <SurfaceView
              android:id="@+id/mFirstSurfaceView"
              android:layout_width="match_parent"
              android:layout_height="0dp"
              android:layout_weight="1"
              />
          <RelativeLayout
              android:layout_width="match_parent"
              android:layout_height="100dp"
              android:background="#0E0E0E">
              <!--提供預(yù)覽功能,以及從相冊(cè)選取圖片進(jìn)行識(shí)別功能-->
              <de.hdodenhof.circleimageview.CircleImageView
                  android:id="@+id/img_show"
                  android:layout_width="60dp"
                  android:layout_height="60dp"
                  android:layout_alignParentStart="true"
                  android:layout_alignParentBottom="true"
                  android:src="@mipmap/ic_launcher"
                  android:layout_marginStart="10dp"/>
              <Button
                  android:id="@+id/take_picture"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_centerInParent="true"
                  android:layout_alignParentBottom="true"
                  android:text="拍照"/>
              <!--點(diǎn)擊觀看識(shí)別結(jié)果-->
              <Button
                  android:id="@+id/recognition_result"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_alignParentEnd="true"
                  android:layout_alignParentBottom="true"
                  android:text="結(jié)果"
                  android:layout_marginEnd="10dp"/>
          </RelativeLayout>
      </LinearLayout>

      其中SurfaceView是用來(lái)預(yù)覽拍攝圖片的。CircleImageView是我們第一步提到的開(kāi)源庫(kù)。
      (三)在MainActivity中實(shí)現(xiàn)相關(guān)功能
      老規(guī)矩,先貼整體代碼:
      MainActivity.class

      package com.example.wordrecognition;
      
      import android.annotation.TargetApi;
      import android.content.Context;
      import android.content.pm.PackageManager;
      import android.graphics.Bitmap;
      import android.graphics.BitmapFactory;
      import android.graphics.Camera;
      import android.graphics.ImageFormat;
      import android.hardware.camera2.*;
      import android.media.Image;
      import android.media.ImageReader;
      import android.os.Handler;
      import android.os.HandlerThread;
      import android.support.annotation.IntDef;
      import android.support.annotation.NonNull;
      import android.support.v4.app.ActivityCompat;
      import android.support.v7.app.ActionBar;
      import android.support.v7.app.AppCompatActivity;
      import android.os.Bundle;
      import android.view.Surface;
      import android.view.SurfaceHolder;
      import android.view.SurfaceView;
      import android.view.View;
      import android.widget.Button;
      import android.widget.ImageView;
      import android.widget.Toast;
      import android.Manifest.permission.*;
      
      import java.nio.ByteBuffer;
      import java.util.Arrays;
      import java.util.jar.Manifest;
      
      public class MainActivity extends AppCompatActivity {
          private CameraManager mCameraManager;
          private SurfaceView mSurfaceView;
          private SurfaceHolder mSurfaceViewHolder;
          private Handler mHandler;
          private String mCameraId;
          private ImageReader mImageReader;
          private CameraDevice mCameraDevice;
          private CaptureRequest.Builder mPreviewBuilder;
          private CameraCaptureSession mSession;
          private ImageView img_show;
          private Button take_picture_bt;
          private Handler mainHandler;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              ActionBar actionBar=getSupportActionBar();
              if(actionBar!=null){
                  actionBar.hide();
              }
              img_show= (ImageView) findViewById(R.id.img_show);
              take_picture_bt=(Button)findViewById(R.id.take_picture);
              initSurfaceView();//初始化SurfaceView
              /**
               * 拍照按鈕監(jiān)聽(tīng)
               */
              take_picture_bt.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View view) {
                      takePicture();
                  }
              });
      
          }
      public void initSurfaceView(){
          mSurfaceView = (SurfaceView) findViewById(R.id.mFirstSurfaceView);
          mSurfaceViewHolder = mSurfaceView.getHolder();//通過(guò)SurfaceViewHolder可以對(duì)SurfaceView進(jìn)行管理
          mSurfaceViewHolder.addCallback(new SurfaceHolder.Callback() {
              @Override
              public void surfaceCreated(SurfaceHolder holder) {
                  initCameraAndPreview();
              }
      
              @Override
              public void surfaceDestroyed(SurfaceHolder holder) {
                  //釋放camera
                  if (mCameraDevice != null) {
                      mCameraDevice.close();
                      mCameraDevice = null;
                  }
              }
      
              @Override
              public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
      
              }
          });
      }
          @TargetApi(19)
          public void initCameraAndPreview() {
              HandlerThread handlerThread = new HandlerThread("My First Camera2");
              handlerThread.start();
              mHandler = new Handler(handlerThread.getLooper());
              mainHandler = new Handler(getMainLooper());//用來(lái)處理ui線(xiàn)程的handler,即ui線(xiàn)程
              try {
                  mCameraId = "" + CameraCharacteristics.LENS_FACING_FRONT;
                  mImageReader = ImageReader.newInstance(mSurfaceView.getWidth(), mSurfaceView.getHeight(), ImageFormat.JPEG,/*maxImages*/7);
                  mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mainHandler);//這里必須傳入mainHandler,因?yàn)樯婕暗搅薝i操作
                  mCameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
                  if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                      return;//按理說(shuō)這里應(yīng)該有一個(gè)申請(qǐng)權(quán)限的過(guò)程,但為了使程序盡可能最簡(jiǎn)化,所以先不添加
                  }
                  mCameraManager.openCamera(mCameraId, deviceStateCallback, mHandler);
              } catch (CameraAccessException e) {
                  Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
              }
          }
      
          private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
              @Override
              public void onImageAvailable(ImageReader reader) {
                  //進(jìn)行相片存儲(chǔ)
                  mCameraDevice.close();
                  Image image = reader.acquireNextImage();
                  ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                  byte[] bytes = new byte[buffer.remaining()];
                  buffer.get(bytes);//將image對(duì)象轉(zhuǎn)化為byte,再轉(zhuǎn)化為bitmap
                  final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                  if (bitmap != null) {
                     img_show.setImageBitmap(bitmap);
                  }
              }
          };
          private CameraDevice.StateCallback deviceStateCallback = new CameraDevice.StateCallback() {
              @Override
              public void onOpened(CameraDevice camera) {
                  mCameraDevice = camera;
                  try {
                      takePreview();
                  } catch (CameraAccessException e) {
                      e.printStackTrace();
                  }
              }
      
              @Override
              public void onDisconnected(@NonNull CameraDevice camera) {
                  if (mCameraDevice != null) {
                      mCameraDevice.close();
                      mCameraDevice = null;
                  }
              }
      
              @Override
              public void onError(CameraDevice camera, int error) {
                  Toast.makeText(MainActivity.this, "打開(kāi)攝像頭失敗", Toast.LENGTH_SHORT).show();
              }
          };
      
          public void takePreview() throws CameraAccessException {
              mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
              mPreviewBuilder.addTarget(mSurfaceViewHolder.getSurface());
              mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceViewHolder.getSurface(), mImageReader.getSurface()), mSessionPreviewStateCallback, mHandler);
          }
      
          private CameraCaptureSession.StateCallback mSessionPreviewStateCallback = new CameraCaptureSession.StateCallback() {
              @Override
              public void onConfigured(@NonNull CameraCaptureSession session) {
                  mSession = session;
                  //配置完畢開(kāi)始預(yù)覽
                  try {
                      /**
                       * 設(shè)置你需要配置的參數(shù)
                       */
                      //自動(dòng)對(duì)焦
                      mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                      //打開(kāi)閃光燈
                      mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                      //無(wú)限次的重復(fù)獲取圖像
                      mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
                  } catch (CameraAccessException e) {
                      e.printStackTrace();
                  }
              }
      
              @Override
              public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                  Toast.makeText(MainActivity.this, "配置失敗", Toast.LENGTH_SHORT).show();
              }
          };
          private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
              @Override
              public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                  mSession = session;
              }
      
              @Override
              public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
                  mSession = session;
              }
      
               @Override
              public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                  super.onCaptureFailed(session, request, failure);
              }
          };
      
      
          public void takePicture() {
              try {
                  CaptureRequest.Builder captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);//用來(lái)設(shè)置拍照請(qǐng)求的request
                  captureRequestBuilder.addTarget(mImageReader.getSurface());
                  // 自動(dòng)對(duì)焦
                  captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                  // 自動(dòng)曝光
                  captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                  int rotation = getWindowManager().getDefaultDisplay().getRotation();
                  CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
                  captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getJpegOrientation(cameraCharacteristics, rotation));//使圖片做順時(shí)針旋轉(zhuǎn)
                  CaptureRequest mCaptureRequest = captureRequestBuilder.build();
                  mSession.capture(mCaptureRequest, null, mHandler);
              } catch (CameraAccessException e) {
                  e.printStackTrace();
              }
          }
      //獲取圖片應(yīng)該旋轉(zhuǎn)的角度,使圖片豎直
          public int getOrientation(int rotation) {
              switch (rotation) {
                  case Surface.ROTATION_0:
                      return 90;
                  case Surface.ROTATION_90:
                      return 0;
                  case Surface.ROTATION_180:
                      return 270;
                  case Surface.ROTATION_270:
                      return 180;
                  default:
                      return 0;
              }
          }
          //獲取圖片應(yīng)該旋轉(zhuǎn)的角度,使圖片豎直
          private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) {
              if (deviceOrientation == android.view.OrientationEventListener.ORIENTATION_UNKNOWN)
                  return 0;
              int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
      
              // Round device orientation to a multiple of 90
              deviceOrientation = (deviceOrientation + 45) / 90 * 90;
      
              // LENS_FACING相對(duì)于設(shè)備屏幕的方向,LENS_FACING_FRONT相機(jī)設(shè)備面向與設(shè)備屏幕相同的方向
              boolean facingFront = c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT;
              if (facingFront) deviceOrientation = -deviceOrientation;
      
              // Calculate desired JPEG orientation relative to camera orientation to make
              // the image upright relative to the device orientation
              int jpegOrientation = (sensorOrientation + deviceOrientation + 360) % 360;
      
              return jpegOrientation;
          }
      
      }
      

      代碼有點(diǎn)長(zhǎng),下面我們來(lái)分解它的邏輯
      1,隱藏ActionBar等以及基本部件初始化操作

      ActionBar actionBar=getSupportActionBar();
              if(actionBar!=null){
                  actionBar.hide();
              }
              img_show= (ImageView) findViewById(R.id.img_show);
              take_picture_bt=(Button)findViewById(R.id.take_picture);

      2,初始化surfaceView,即照相機(jī)預(yù)覽界面

           mSurfaceView = (SurfaceView) findViewById(R.id.mFirstSurfaceView);
          mSurfaceViewHolder = mSurfaceView.getHolder();//通過(guò)SurfaceViewHolder可以對(duì)SurfaceView進(jìn)行管理
          mSurfaceViewHolder.addCallback(new SurfaceHolder.Callback() {
          //SurfaceView被成功創(chuàng)建
              @Override
              public void surfaceCreated(SurfaceHolder holder) {
                  initCameraAndPreview();
              }
      //SurfaceView被銷(xiāo)毀
              @Override
              public void surfaceDestroyed(SurfaceHolder holder) {
                  //釋放camera
                  if (mCameraDevice != null) {
                      mCameraDevice.close();
                      mCameraDevice = null;
                  }
              }
      //SurfaceView內(nèi)容發(fā)生改變
              @Override
              public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
      
              }
          });

      首先我們獲取到了SurfaceView實(shí)例,然后通過(guò)SurfaceView獲取SurfaceViewHolder,SurfaceViewHolder是用來(lái)管理SurfaceView的類(lèi),別的類(lèi)通過(guò)SurfaceViewHolder可以對(duì)SurfaceView進(jìn)行編輯。
      然后我們通過(guò)SurfaceViewHolder為SurfaceView添加回調(diào)接口,三個(gè)回調(diào)接口方法的功能注釋已經(jīng)解釋了。在第二個(gè)方法中,釋放了Camera資源。
      而在第一個(gè)方法中,SurfaceView被成功創(chuàng)建后,我們緊接著調(diào)用initCameraAndPreView,初始化Camera和將相機(jī)拍攝界面投射到SurfaceView上。
      3,初始化相機(jī)和實(shí)現(xiàn)相機(jī)預(yù)覽功能

      @TargetApi(19)
          public void initCameraAndPreview() {
              HandlerThread handlerThread = new HandlerThread("My First Camera2");
              handlerThread.start();
              mHandler = new Handler(handlerThread.getLooper());
              mainHandler = new Handler(getMainLooper());//用來(lái)處理ui線(xiàn)程的handler,即ui線(xiàn)程
              try {
                  mCameraId = "" + CameraCharacteristics.LENS_FACING_FRONT;
                  mImageReader = ImageReader.newInstance(mSurfaceView.getWidth(), mSurfaceView.getHeight(), ImageFormat.JPEG,/*maxImages*/7);                                mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mainHandler);//這里必須傳入mainHandler,因?yàn)樯婕暗搅薝i操作
                  mCameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
                  if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                      return;//按理說(shuō)這里應(yīng)該有一個(gè)申請(qǐng)權(quán)限的過(guò)程,但為了使程序盡可能最簡(jiǎn)化,所以先不添加
                  }
                  mCameraManager.openCamera(mCameraId, deviceStateCallback, mHandler);
              } catch (CameraAccessException e) {
                  Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
              }
          }
           private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
              @Override
              public void onImageAvailable(ImageReader reader) {
                  //進(jìn)行相片存儲(chǔ)
                  mCameraDevice.close();
                  //mSurfaceView.setVisibility(View.GONE);//存疑
                  Image image = reader.acquireNextImage();
                  ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                  byte[] bytes = new byte[buffer.remaining()];
                  buffer.get(bytes);//將image對(duì)象轉(zhuǎn)化為byte,再轉(zhuǎn)化為bitmap
                  final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                  if (bitmap != null) {
                     img_show.setImageBitmap(bitmap);
                  }
              }
          };

      在這步中首先我們初始化了兩個(gè)線(xiàn)程:

              HandlerThread handlerThread = new HandlerThread("My First Camera2");
              handlerThread.start();
              mHandler = new Handler(handlerThread.getLooper());
              mainHandler = new Handler(getMainLooper());//用來(lái)處理ui線(xiàn)程的handler,即ui線(xiàn)程

      關(guān)于HandlerThread不熟悉的讀者可以看下鴻洋大神的博客——HandlerThread.
      mHandler用來(lái)處理普通線(xiàn)程,mainHandler用來(lái)處理主線(xiàn)程——ui線(xiàn)程。

      然后我們?cè)O(shè)置了CameraId:
      前置攝像頭:LENS_FACING_BACK,名字不要弄反了。
      后置攝像頭:LENS_FACING_FRONT。

      設(shè)置ImageReader,用來(lái)讀取拍攝圖像的類(lèi),ImageReader.newInstance方法的原型:

      ImageReader.newInstance(int width,int height,int format,int maxImages);
      其它參數(shù)好理解,maxImages代表用戶(hù)想讀取的最大Image對(duì)象數(shù)量。

      然后我們?yōu)镮mageReader設(shè)置了監(jiān)聽(tīng)接口:

      mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mainHandler);//這里必須傳入mainHandler,因?yàn)樯婕暗搅薝i操作

      mOnImageAvailiableListener對(duì)象如下:

       private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
              @Override
              public void onImageAvailable(ImageReader reader) {
                  //進(jìn)行相片存儲(chǔ)和展示
                  mCameraDevice.close();
                  Image image = reader.acquireNextImage();
                  ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                  byte[] bytes = new byte[buffer.remaining()];
                  buffer.get(bytes);//將image對(duì)象轉(zhuǎn)化為byte,再轉(zhuǎn)化為bitmap
                  final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                  if (bitmap != null) {
                     img_show.setImageBitmap(bitmap);
                  }
              }
          };

      這個(gè)接口在圖片拍攝完成后就會(huì)回調(diào),這個(gè)方法中我們關(guān)閉了相機(jī),并進(jìn)行了圖片的讀取,其中buffer.remaining返回buffer的元素?cái)?shù)量,image.getPlanes()代表獲取圖片像素信息組成的數(shù)組。
      同時(shí)還要注意到,我們?cè)O(shè)置監(jiān)聽(tīng)接口時(shí)還傳入了mainHandler,這代表我們可以在回調(diào)方法中進(jìn)行ui操作。

      緊接著獲取CameraManager,并打開(kāi)了Camera:

      mCameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
                  if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                      return;//按理說(shuō)這里應(yīng)該有一個(gè)申請(qǐng)權(quán)限的過(guò)程,但為了使程序盡可能最簡(jiǎn)化,所以先不添加
                  }
                  mCameraManager.openCamera(mCameraId, deviceStateCallback, mHandler);
              } catch (CameraAccessException e) {
                  Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
              }

      注意打開(kāi)Camera之前一定要?jiǎng)討B(tài)申請(qǐng)權(quán)限,這里暫時(shí)還沒(méi)寫(xiě),運(yùn)行時(shí)我是直接在手機(jī)打開(kāi)相關(guān)權(quán)限的。否則無(wú)法發(fā)運(yùn)行?。?!
      在打開(kāi)Camera時(shí)傳入了CameraDevice.StateCallback,用來(lái)反饋相機(jī)工作狀態(tài):

      private CameraDevice.StateCallback deviceStateCallback = new CameraDevice.StateCallback() {
              @Override
              public void onOpened(CameraDevice camera) {
                  mCameraDevice = camera;
                  try {
                      takePreview();
                  } catch (CameraAccessException e) {
                      e.printStackTrace();
                  }
              }
      
              @Override
              public void onDisconnected(@NonNull CameraDevice camera) {
                  if (mCameraDevice != null) {
                      mCameraDevice.close();
                      mCameraDevice = null;
                  }
              }
      
              @Override
              public void onError(CameraDevice camera, int error) {
                  Toast.makeText(MainActivity.this, "打開(kāi)攝像頭失敗", Toast.LENGTH_SHORT).show();
              }
          };
      

      相信聰明的讀者從字面意思就可以理解代碼的意義了,就不細(xì)說(shuō)了。
      4,顯示預(yù)覽界面,進(jìn)行正式預(yù)覽
      在成功打開(kāi)相機(jī)后,回調(diào)onOpened方法,調(diào)用takePreview()方法,進(jìn)行正式的預(yù)覽,經(jīng)過(guò)這步,我們就可以實(shí)時(shí)看到拍攝的畫(huà)面。

       public void takePreview() throws CameraAccessException {
              mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
              mPreviewBuilder.addTarget(mSurfaceViewHolder.getSurface());
              mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceViewHolder.getSurface(), mImageReader.getSurface()), mSessionPreviewStateCallback, mHandler);
          }
       private CameraCaptureSession.StateCallback mSessionPreviewStateCallback = new CameraCaptureSession.StateCallback() {
              @Override
              public void onConfigured(@NonNull CameraCaptureSession session) {
                  mSession = session;
                  //配置完畢開(kāi)始預(yù)覽
                  try {
                      /**
                       * 設(shè)置你需要配置的參數(shù)
                       */
                      //自動(dòng)對(duì)焦
                      mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                      //打開(kāi)閃光燈
                      mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                      //無(wú)限次的重復(fù)獲取圖像
                      mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
                  } catch (CameraAccessException e) {
                      e.printStackTrace();
                  }
              }
      
              @Override
              public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                  Toast.makeText(MainActivity.this, "配置失敗", Toast.LENGTH_SHORT).show();
              }
          };
          private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
              @Override
              public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                  mSession = session;
              }
      
              @Override
              public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
                  mSession = session;
              }
               @Override
              public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                  super.onCaptureFailed(session, request, failure);
              }
          };
       mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

      mPreviewBuilder是前文講過(guò)的CaptureRequest的Builder(Builder設(shè)計(jì)模式),用來(lái)對(duì)CaptureRequest進(jìn)行編輯。這里有個(gè)參數(shù)——CameraDevice.TEMPLATE_PREVIEW:看名字可以猜到,這是表明這個(gè)request是針對(duì)于相機(jī)預(yù)覽界面的。

       mPreviewBuilder.addTarget(mSurfaceViewHolder.getSurface());

      將request設(shè)置的參數(shù)數(shù)據(jù)應(yīng)用于SurfaceView對(duì)應(yīng)的surface,request設(shè)置的參數(shù)形成的圖像數(shù)據(jù)都會(huì)保存在SurfaceView的surface中。
      來(lái)簡(jiǎn)單介紹下Surface:

      應(yīng)用產(chǎn)生的圖像數(shù)據(jù)保存的地方,SurfaceView和ImageReader都有一個(gè)對(duì)應(yīng)的surface。
      mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceViewHolder.getSurface(), mImageReader.getSurface()), mSessionPreviewStateCallback, mHandler);

      這個(gè)在前文也提到過(guò),在安卓端和相機(jī)硬件端建立通道,進(jìn)行信息的交換。
      我們來(lái)看看這個(gè)方法的原型:

      createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)

      只有第一個(gè)參數(shù)有點(diǎn)難理解,它是在聲明相機(jī)拍攝圖像數(shù)據(jù)的存儲(chǔ)點(diǎn)——Surface對(duì)象。
      再回到我們程序中,第一個(gè)參數(shù)是:

      Arrays.asList(mSurfaceViewHolder.getSurface(), mImageReader.getSurface()

      即是SurfaceView和ImageReader的Surface組成的一個(gè)數(shù)組,表明圖像數(shù)據(jù)將會(huì)輸出到這兩個(gè)地方。

      再看mSessionPreviewStateCallback:

       private CameraCaptureSession.StateCallback mSessionPreviewStateCallback = new CameraCaptureSession.StateCallback() {
              @Override
              public void onConfigured(@NonNull CameraCaptureSession session) {
                  mSession = session;
                  //配置完畢開(kāi)始預(yù)覽
                  try {
                      /**
                       * 設(shè)置你需要配置的參數(shù)
                       */
                      //自動(dòng)對(duì)焦
                      mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                      //打開(kāi)閃光燈
                      mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                      //無(wú)限次的重復(fù)獲取圖像
                      mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
                  } catch (CameraAccessException e) {
                      e.printStackTrace();
                  }
              }
      
      
              @Override
              public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                  Toast.makeText(MainActivity.this, "配置失敗", Toast.LENGTH_SHORT).show();
              }
          };
      

      這是創(chuàng)建session的一個(gè)回調(diào),前文也提到過(guò)。
      方法中分別對(duì)session建立成功和失敗的情況進(jìn)行了處理,成功后,設(shè)置了一系列參數(shù),最后調(diào)用:

                  //無(wú)限次的重復(fù)獲取圖像
                 mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);

      這個(gè)很好理解,預(yù)覽的過(guò)程其實(shí)就是不斷重復(fù)請(qǐng)求圖像數(shù)據(jù)的過(guò)程,所以叫”repeating”。
      其原型為:

      setRepeatingRequest(CaptureRequest request, CameraCaptureSession.CaptureCallback listener, Handler handler)

      注意第二個(gè)參數(shù),雖然我們目前的代碼設(shè)置為null沒(méi)有用到,但是還是給出了這個(gè)回調(diào)接口的代碼:

       private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
       //拍攝完全完成并且成功,拍攝圖像數(shù)據(jù)可用時(shí)回調(diào)
              @Override
              public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                  mSession = session;
              }
      //拍攝進(jìn)行中,但拍攝圖像數(shù)據(jù)部分可用時(shí)回調(diào)
              @Override
              public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
                  mSession = session;
              }
          };
          //拍攝失敗時(shí)回調(diào)
           @Override
              public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                  super.onCaptureFailed(session, request, failure);
              }

      我們可以在這個(gè)回調(diào)做圖片的保存工作,但當(dāng)然還是出于最簡(jiǎn)化原則,暫時(shí)不寫(xiě)。
      5,一番辛苦,我們終于可以拍照片了?。?!

       public void takePicture() {
              try {
                  CaptureRequest.Builder captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);//用來(lái)設(shè)置拍照請(qǐng)求的request
                  captureRequestBuilder.addTarget(mImageReader.getSurface());
                  // 自動(dòng)對(duì)焦
                  captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                  // 自動(dòng)曝光
                  captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                  int rotation = getWindowManager().getDefaultDisplay().getRotation();
                  CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
                  captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getJpegOrientation(cameraCharacteristics, rotation));//使圖片做順時(shí)針旋轉(zhuǎn)
                  //captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,getOrientaion(rotation))兩種方法都可以。
                  CaptureRequest mCaptureRequest = captureRequestBuilder.build();
                  mSession.capture(mCaptureRequest, null, mHandler);
              } catch (CameraAccessException e) {
                  e.printStackTrace();
              }
          }
      CaptureRequest.Builder captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);//用來(lái)設(shè)置拍照請(qǐng)求的request

      這個(gè)之前講過(guò),只是參數(shù)改變了——CameraDevice.TEMPLATE_STILL_CAPTURE,這次是針對(duì)拍攝圖片的request。

      接下來(lái)挑關(guān)鍵的講:

      int rotation = getWindowManager().getDefaultDisplay().getRotation();

      獲取屏幕的方向,你旋轉(zhuǎn)屏幕這個(gè)值會(huì)發(fā)生相應(yīng)變化,因?yàn)閷?duì)這個(gè)理解也不深,不透徹,不敢亂講,大家可以google一下,很多這個(gè)的講解,如果我以后理解深了,就會(huì)寫(xiě)進(jìn)博客。

      captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getJpegOrientation(cameraCharacteristics, rotation));//使圖片做順時(shí)針旋轉(zhuǎn)
                  //captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,getOrientaion(rotation))兩種方法都可以。

      這個(gè)是設(shè)置圖片的方向使圖片豎直放置,注意第二個(gè)參數(shù),代表應(yīng)該圖片應(yīng)該旋轉(zhuǎn)的角度,因?yàn)檫@個(gè)比較復(fù)雜(真的復(fù)雜,,,),大家想了解可以看看這篇博客——Android相機(jī)開(kāi)發(fā)那些坑。
      代碼中我提供了兩種第二個(gè)參數(shù):
      網(wǎng)友提出的:

      //獲取圖片應(yīng)該旋轉(zhuǎn)的角度,使圖片豎直
          public int getOrientation(int rotation) {
              switch (rotation) {
                  case Surface.ROTATION_0:
                      return 90;
                  case Surface.ROTATION_90:
                      return 0;
                  case Surface.ROTATION_180:
                      return 270;
                  case Surface.ROTATION_270:
                      return 180;
                  default:
                      return 0;
              }
          }
      

      Google官網(wǎng)上的:

       //獲取圖片應(yīng)該旋轉(zhuǎn)的角度,使圖片豎直
          private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) {
              if (deviceOrientation == android.view.OrientationEventListener.ORIENTATION_UNKNOWN)
                  return 0;
              int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
      
              // Round device orientation to a multiple of 90
              deviceOrientation = (deviceOrientation + 45) / 90 * 90;
      
              // LENS_FACING相對(duì)于設(shè)備屏幕的方向,LENS_FACING_FRONT相機(jī)設(shè)備面向與設(shè)備屏幕相同的方向
              boolean facingFront = c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT;
              if (facingFront) deviceOrientation = -deviceOrientation;
      
              // Calculate desired JPEG orientation relative to camera orientation to make
              // the image upright relative to the device orientation
              int jpegOrientation = (sensorOrientation + deviceOrientation + 360) % 360;
      
              return jpegOrientation;
          }

      目前發(fā)現(xiàn)兩種都可以,所以?xún)煞N都放上來(lái),但個(gè)人也不是全部理解,所以很抱歉,暫時(shí)還不能給大家做解釋?zhuān)贿^(guò)相信聰明的你們可以通過(guò)閱讀上面的博客理解它!

      mSession.capture(mCaptureRequest, null, mHandler);

      這是最關(guān)鍵的一步,通過(guò)這步,拍照動(dòng)作才真正完成。
      其原型是:

      capture(CaptureRequest request, CameraCaptureSession.CaptureCallback listener, Handler handler)

      相對(duì)簡(jiǎn)單,就不解釋了。

      經(jīng)過(guò)上述有點(diǎn)復(fù)雜的步驟,相信大家都可以做出一個(gè)最簡(jiǎn)單的相機(jī)應(yīng)用

      三,進(jìn)階應(yīng)用之圖片的保存
      我將它集成在一個(gè)方法中,方便需要時(shí)插入到代碼中:

      //將照片存儲(chǔ)在相機(jī)照片存儲(chǔ)位置,這里采用bitmap方式保存
          public String savePicture(byte[] imgBytes) {
              pictureId++;
              String imgPath = Environment.getExternalStorageDirectory() + "/DCIM/Camera/WordRecognition_picture" + pictureId + ".jpg";
              Bitmap bitmap = BitmapFactory.decodeByteArray(imgBytes, 0, imgBytes.length);//圖像數(shù)據(jù)被轉(zhuǎn)化為bitmap
              File outputImage = new File(imgPath);
              FileOutputStream outputStream = null;
              try {
                  if (outputImage.exists()) {
                      outputImage.delete();//存在就刪除
                  }
                  outputImage.createNewFile();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              try {
                  outputStream = new FileOutputStream(outputImage);
                  bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);//第二個(gè)參數(shù)為壓縮質(zhì)量
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              }
              try {
                  outputStream.flush();
                  outputStream.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      //及時(shí)更新到系統(tǒng)相冊(cè)
              MediaScannerConnection.scanFile(this, new String[]{Environment.getExternalStorageDirectory() + "http://DCIM//Camera//WordRecogniton_picture" + pictureId + ".jpg"}, null, null);//"http://"可以用File.separator代替
              return imgPath;
          }
      

      同樣,我們接下來(lái)分開(kāi)講解:

      public String savePicture(byte[] imgBytes)

      參數(shù)部分我傳入了圖片的byte數(shù)據(jù)格式,那么它是如何獲得的呢——

       private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
              @Override
              public void onImageAvailable(ImageReader reader) {
                  byte[] imgBytes;
                  imgBytes = getImagBytes(reader);//獲取img的bytes數(shù)據(jù)格式
                  filePath = savePicture(imgBytes);
                  }
          };

      這個(gè)就是從之前代碼改過(guò)來(lái)的,一般都在這個(gè)回調(diào)方法中保存照片。

       pictureId++;
              String imgPath = Environment.getExternalStorageDirectory() + "/DCIM/Camera/WordRecognition_picture" + pictureId + ".jpg";

      pictureId就是一個(gè)為命名專(zhuān)門(mén)而設(shè)的一個(gè)int變量,初衷是為了不使圖片命名重復(fù),但現(xiàn)在看來(lái)其實(shí)使用系統(tǒng)時(shí)間應(yīng)該更明智。
      imgPath是你的圖片要存放的目標(biāo)文件地址,其中
      Environment.getExternalStorageDirectory() :內(nèi)部存儲(chǔ)文件夾,其下是所有內(nèi)部存儲(chǔ)文件,你打開(kāi)文件管理,應(yīng)該可以看到這個(gè)內(nèi)部存儲(chǔ)文件夾,點(diǎn)進(jìn)去是手機(jī)里各種文件夾。
      DCIM/Camera:所有的安卓手機(jī)拍攝的照片都保存在這個(gè)文件夾中,大家可以去看看。

       File outputImage = new File(imgPath);
              FileOutputStream outputStream = null;
              try {
                  if (outputImage.exists()) {
                      outputImage.delete();//存在就刪除
                  }
                  outputImage.createNewFile();
              } catch (IOException e) {
                  e.printStackTrace();
              }

      創(chuàng)造對(duì)應(yīng)文件及輸出流,對(duì)應(yīng)文件不存在就創(chuàng)造,存在就刪除。

       bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);//第二個(gè)參數(shù)為壓縮質(zhì)量

      Bitmap自帶的一個(gè)圖像壓縮方法,第一個(gè)參數(shù)為壓縮的格式,第二個(gè)參數(shù)為壓縮質(zhì)量(100表示無(wú)損),第三個(gè)參數(shù)表明壓縮數(shù)據(jù)的輸出對(duì)象。

      //及時(shí)更新到系統(tǒng)相冊(cè)
              MediaScannerConnection.scanFile(this, new String[]{Environment.getExternalStorageDirectory() + "http://DCIM//Camera//WordRecogniton_picture" + pictureId + ".jpg"}, null, null);//"http://"可以用File.separator代替

      更新圖片到相冊(cè)的一個(gè)方法。

      四,進(jìn)階應(yīng)用之調(diào)用系統(tǒng)相冊(cè)
      我將其寫(xiě)成一個(gè)方法:

       public void openAlbum() {
              Intent intent = new Intent("android.intent.action.GET_CONTENT");//選擇照片后毀掉onActivityResult方法
              intent.setType("image/*");
              startActivityForResult(intent, CHOOSE_PHOTO);
          }
      
      @Override
          protected void onActivityResult(int requestCode, int resultCode, Intent data) {
              switch (requestCode) {
                  case CHOOSE_PHOTO:
                      String imgPath=null;
                      if (resultCode == RESULT_OK) {
                          if (Build.VERSION.SDK_INT > 19) {
                              imgPath = handlerImgOnNewVersion(data);
                          } else {
                              imgPath = handlerImgOnOldVersion(data);
                          }
                          //以上獲取了選擇的圖片的路徑,在這里可以應(yīng)用這個(gè)路徑,做一些想要做的東西
                      }
                      break;
                  default:
              }
          }
      private String handlerImgOnOldVersion(Intent data) {
              Uri uri = data.getData();
              String imgPath = getImagePath(uri, null);
              return imgPath;
          }
          private String handlerImgOnNewVersion(Intent data) {
              String imgPath = null;//選擇的圖片的路徑
              Uri uri = data.getData();//選擇圖片的結(jié)果,即圖片地址的封裝,接下來(lái)對(duì)其進(jìn)行解析
              if (DocumentsContract.isDocumentUri(this, uri)) {//判斷是否是document類(lèi)型
                  String docId = DocumentsContract.getDocumentId(uri);
                  switch (uri.getAuthority())//就是獲取uri的最開(kāi)頭部分
                  {
                      case "com.android.providers.media.documents":
                          String id = docId.split(":")[1];//解析出數(shù)字格式的id
                          String selection = MediaStore.Images.Media._ID + "=" + id;
                          imgPath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
                          break;
                      case "com.android.providers.downloads.documents":
                          Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
                          imgPath = getImagePath(contentUri, null);
                          break;
                      default:
                  }
              } else if ("content".equalsIgnoreCase(uri.getScheme())) {
                  imgPath = getImagePath(uri, null);
              } else if ("file".equalsIgnoreCase(uri.getScheme())) {
                  imgPath = uri.getPath();
              }
              return imgPath;
          }
      
      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;
          }

      現(xiàn)在分開(kāi)講解:

      public void openAlbum() {
              Intent intent = new Intent("android.intent.action.GET_CONTENT");//選擇照片后毀掉onActivityResult方法
              intent.setType("image/*");
              startActivityForResult(intent, CHOOSE_PHOTO);
          }

      此處intent啟動(dòng)顯然是隱式啟動(dòng),傳入系統(tǒng)內(nèi)部定義的action:”android.intent.action.GET_CONTENT”:代表你想要獲取特定種類(lèi)的數(shù)據(jù),setType方法確定你要獲取的類(lèi)型。

      再看回調(diào):
      你選擇一張圖片時(shí)就會(huì)回調(diào)onActivityResult方法

                      if (resultCode == RESULT_OK) {
                          if (Build.VERSION.SDK_INT > 19) {
                              imgPath = handlerImgOnNewVersion(data);
                          } else {
                              imgPath = handlerImgOnOldVersion(data);
                          }

      這里做了個(gè)系統(tǒng)適配:
      4.4系統(tǒng)以上調(diào)用handlerImgOnNewVersion方法:

        private String handlerImgOnNewVersion(Intent data) {
              String imgPath = null;//選擇的圖片的路徑
              Uri uri = data.getData();//選擇圖片的結(jié)果,即圖片地址的封裝,接下來(lái)對(duì)其進(jìn)行解析
              if (DocumentsContract.isDocumentUri(this, uri)) {//判斷是否是document類(lèi)型,對(duì)應(yīng)DocumentProvider
                  String docId = DocumentsContract.getDocumentId(uri);//代表文件的唯一id
                  switch (uri.getAuthority())//就是獲取uri的最開(kāi)頭部分
                  {
                      case "com.android.providers.media.documents"://多媒體文件類(lèi)型
                          String id = docId.split(":")[1];//解析出數(shù)字格式的id
                          String selection = MediaStore.Images.Media._ID + "=" + id;//篩選條件
                          imgPath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
                          break;
                      case "com.android.providers.downloads.documents"://下載內(nèi)容
                          Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));//將給定的id加到路徑的末尾,相當(dāng)于構(gòu)造了這類(lèi)文件的唯一id
                          imgPath = getImagePath(contentUri, null);
                          break;
                      default:
                  }
              } else if ("content".equalsIgnoreCase(uri.getScheme())) {//對(duì)應(yīng)ContentProvider
                  imgPath = getImagePath(uri, null);
              } else if ("file".equalsIgnoreCase(uri.getScheme())) {//對(duì)應(yīng)FileProvider
                  imgPath = uri.getPath();
              }
              return imgPath;
          }
      

      你所選擇的圖片的信息封裝在data中,通過(guò)getData方法你獲取到圖片對(duì)應(yīng)的Uri對(duì)象,4.4系統(tǒng)以后Uri就被封裝了,不能直接提取圖片地址,必須解析。
      一個(gè)Uri的數(shù)據(jù)格式分為下面三種:

      [scheme:]scheme-specific-part[#fragment]  
      [scheme:][//authority][path][?query][#fragment]  
      [scheme:][//host:port][path][?query][#fragment]  

      顯然這里是第二種。
      其實(shí)理解各類(lèi)型uri是怎么劃分,哪種文件的uri屬于哪種類(lèi)型是沒(méi)有什么意義的,我們只需要知道你獲取任意形式內(nèi)容時(shí),Uri有三個(gè)大的類(lèi)型來(lái)源——
      DocumentProvider,ContentProvider,FileProvider(三種內(nèi)容提供器),即按Scheme可以分為這三大類(lèi)。
      然后每種可能按authority分小類(lèi),處理方法不同。其它在注釋中大都都解釋了,selection代表篩選的條件,系統(tǒng)通過(guò)這個(gè)條件和uri識(shí)別出對(duì)應(yīng)文件。

      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;
          }

      query原型:

       query(Uri,String[]projection,String selection,String[]selectionArgs,String sortOrder)

      projection:所要搜尋的列的名字(columns),null表示所有列
      selection:篩選行的條件,null表示所有行
      selectionArgs:為selection中的占位符“?”提供具體的值
      sorOrder:返回結(jié)果的排序方式,null表示默認(rèn)順序
      query返回Cursor用于遍歷數(shù)據(jù)。

      cursor.getColumnIndex(MediaStore.Images.Media.DATA)

      返回指定列的名稱(chēng)對(duì)應(yīng)的Cursor索引,用于搜尋對(duì)應(yīng)數(shù)據(jù)。
      MediaStore.Images.Media.DATA存儲(chǔ)了對(duì)應(yīng)圖片的地址信息。
      以上種種過(guò)程其實(shí)是查表的過(guò)程,你可以想象系統(tǒng)用了一張很大的表來(lái)存儲(chǔ)各種信息。
      介紹到這,相信大家差不多理解了如何打開(kāi)相冊(cè)。
      馬上我會(huì)傳一下自己寫(xiě)的一個(gè)小的文字識(shí)別的app.

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多