在本教程中,我们将开发一个应用程序,该应用程序从相机或图库中选取一幅图像并将其显示在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截图项目结构
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将图像设置为图像视图。
运行中的应用程序的输出如下所示。注意:为了捕捉和显示摄像头拍摄的图像,你需要在智能手机上运行该应用程序,原因很明显。本教程就此结束。您可以从下面的链接下载Image Capture的Android项目。
从Camera Project下载安卓截图