Android 从相机和图库捕捉图像

在本教程中,我们将开发一个应用程序,该应用程序从相机或图库中选取一幅图像并将其显示在ImageView中。注意:以下代码适用于Android Nougat之前的版本。有关最新的工作示例,请查看[更新的文章](http://Note:谷歌绕开了按字母顺序排列的安卓版本。Android Q已更名为Android 10。由于本教程是在Google决定这样做之前编写的,因此您将在文章中的某些地方看到Android Q。)

Android截图概述

随着Android Marshmallow的开始,运行时权限需要最先实现。在Android Manifest.xml文件中的应用程序标签上方添加以下权限。

 1<uses-feature
 2        android:name="android.hardware.camera"
 3        android:required="false" />
 4    <uses-feature
 5        android:name="android.hardware.camera.autofocus"
 6        android:required="false" />
 7    <uses-feature
 8        android:name="android.hardware.camera.flash"
 9        android:required="false" />
10
11    <uses-permission android:name="android.permission.CAMERA" />
12    <uses-permission android:name="ANDROID.PERMISSION.READ_EXTERNAL_STORAGE"/>

通过添加 android.hardware.camera ,Play Store会检测并阻止在没有摄像头的设备上安装应用程序。Intent是将操作委托给另一个应用程序的标准方式。要启动本机摄像头,Intent需要** android.provider.MediaStore.ACTION_IMAGE_CAPTURE** 。要从图库中选择图像,Intent需要以下参数:** Intent.ACTION_GET_CONTENT** 。在本教程中,我们将调用一个图像拾取器,它允许我们从相机或图库中选择一个图像,并在圆形图像视图和普通图像视图中显示该图像。在build.gradle文件中添加以下依赖项。** compile 'de.hdodenhof:circleimageview:2.1.0'**

Android截图项目结构

来自摄像头和图库选取器project的安卓抓拍图片

Android采集图片代码

Active_main.xml的布局保持不变,只是Fab按钮的图标更改为**@android:Drawable/ic_Menu_Camera** 。** content_main.xml** 如下:

 1<?xml version="1.0" encoding="utf-8"?>
 2<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
 3    xmlns:app="https://schemas.android.com/apk/res-auto"
 4    xmlns:tools="https://schemas.android.com/tools"
 5    android:id="@+id/content_main"
 6    android:layout_width="match_parent"
 7    android:layout_height="match_parent"
 8    android:paddingBottom="@dimen/activity_vertical_margin"
 9    android:paddingLeft="@dimen/activity_horizontal_margin"
10    android:paddingRight="@dimen/activity_horizontal_margin"
11    android:paddingTop="@dimen/activity_vertical_margin"
12    android:background="#000000"
13    app:layout_behavior="@string/appbar_scrolling_view_behavior"
14    tools:context="com.journaldev.imagepicker.MainActivity"
15    tools:showIn="@layout/activity_main">
16
17    <RelativeLayout
18        android:layout_width="250dp"
19        android:layout_height="250dp"
20        android:layout_centerHorizontal="true"
21        android:layout_centerVertical="true"
22        android:background="@drawable/image_border"
23        android:clickable="true"
24        android:orientation="vertical">
25
26        <ImageView
27            android:id="@+id/imageView"
28            android:layout_width="match_parent"
29            android:layout_height="match_parent"
30            android:adjustViewBounds="true"
31            android:scaleType="centerCrop" />
32
33    </RelativeLayout>
34
35    <de.hdodenhof.circleimageview.CircleImageView
36        android:id="@+id/img_profile"
37        android:layout_width="100dp"
38        android:layout_height="100dp"
39        android:layout_gravity="center_horizontal"
40        android:src="@drawable/profile"
41        app:civ_border_width="5dp"
42        app:civ_border_color="#FFFFFF"
43        android:layout_alignParentBottom="true"
44        android:layout_centerHorizontal="true" />
45
46</RelativeLayout>

MainActivity.java 的代码如下所示

  1public class MainActivity extends AppCompatActivity {
  2
  3    Bitmap myBitmap;
  4    Uri picUri;
  5
  6    private ArrayList permissionsToRequest;
  7    private ArrayList permissionsRejected = new ArrayList();
  8    private ArrayList permissions = new ArrayList();
  9
 10    private final static int ALL_PERMISSIONS_RESULT = 107;
 11
 12    @Override
 13    protected void onCreate(Bundle savedInstanceState) {
 14        super.onCreate(savedInstanceState);
 15        setContentView(R.layout.activity_main);
 16        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
 17        setSupportActionBar(toolbar);
 18
 19        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
 20        fab.setOnClickListener(new View.OnClickListener() {
 21            @Override
 22            public void onClick(View view) {
 23                startActivityForResult(getPickImageChooserIntent(), 200);
 24            }
 25        });
 26
 27        permissions.add(CAMERA);
 28        permissionsToRequest = findUnAskedPermissions(permissions);
 29        //get the permissions we have asked for before but are not granted..
 30        //we will store this in a global list to access later.
 31
 32        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 33
 34            if (permissionsToRequest.size() > 0)
 35                requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
 36        }
 37
 38    }
 39
 40    @Override
 41    public boolean onCreateOptionsMenu(Menu menu) {
 42        // Inflate the menu; this adds items to the action bar if it is present.
 43        getMenuInflater().inflate(R.menu.menu_main, menu);
 44        return true;
 45    }
 46
 47    @Override
 48    public boolean onOptionsItemSelected(MenuItem item) {
 49        // Handle action bar item clicks here. The action bar will
 50        // automatically handle clicks on the Home/Up button, so long
 51        // as you specify a parent activity in AndroidManifest.xml.
 52        int id = item.getItemId();
 53
 54        //noinspection SimplifiableIfStatement
 55        if (id == R.id.action_settings) {
 56            return true;
 57        }
 58
 59        return super.onOptionsItemSelected(item);
 60    }
 61
 62    /**
 63     * Create a chooser intent to select the source to get image from.<br />
 64     * The source can be camera's (ACTION_IMAGE_CAPTURE) or gallery's (ACTION_GET_CONTENT).<br />
 65     * All possible sources are added to the intent chooser.
 66     */
 67    public Intent getPickImageChooserIntent() {
 68
 69        // Determine Uri of camera image to save.
 70        Uri outputFileUri = getCaptureImageOutputUri();
 71
 72        List allIntents = new ArrayList();
 73        PackageManager packageManager = getPackageManager();
 74
 75        // collect all camera intents
 76        Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
 77        List listCam = packageManager.queryIntentActivities(captureIntent, 0);
 78        for (ResolveInfo res : listCam) {
 79            Intent intent = new Intent(captureIntent);
 80            intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
 81            intent.setPackage(res.activityInfo.packageName);
 82            if (outputFileUri != null) {
 83                intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
 84            }
 85            allIntents.add(intent);
 86        }
 87
 88        // collect all gallery intents
 89        Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
 90        galleryIntent.setType("image/*");
 91        List listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
 92        for (ResolveInfo res : listGallery) {
 93            Intent intent = new Intent(galleryIntent);
 94            intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
 95            intent.setPackage(res.activityInfo.packageName);
 96            allIntents.add(intent);
 97        }
 98
 99        // the main intent is the last in the list (fucking android) so pickup the useless one
100        Intent mainIntent = allIntents.get(allIntents.size() - 1);
101        for (Intent intent : allIntents) {
102            if (intent.getComponent().getClassName().equals("com.android.documentsui.DocumentsActivity")) {
103                mainIntent = intent;
104                break;
105            }
106        }
107        allIntents.remove(mainIntent);
108
109        // Create a chooser from the main intent
110        Intent chooserIntent = Intent.createChooser(mainIntent, "Select source");
111
112        // Add all other intents
113        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));
114
115        return chooserIntent;
116    }
117
118    /**
119     * Get URI to image received from capture by camera.
120     */
121    private Uri getCaptureImageOutputUri() {
122        Uri outputFileUri = null;
123        File getImage = getExternalCacheDir();
124        if (getImage != null) {
125            outputFileUri = Uri.fromFile(new File(getImage.getPath(), "profile.png"));
126        }
127        return outputFileUri;
128    }
129
130    @Override
131    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
132
133        Bitmap bitmap;
134        if (resultCode == Activity.RESULT_OK) {
135
136            ImageView imageView = (ImageView) findViewById(R.id.imageView);
137
138            if (getPickImageResultUri(data) != null) {
139                picUri = getPickImageResultUri(data);
140
141                try {
142                    myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
143                    myBitmap = rotateImageIfRequired(myBitmap, picUri);
144                    myBitmap = getResizedBitmap(myBitmap, 500);
145
146                    CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
147                    croppedImageView.setImageBitmap(myBitmap);
148                    imageView.setImageBitmap(myBitmap);
149
150                } catch (IOException e) {
151                    e.printStackTrace();
152                }
153
154            } else {
155
156                bitmap = (Bitmap) data.getExtras().get("data");
157
158                myBitmap = bitmap;
159                CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
160                if (croppedImageView != null) {
161                    croppedImageView.setImageBitmap(myBitmap);
162                }
163
164                imageView.setImageBitmap(myBitmap);
165
166            }
167
168        }
169
170    }
171
172    private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException {
173
174        ExifInterface ei = new ExifInterface(selectedImage.getPath());
175        int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
176
177        switch (orientation) {
178            case ExifInterface.ORIENTATION_ROTATE_90:
179                return rotateImage(img, 90);
180            case ExifInterface.ORIENTATION_ROTATE_180:
181                return rotateImage(img, 180);
182            case ExifInterface.ORIENTATION_ROTATE_270:
183                return rotateImage(img, 270);
184            default:
185                return img;
186        }
187    }
188
189    private static Bitmap rotateImage(Bitmap img, int degree) {
190        Matrix matrix = new Matrix();
191        matrix.postRotate(degree);
192        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
193        img.recycle();
194        return rotatedImg;
195    }
196
197    public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
198        int width = image.getWidth();
199        int height = image.getHeight();
200
201        float bitmapRatio = (float) width / (float) height;
202        if (bitmapRatio > 0) {
203            width = maxSize;
204            height = (int) (width / bitmapRatio);
205        } else {
206            height = maxSize;
207            width = (int) (height * bitmapRatio);
208        }
209        return Bitmap.createScaledBitmap(image, width, height, true);
210    }
211
212    /**
213     * Get the URI of the selected image from {@link #getPickImageChooserIntent()}.<br />
214     * Will return the correct URI for camera and gallery image.
215     *
216     * @param data the returned data of the activity result
217     */
218    public Uri getPickImageResultUri(Intent data) {
219        boolean isCamera = true;
220        if (data != null) {
221            String action = data.getAction();
222            isCamera = action != null && action.equals(MediaStore.ACTION_IMAGE_CAPTURE);
223        }
224
225        return isCamera ? getCaptureImageOutputUri() : data.getData();
226    }
227
228    @Override
229    protected void onSaveInstanceState(Bundle outState) {
230        super.onSaveInstanceState(outState);
231
232        // save file url in bundle as it will be null on scren orientation
233        // changes
234        outState.putParcelable("pic_uri", picUri);
235    }
236
237    @Override
238    protected void onRestoreInstanceState(Bundle savedInstanceState) {
239        super.onRestoreInstanceState(savedInstanceState);
240
241        // get the file url
242        picUri = savedInstanceState.getParcelable("pic_uri");
243    }
244
245    private ArrayList findUnAskedPermissions(ArrayList wanted) {
246        ArrayList result = new ArrayList();
247
248        for (String perm : wanted) {
249            if (!hasPermission(perm)) {
250                result.add(perm);
251            }
252        }
253
254        return result;
255    }
256
257    private boolean hasPermission(String permission) {
258        if (canMakeSmores()) {
259            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
260                return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
261            }
262        }
263        return true;
264    }
265
266    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
267        new AlertDialog.Builder(this)
268                .setMessage(message)
269                .setPositiveButton("OK", okListener)
270                .setNegativeButton("Cancel", null)
271                .create()
272                .show();
273    }
274
275    private boolean canMakeSmores() {
276        return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
277    }
278
279    @TargetApi(Build.VERSION_CODES.M)
280    @Override
281    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
282
283        switch (requestCode) {
284
285            case ALL_PERMISSIONS_RESULT:
286                for (String perms : permissionsToRequest) {
287                    if (hasPermission(perms)) {
288
289                    } else {
290
291                        permissionsRejected.add(perms);
292                    }
293                }
294
295                if (permissionsRejected.size() > 0) {
296
297                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
298                        if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
299                            showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
300                                    new DialogInterface.OnClickListener() {
301                                        @Override
302                                        public void onClick(DialogInterface dialog, int which) {
303                                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
304
305                                                //Log.d("API123", "permisionrejected " + permissionsRejected.size());
306
307                                                requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
308                                            }
309                                        }
310                                    });
311                            return;
312                        }
313                    }
314
315                }
316
317                break;
318        }
319
320    }
321}

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

  • 当用户开始活动时,我们需要请求摄像头运行时权限。
  • 当我们开始意图取回一些结果时,我们需要使用相关参数调用startActivityForResult
  • 我们没有使用对话框来分别调用Camera和Gallery的Intent,而是使用了一个方法getPickImageChooserIntent(),该方法为所有的Camera和Gallery Intent创建一个选择器Intent(请注意文档的Intent)。 Invent.EXTRA_INITIAL_INTTENTS** 用于将多个应用意图添加到一个位置
  • 对于摄像头意图, MediaStore.EXTRA_OUTPUT** 作为额外参数传递,指定图像存储路径。如果没有此选项,您将只返回一个小分辨率的图像。 摄像头 返回的图片的URI路径** 在方法** getCaptureImageOutputUri()** 中获取。 onActivityResult 实质上返回镜像的URI。有些设备会将位图返回为data.getExtras().get();
  • 当点击图片时,摄像头屏幕在返回时重启活动,从而导致getCaptureImageOutputUri()方法存储的URI变为空。因此,我们必须使用 onSaveInstanceState()** 和** onRestoreInstanceState()** 存储和恢复该URI。
  • 位图是从以下代码行中的URI检索的。myBitmap=MediaStore.Images.Media.getBitmap(this.getContentResolver(),Picuri);
  • 众所周知,三星Galaxy等设备可以在横向捕捉到图像。检索图像并按原样显示可能会导致图像以错误的方向显示。因此,我们将方法称为 rotateImageIfRequired(myBitmap,Picuri);**
  • ExifInterface是一个用于读取和写入JPEG文件或原始图像文件中的Exif标签的类。
  • 最后,我们调用方法 getResizedBitmap()** 来按宽度或高度(取较大者)缩放位图,并使用setImageBitmap将图像设置为图像视图。

运行中的应用程序的输出如下所示。注意:为了捕捉和显示摄像头拍摄的图像,你需要在智能手机上运行该应用程序,原因很明显。Android Capture image本教程就此结束。您可以从下面的链接下载Image Capture的Android项目。

从Camera Project下载安卓截图

Published At
Categories with 技术
Tagged with
comments powered by Disqus