Android从相机和图库捕获图像

时间:2020-02-23 14:28:49  来源:igfitidea点击:

在本教程中,我们将开发一个应用程序,该应用程序可以从相机或者图库中拾取图像并将其显示在ImageView中。

注意:以下代码适用于Android之前的牛轧糖版本。
有关最新的工作示例,请查看更新的文章。

Android Capture图像概述

随着Android Marshmallow的开始,需要首先实现运行时权限。

在Android Manifest.xml文件中的应用程序标签上方添加以下权限。

<uses-feature
      android:name="android.hardware.camera"
      android:required="false" 
  <uses-feature
      android:name="android.hardware.camera.autofocus"
      android:required="false" 
  <uses-feature
      android:name="android.hardware.camera.flash"
      android:required="false" 

  <uses-permission android:name="android.permission.CAMERA" 
  <uses-permission android:name="ANDROID.PERMISSION.READ_EXTERNAL_STORAGE"

通过添加android.hardware.camera,Play商店可以检测并阻止在没有相机的设备上安装该应用程序。

意图是将动作委派给另一个应用程序的标准方法。
要启动本机相机,Intent需要android.provider.MediaStore.ACTION_IMAGE_CAPTURE。

要从图库中选择图像,Intent需要以下参数:Intent.ACTION_GET_CONTENT。

在本教程中,我们将调用图像选择器,使我们可以从相机或者图库中选择图像,并以圆形图像视图和普通图像视图显示该图像。
在build.gradle文件中添加以下依赖项。
compile'de.hdodenhof:circleimageview:2.1.0'

Android Capture图像代码

activity_main.xml的布局保持不变,除非将FAB按钮的图标更改更改为@android:drawable/ic_menu_camera

下面给出了" content_main.xml":

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:id="@+id/content_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  android:background="#000000"
  app:layout_behavior="@string/appbar_scrolling_view_behavior"
  tools:context="com.theitroad.imagepicker.MainActivity"
  tools:showIn="@layout/activity_main">

  <RelativeLayout
      android:layout_width="250dp"
      android:layout_height="250dp"
      android:layout_centerHorizontal="true"
      android:layout_centerVertical="true"
      android:background="@drawable/image_border"
      android:clickable="true"
      android:orientation="vertical">

      <ImageView
          android:id="@+id/imageView"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:adjustViewBounds="true"
          android:scaleType="centerCrop" 

  </RelativeLayout>

  <de.hdodenhof.circleimageview.CircleImageView
      android:id="@+id/img_profile"
      android:layout_width="100dp"
      android:layout_height="100dp"
      android:layout_gravity="center_horizontal"
      android:src="@drawable/profile"
      app:civ_border_width="5dp"
      app:civ_border_color="#FFFFFF"
      android:layout_alignParentBottom="true"
      android:layout_centerHorizontal="true" 

</RelativeLayout>

下面给出了MainActivity.java的代码

public class MainActivity extends AppCompatActivity {

  Bitmap myBitmap;
  Uri picUri;

  private ArrayList permissionsToRequest;
  private ArrayList permissionsRejected = new ArrayList();
  private ArrayList permissions = new ArrayList();

  private final static int ALL_PERMISSIONS_RESULT = 107;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
      setSupportActionBar(toolbar);

      FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
      fab.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              startActivityForResult(getPickImageChooserIntent(), 200);
          }
      });

      permissions.add(CAMERA);
      permissionsToRequest = findUnAskedPermissions(permissions);
      //get the permissions we have asked for before but are not granted..
      //we will store this in a global list to access later.

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

          if (permissionsToRequest.size() > 0)
              requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
      }

  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
      //Inflate the menu; this adds items to the action bar if it is present.
      getMenuInflater().inflate(R.menu.menu_main, menu);
      return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
      //Handle action bar item clicks here. The action bar will
      //automatically handle clicks on the Home/Up button, so long
      //as you specify a parent activity in AndroidManifest.xml.
      int id = item.getItemId();

      //noinspection SimplifiableIfStatement
      if (id == R.id.action_settings) {
          return true;
      }

      return super.onOptionsItemSelected(item);
  }

  /**
   * Create a chooser intent to select the source to get image from.<br 
   * The source can be camera's (ACTION_IMAGE_CAPTURE) or gallery's (ACTION_GET_CONTENT).<br 
   * All possible sources are added to the intent chooser.
   */
  public Intent getPickImageChooserIntent() {

      //Determine Uri of camera image to save.
      Uri outputFileUri = getCaptureImageOutputUri();

      List allIntents = new ArrayList();
      PackageManager packageManager = getPackageManager();

      //collect all camera intents
      Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
      List listCam = packageManager.queryIntentActivities(captureIntent, 0);
      for (ResolveInfo res : listCam) {
          Intent intent = new Intent(captureIntent);
          intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
          intent.setPackage(res.activityInfo.packageName);
          if (outputFileUri != null) {
              intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
          }
          allIntents.add(intent);
      }

      //collect all gallery intents
      Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
      galleryIntent.setType("image/*");
      List listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
      for (ResolveInfo res : listGallery) {
          Intent intent = new Intent(galleryIntent);
          intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
          intent.setPackage(res.activityInfo.packageName);
          allIntents.add(intent);
      }

      //the main intent is the last in the list (fucking android) so pickup the useless one
      Intent mainIntent = allIntents.get(allIntents.size() - 1);
      for (Intent intent : allIntents) {
          if (intent.getComponent().getClassName().equals("com.android.documentsui.DocumentsActivity")) {
              mainIntent = intent;
              break;
          }
      }
      allIntents.remove(mainIntent);

      //Create a chooser from the main intent
      Intent chooserIntent = Intent.createChooser(mainIntent, "Select source");

      //Add all other intents
      chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));

      return chooserIntent;
  }

  /**
   * Get URI to image received from capture by camera.
   */
  private Uri getCaptureImageOutputUri() {
      Uri outputFileUri = null;
      File getImage = getExternalCacheDir();
      if (getImage != null) {
          outputFileUri = Uri.fromFile(new File(getImage.getPath(), "profile.png"));
      }
      return outputFileUri;
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {

      Bitmap bitmap;
      if (resultCode == Activity.RESULT_OK) {

          ImageView imageView = (ImageView) findViewById(R.id.imageView);

          if (getPickImageResultUri(data) != null) {
              picUri = getPickImageResultUri(data);

              try {
                  myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
                  myBitmap = rotateImageIfRequired(myBitmap, picUri);
                  myBitmap = getResizedBitmap(myBitmap, 500);

                  CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
                  croppedImageView.setImageBitmap(myBitmap);
                  imageView.setImageBitmap(myBitmap);

              } catch (IOException e) {
                  e.printStackTrace();
              }

          } else {

              bitmap = (Bitmap) data.getExtras().get("data");

              myBitmap = bitmap;
              CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
              if (croppedImageView != null) {
                  croppedImageView.setImageBitmap(myBitmap);
              }

              imageView.setImageBitmap(myBitmap);

          }

      }

  }

  private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException {

      ExifInterface ei = new ExifInterface(selectedImage.getPath());
      int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

      switch (orientation) {
          case ExifInterface.ORIENTATION_ROTATE_90:
              return rotateImage(img, 90);
          case ExifInterface.ORIENTATION_ROTATE_180:
              return rotateImage(img, 180);
          case ExifInterface.ORIENTATION_ROTATE_270:
              return rotateImage(img, 270);
          default:
              return img;
      }
  }

  private static Bitmap rotateImage(Bitmap img, int degree) {
      Matrix matrix = new Matrix();
      matrix.postRotate(degree);
      Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
      img.recycle();
      return rotatedImg;
  }

  public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
      int width = image.getWidth();
      int height = image.getHeight();

      float bitmapRatio = (float) width/(float) height;
      if (bitmapRatio > 0) {
          width = maxSize;
          height = (int) (width/bitmapRatio);
      } else {
          height = maxSize;
          width = (int) (height * bitmapRatio);
      }
      return Bitmap.createScaledBitmap(image, width, height, true);
  }

  /**
   * Get the URI of the selected image from {@link #getPickImageChooserIntent()}.<br 
   * Will return the correct URI for camera and gallery image.
   *
   * @param data the returned data of the activity result
   */
  public Uri getPickImageResultUri(Intent data) {
      boolean isCamera = true;
      if (data != null) {
          String action = data.getAction();
          isCamera = action != null && action.equals(MediaStore.ACTION_IMAGE_CAPTURE);
      }

      return isCamera ? getCaptureImageOutputUri() : data.getData();
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);

      //save file url in bundle as it will be null on scren orientation
      //changes
      outState.putParcelable("pic_uri", picUri);
  }

  @Override
  protected void onRestoreInstanceState(Bundle savedInstanceState) {
      super.onRestoreInstanceState(savedInstanceState);

      //get the file url
      picUri = savedInstanceState.getParcelable("pic_uri");
  }

  private ArrayList findUnAskedPermissions(ArrayList wanted) {
      ArrayList result = new ArrayList();

      for (String perm : wanted) {
          if (!hasPermission(perm)) {
              result.add(perm);
          }
      }

      return result;
  }

  private boolean hasPermission(String permission) {
      if (canMakeSmores()) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
              return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
          }
      }
      return true;
  }

  private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
      new AlertDialog.Builder(this)
              .setMessage(message)
              .setPositiveButton("OK", okListener)
              .setNegativeButton("Cancel", null)
              .create()
              .show();
  }

  private boolean canMakeSmores() {
      return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
  }

  @TargetApi(Build.VERSION_CODES.M)
  @Override
  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

      switch (requestCode) {

          case ALL_PERMISSIONS_RESULT:
              for (String perms : permissionsToRequest) {
                  if (hasPermission(perms)) {

                  } else {

                      permissionsRejected.add(perms);
                  }
              }

              if (permissionsRejected.size() > 0) {

                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                      if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
                          showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
                                  new DialogInterface.OnClickListener() {
                                      @Override
                                      public void onClick(DialogInterface dialog, int which) {
                                          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                                              //Log.d("API123", "permisionrejected " + permissionsRejected.size());

                                              requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
                                          }
                                      }
                                  });
                          return;
                      }
                  }

              }

              break;
      }

  }
}

