Android学习——从相册中选择照片

虽然调用摄像头拍照既方便又快捷,但我们并不是每次都需要去当场拍一张照片的。因为每个人的手机相册里应该都会存有许许多多张照片,直接从相册里选取一张现有的照片会比打开相机拍一张照片更加常用。一个优秀的应用程序应该将这两种选择方式都提供给用户,由用户来决定使用哪一种。下面我们就来看一下,如何才能实现从相册中选择照片的功能。

还是在CameraAlbumTest项目的基础上进行修改,编辑activity_main.xml文件,在布局中添加一个按钮用于从相册中选择照片,代码如下所示:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:orientation="vertical"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent" >
 5  
 6     <Button
 7         android:id="@+id/take_photo"
 8         android:layout_width="match_parent"
 9         android:layout_height="wrap_content"
10         android:text="Take Photo"
11         />
12  
13     <ImageView
14         android:id="@+id/picture"
15         android:layout_width="wrap_content"
16         android:layout_height="wrap_content"
17         android:layout_gravity="center_horizontal"
18         />
19  
20     <Button
21         android:id="@+id/choose_from_album"
22         android:layout_width="match_parent"
23         android:layout_height="wrap_content"
24         android:text="Choose from Album"
25         />
26  
27  
28 </LinearLayout>
  1 package com.example.cameraalbumtest;
  2  
  3 import androidx.appcompat.app.AppCompatActivity;
  4 import androidx.core.app.ActivityCompat;
  5 import androidx.core.content.ContextCompat;
  6 import androidx.core.content.FileProvider;
  7  
  8 import android.Manifest;
  9 import android.annotation.TargetApi;
 10 import android.content.ContentUris;
 11 import android.content.Intent;
 12 import android.content.pm.PackageManager;
 13 import android.database.Cursor;
 14 import android.graphics.Bitmap;
 15 import android.graphics.BitmapFactory;
 16 import android.net.Uri;
 17 import android.os.Build;
 18 import android.os.Bundle;
 19 import android.provider.DocumentsContract;
 20 import android.provider.MediaStore;
 21 import android.view.View;
 22 import android.widget.Button;
 23 import android.widget.ImageView;
 24 import android.widget.Toast;
 25  
 26 import java.io.File;
 27 import java.io.FileNotFoundException;
 28 import java.io.IOException;
 29  
 30 public class MainActivity extends AppCompatActivity {
 31  
 32     public static final int TAKE_PHOTO=1;//拍照
 33     public static final int CHOOSE_PHOTO=2;//从相册取照片
 34  
 35     private ImageView picture;
 36  
 37     private Uri imageUri;
 38  
 39     @Override
 40     protected void onCreate(Bundle savedInstanceState) {
 41         super.onCreate(savedInstanceState);
 42         setContentView(R.layout.activity_main);
 43  
 44         //获取实例
 45         Button takePhoto=(Button) findViewById(R.id.take_photo);
 46         picture =(ImageView) findViewById(R.id.picture);
 47  
 48         takePhoto.setOnClickListener(new View.OnClickListener() {
 49             @Override
 50             public void onClick(View v) {
 51                 //创建file对象,用于存储拍照后的图片
 52                 File outputImage=new File(getExternalCacheDir(),"output_image.jpg");//把图片进行命名
 53                 //调用getExternalCacheDir()可以得到手机SD卡的应用关联缓存目录
 54                 //所谓的应用关联缓存目录,就是指SD卡中专门用于存放当前应用缓存数据的位置
 55                 //具体的路径是/sdcard/Android/data/<package name>/cache
 56  
 57                 //因为从Android 6.0系统开始,读写SD卡被列为了危险权限,
 58                 // 如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用应用关联目录则可以跳过这一步。
 59                 try {
 60                     if (outputImage.exists()) {//如果已经存在了图片,则删掉,
 61                         outputImage.delete();
 62                     }
 63                     outputImage.createNewFile();//将图片放入
 64                 }catch (IOException e){
 65                     e.printStackTrace();
 66                 }
 67  
 68                 //获取Uri对象
 69                 //这个Uri对象标识着output_image.jpg这张图片的本地真实路径。
 70                 if (Build.VERSION.SDK_INT >= 24) {
 71                     //调用FileProvider的getUriForFile() 方法将File 对象转换成一个封装过的Uri对象
 72                     imageUri = FileProvider.getUriForFile(MainActivity.this,"com.example.cameraalbumtest.fileprovider", outputImage);
 73                     //FileProvider则是一种特殊的内容提供器,它使用了和内容提供器类似的机制来对数据进行保护,
 74                     // 可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。
 75                     //第一个参数要求传入Context 对象
 76                     //第二个参数可以是任意唯一的字符串 (需要在AndroidManifest.xml中声明)
 77                     //第三个参数则是我们刚刚创建的File 对象
 78                 } else {//若系统的版本低于Android7.0,则调用下面的方法将File对象转换为Uri对象
 79                     imageUri = Uri.fromFile(outputImage);
 80                 }
 81  
 82  
 83                 //启动相机程序
 84                 Intent intent= new Intent("android.media.action.IMAGE_CAPTURE");
 85                 intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);//指定图片的输出地址
 86                 startActivityForResult(intent,TAKE_PHOTO);//调用startActivityForResult() 来启动活动。
 87             }
 88         });
 89  
 90         //从相册中取图片
 91         Button chooseFromAlbum=(Button) findViewById(R.id.choose_from_album);
 92         chooseFromAlbum.setOnClickListener(new View.OnClickListener() {
 93             @Override
 94             public void onClick(View v) {//定义该按钮点击事件
 95                 //申请一个运行时权限处理
 96                 //权限WRITE_EXTERNAL_STORAGE表示同时授予程序对SD卡读和写的能力。
 97                 if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED)
 98                 {
 99                     ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
100                 }else {//授予了权限后,则调用openAlbum方法来获取图片
101                     openAlbum();
102                 }
103  
104             }
105         });
106  
107     }
108  
109     //定义openAlbum()方法来获取图片
110     private void openAlbum(){
111         Intent intent =new Intent("android.intent.action.GET_CONTENT");//构建一个intent对象,并将它的action指定
112         intent.setType("image/*");
113         startActivityForResult(intent, CHOOSE_PHOTO);//打开相册程序,选择照片
114         //给第二个参数传入的值变成了CHOOSE_PHOTO
115         // 这样当从相册选择完图片回到onActivityResult() 方法时
116         // 就会进入CHOOSE_PHOTO 的case 来处理图片。
117     }
118  
119     @Override
120     public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults){
121         switch (requestCode){
122             case 1:
123                 if (grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
124                     openAlbum();
125                 }else {
126                     Toast.makeText(this,"You denied the permission",Toast.LENGTH_SHORT).show();
127                 }
128                 break;
129             default:
130                 break;
131         }
132     }
133  
134  
135     //使用startActivityForResult() 来启动活动的,
136     // 因此拍完照后会有结果返回到onActivityResult() 方法中。
137     //在此函数中显示图像
138     @Override
139     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
140         super.onActivityResult(requestCode, resultCode, data);
141         switch (requestCode) {
142             case TAKE_PHOTO://拍照
143                 if (resultCode == RESULT_OK) {//如果拍照成功
144                     try {
145                         // 将拍摄的照片显示出来
146                         //可以调用BitmapFactory的decodeStream() 方法将output_image.jpg这张照片解析成Bitmap 对象
147                         Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
148                         picture.setImageBitmap(bitmap);//将Bitmap对象,设置到ImageView中显示出来。
149                     } catch (FileNotFoundException e) {
150                         e.printStackTrace();
151                     }
152                 }
153                 break;
154             case CHOOSE_PHOTO://打开相册
155                 if (resultCode == RESULT_OK){
156                     //判断手机的系统的版本号
157                     if (Build.VERSION.SDK_INT>=19){
158                         //4.4及以上系统使用这个方法处理图片
159                         handleImageOnKitKat(data);
160                     } else {
161                         // 4.4以下系统使用这个方法处理图片
162                         handleImageBeforeKitKat(data);
163                     }
164                 }
165             default:
166                 break;
167         }
168     }
169  
170     //因为Android系统从4.4版本开始,选取相册中的图片不再返回图片真实的Uri了,而是一个封装过的Uri
171     // 因此如果是4.4版本以上的手机就需要对这个Uri进行解析才行。
172     @TargetApi(19)
173     private void handleImageOnKitKat(Intent data) {//用于解析Android4.4版本以上的封装过的Uri
174         String imagePath = null;
175         Uri uri = data.getData();
176  
177         if (DocumentsContract.isDocumentUri(this, uri)) {
178             // 如果是document类型的Uri,则通过document id处理
179             String docId = DocumentsContract.getDocumentId(uri);
180             if("com.android.providers.media.documents".equals(uri.getAuthority())) {
181                 //如果Uri的authority是media格式的话,document id 还需要再进行一次解析
182                 //要通过字符串分割的方式取出后半部分才能得到真正的数字id
183                 String id = docId.split(":")[1]; // 解析出数字格式的id
184                 String selection = MediaStore.Images.Media._ID + "=" + id;
185                 imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
186             } else if ("com.android.providers.downloads.documents".equals(uri. getAuthority())) {
187                 Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
188                 imagePath = getImagePath(contentUri, null);
189             }
190         } else if ("content".equalsIgnoreCase(uri.getScheme())) {
191             // 如果是content类型的Uri,则使用普通方式处理
192             imagePath = getImagePath(uri, null);
193         } else if ("file".equalsIgnoreCase(uri.getScheme())) {
194             // 如果是file类型的Uri,直接获取图片路径即可
195             imagePath = uri.getPath();
196         }
197         displayImage(imagePath); // 根据图片路径显示图片
198     }
199  
200     //它的Uri是没有封装过的,不需要任何解析
201     private void handleImageBeforeKitKat(Intent data) {
202         Uri uri = data.getData();
203         String imagePath = getImagePath(uri, null);//直接将Uri传入到getImagePath() 方法当中就能获取到图片的真实路径了
204         displayImage(imagePath);//让图片显示到界面上
205     }
206  
207     //获取到图片的真实路径了
208     private String getImagePath(Uri uri, String selection) {
209         String path = null;
210         // 通过Uri和selection来获取真实的图片路径
211         Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
212         if (cursor != null) {
213             if (cursor.moveToFirst()) {
214                 path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
215             }
216             cursor.close();
217         }
218         return path;
219     }
220  
221  
222     //将图片显示到界面上
223     private void displayImage(String imagePath) {
224         if (imagePath != null) {
225             Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
226             picture.setImageBitmap(bitmap);
227         } else {
228             Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
229         }
230     }
231 }
原文地址:https://www.cnblogs.com/znjy/p/14908533.html