从上面的代码中可以得出很多推断。

  • 当用户启动活动时,我们需要请求Camera运行时权限。

  • 在开始获取一些结果的意图时,我们需要使用相关参数调用startActivityForResult

  • 我们没有使用对话框来分别调用"相机和画廊意图",而是使用了getPickImageChooserIntent()方法,该方法为所有相机和画廊意图创建一个选择器意图(请注意文档意图)。
    Intent.EXTRA_INITIAL_INTENTS用于在一个位置添加多个应用程序意图

  • 对于摄像机意图,将传递MediaStore.EXTRA_OUTPUT作为另外内容以指定图像存储路径。
    否则,您将只获得一幅小分辨率的图像。

  • 相机返回的图像的URI路径是在getCaptureImageOutputUri()方法内部获取的。

  • onActivityResult本质上返回图像的URI。
    某些设备的确将位图返回为data.getExtras().get(" data");

  • 单击图像后,返回时的相机屏幕将重新启动活动,从而使从方法getCaptureImageOutputUri()存储的URI变为空。

因此,我们必须使用" onSaveInstanceState()"和" onRestoreInstanceState()"来存储和还原该URI。

  • 在下面的代码行中,从URI中检索位图。

myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),picUri);

  • 众所周知,像三星 galaxy这样的设备可以横向捕获图像。
    检索图像并按原样显示可能会导致其显示方向错误。
    因此,我们将方法称为" rotateImageIfRequired(myBitmap,picUri);"。

  • ExifInterface是用于在JPEG文件或者RAW图像文件中读写Exif标签的类。

  • 最后,我们调用方法getResizedBitmap()来按宽度或者高度(以较大者为准)缩放位图,并使用setImageBitmap将图像设置为图像视图。

实际应用程序的输出如下。
注意:由于明显的原因,要从相机捕获并显示图像,您需要在智能手机上运行该应用程序。