先在布局文件中加入两个按钮和一个图片控件
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical"> 6 7 <Button 8 android:id="@+id/take_photo" 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:text="拍照" /> 12 13 <Button 14 android:id="@+id/choose_from_album" 15 android:layout_width="match_parent" 16 android:layout_height="wrap_content" 17 android:text="从相册里选择图片"/> 18 19 <ImageView 20 android:id="@+id/picture" 21 android:layout_width="wrap_content" 22 android:layout_height="wrap_content" 23 android:layout_gravity="center_horizontal"/> 24 25 </LinearLayout>
然后先编写调用摄像头的代码:使用一个按钮来打开相机应用,然后在按钮的点击事件中调用摄像头
1 takePhoto.setOnClickListener(new View.OnClickListener() { 2 @Override 3 public void onClick(View view) { 4 //创建File对象,用于存储拍照后的照片 5 File outputImage = new File(getExternalCacheDir(),"output_image.jpg"); 6 try{ 7 if(outputImage.exists()){ 8 outputImage.delete(); 9 } 10 outputImage.createNewFile(); 11 }catch (IOException e){ 12 e.printStackTrace(); 13 } 14 15 //将outputImage的路径由File对象转换成Uri对象 16 if(Build.VERSION.SDK_INT >= 24){ 17 //如果是android7.0以上需要使用FileProvider.getUriForFile()这个方法 18 imageUri = FileProvider.getUriForFile(MainActivity.this,"xbt.exp14",outputImage); 19 }else { 20 //如果不是android7.0以上就直接调用Uri.fromFile()方法 21 imageUri = Uri.fromFile(outputImage); 22 } 23 24 //启动相机程序 25 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); 26 intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); 27 startActivityForResult(intent, TAKE_PHOTO); 28 } 29 });
先创建一个File对象用于存储摄像头拍下的照片,getExternalCacheDir()可以得到当前应用的关联缓存目录,具体就是/Android/data/(你的应用的名字)/cache
然后要进行一个判断如果设备的系统版本低于Androi7.0,就直接调用Uri的fromFile()方法直接将File对象转换为Uri对象,如果是Androi7.0以上,因为系统对直接使用本地真实路径的Uri被认为是不安全的,所以用FileProvider这样一个特殊的内容提供器将封装过的Uri共享给外部,FileProvider.getUriForFile()有三个参数,第一个参数是Context()对象,第二个参数是一个任意唯一的字符串,第三个就是要转换的File对象。
最后启动相机程序先是构建一个Intent对象,并将这个Intent的action指定为android.media.action.IMAGE_CAPTURE,再调用Intent的putExtra()方法指定图片的输出地址,再来使用startActivityForResult()启动响应这个intent。
然后还要对内容提供器进行注册:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="xbt.exp14"> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="xbt.exp14" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider> </application> </manifest>
android的属性值是固定的, android:authorities的属性值要与刚才FileProvider.getUriForFile()的第二个参数相同,<meta-data>用来指定Uri的共享路径,并引用了@xml/file_paths资源,不过这个资源需要自己创建。
右击res目录→New→Directory,创建一个xml目录,然后右击这个xml目录→New→File,创建一个file_paths.xml文件,其中的内容如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <paths xmlns:android="http://schemas.android.com/apk/res/android"> 3 <external-path name="my_images" path="" /> 4 </paths>
external-path就是用来指定Uri共享的,name 的属性值随便填,path的值表示共享的具体路径,空值就是整个SD卡。
为了兼容Android4.4之前的系统,还需要声明权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
然后是使用系统相册的具体步骤大概是:
1、验证是否拥有从sd卡读取照片的权限。
2、如果没有读取的权限需要动态申请权限。
3、打开系统相册。
4、处理返回的Uri
5、显示图片。
所以按键choose_from_album的点击事件里首先验证是否拥有从sd卡读取照片的权限。
1 chooseFromAlum.setOnClickListener(new View.OnClickListener() { 2 @Override 3 public void onClick(View view) { 4 //判断有没有使用相册的权限 5 if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ 6 ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); 7 }else{ 8 Toast.makeText(MainActivity.this,"已经获取相册权限申请",Toast.LENGTH_SHORT).show(); 9 openAlbum(); 10 } 11 } 12 }); 13 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){ 14 switch (requestCode){ 15 case 1 : 16 if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ 17 openAlbum();; 18 }else{ 19 Toast.makeText(MainActivity.this,"相册权限申请失败",Toast.LENGTH_SHORT).show(); 20 } 21 break; 22 default: 23 break; 24 } 25 }
如果已经获取了权限使用openAlbum()方法打开相册。
1 private void openAlbum(){ 2 Intent intent = new Intent("android.intent.action.GET_CONTENT"); 3 intent.setType("image/*"); 4 startActivityForResult(intent,CHOOSE_PHOTO); 5 }
在选择好图片后会返回到onActivityResult()方法中,这里先是验证系统的版本如果是Android4.4以上使用handleImageOnKitKat()方法处理返回的Uri,因为在Android系统从4.4开始选取相册中的图片就不再返回图片真实的Uri了,需要额外的解析。
1 protected void onActivityResult(int requestCode, int resultCode,Intent data){ 2 switch (requestCode){ 3 ... 4 case CHOOSE_PHOTO: 5 if(resultCode == RESULT_OK){ 6 //判断手机的系统版本号 7 if(Build.VERSION.SDK_INT >= 19) { 8 // 4.4及以上系统使用这个方法处理图片 9 handleImageOnKitKat(data); 10 } else { 11 // 4.4以下系统使用这个方法处理图片 12 handleImageBeforeKitKat(data); 13 } 14 } 15 break; 16 default: 17 break; 18 } 19 }
handleImageOnKitKat()的代码是这样:
1 private void handleImageOnKitKat (Intent data){ 2 String imagePath = null; 3 Uri uri = data.getData(); 4 if(DocumentsContract.isDocumentUri(MainActivity.this,uri)){ 5 //如果是document类型的Uri,则通过document id 处理 6 String docId = DocumentsContract.getDocumentId(uri); 7 if("com.android.providers.media.documents".equals(uri.getAuthority())){ 8 String id = docId.split(":")[1]; 9 String selection = MediaStore.Images.Media._ID + "=" + id; 10 imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); 11 }else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){ 12 Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId)); 13 imagePath = getImagePath(contentUri, null); 14 } 15 }else if("content".equalsIgnoreCase(uri.getScheme())){ 16 //如果是content类型的Uri,则使用普通的方式处理 17 imagePath = getImagePath(uri, null); 18 }else if("file".equalsIgnoreCase(uri.getScheme())){ 19 //如果是file类型的Uri,直接获取图片路径即可 20 imagePath = uri.getPath(); 21 } 22 displayImage(imagePath); 23 }
4.4以下的系统就不需要解析直接传入就好:
1 private void handleImageBeforeKitKat (Intent data){ 2 Uri uri = data.getData(); 3 String imagePath = getImagePath(uri, null); 4 displayImage(imagePath); 5 }
getImagePath()是一个通过Uri和selectio来获取图片的真实路径的方法:
1 private String getImagePath(Uri uri, String selection){ 2 String path = null; 3 Cursor cursor = getContentResolver().query(uri, null, selection, null, null); 4 if(cursor != null){ 5 if(cursor.moveToFirst()){ 6 path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); 7 } 8 cursor.close(); 9 } 10 return path; 11 }
最后就是显示图片的方法:
1 private void displayImage(String imagePath){ 2 if(imagePath != null){ 3 Bitmap bitmap = BitmapFactory.decodeFile(imagePath); 4 picture.setImageBitmap(bitmap); 5 }else { 6 Toast.makeText(MainActivity.this,"获取图片失败",Toast.LENGTH_SHORT).show(); 7 } 8 }
最后的最后就是整体的代码,
1 public class MainActivity extends AppCompatActivity { 2 3 public static final int TAKE_PHOTO = 1; 4 5 public static final int CHOOSE_PHOTO = 2; 6 7 private Uri imageUri; 8 9 private ImageView picture; 10 11 @Override 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.activity_main); 15 16 //获取按钮和图片的id 17 Button takePhoto = (Button) findViewById(R.id.take_photo); 18 Button chooseFromAlum = (Button)findViewById(R.id.choose_from_album); 19 picture = (ImageView) findViewById(R.id.picture); 20 21 // 22 takePhoto.setOnClickListener(new View.OnClickListener() { 23 @Override 24 public void onClick(View view) { 25 //创建File对象,用于存储拍照后的照片 26 File outputImage = new File(getExternalCacheDir(),"output_image.jpg"); 27 try{ 28 if(outputImage.exists()){ 29 outputImage.delete(); 30 } 31 outputImage.createNewFile(); 32 }catch (IOException e){ 33 e.printStackTrace(); 34 } 35 36 //将outputImage的路径由File对象转换成Uri对象 37 if(Build.VERSION.SDK_INT >= 24){ 38 //如果是android7.0以上需要使用FileProvider.getUriForFile()这个方法 39 imageUri = FileProvider.getUriForFile(MainActivity.this,"xbt.exp14",outputImage); 40 }else { 41 //如果不是android7.0以上就直接调用Uri.fromFile()方法 42 imageUri = Uri.fromFile(outputImage); 43 } 44 45 //启动相机程序 46 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); 47 intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); 48 startActivityForResult(intent, TAKE_PHOTO); 49 } 50 }); 51 52 chooseFromAlum.setOnClickListener(new View.OnClickListener() { 53 @Override 54 public void onClick(View view) { 55 //判断有没有使用相册的权限 56 if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ 57 ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); 58 }else{ 59 Toast.makeText(MainActivity.this,"已经获取相册权限申请",Toast.LENGTH_SHORT).show(); 60 openAlbum(); 61 } 62 } 63 }); 64 } 65 66 /* 67 打开相册 68 */ 69 private void openAlbum(){ 70 Intent intent = new Intent("android.intent.action.GET_CONTENT"); 71 intent.setType("image/*"); 72 startActivityForResult(intent,CHOOSE_PHOTO); 73 } 74 75 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){ 76 switch (requestCode){ 77 case 1 : 78 if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ 79 openAlbum();; 80 }else{ 81 Toast.makeText(MainActivity.this,"相册权限申请失败",Toast.LENGTH_SHORT).show(); 82 } 83 break; 84 default: 85 break; 86 } 87 } 88 protected void onActivityResult(int requestCode, int resultCode,Intent data){ 89 switch (requestCode){ 90 case TAKE_PHOTO: 91 if(resultCode == RESULT_OK){ 92 try { 93 //将拍摄的照片显示出来 94 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); 95 picture.setImageBitmap(bitmap); 96 Toast.makeText(MainActivity.this,"图片已显示",Toast.LENGTH_SHORT).show(); 97 } catch (FileNotFoundException e){ 98 e.printStackTrace(); 99 } 100 } 101 break; 102 case CHOOSE_PHOTO: 103 if(resultCode == RESULT_OK){ 104 //判断手机的系统版本号 105 if(Build.VERSION.SDK_INT >= 19) { 106 // 4.4及以上系统使用这个方法处理图片 107 handleImageOnKitKat(data); 108 } else { 109 // 4.4以下系统使用这个方法处理图片 110 handleImageBeforeKitKat(data); 111 } 112 } 113 break; 114 default: 115 break; 116 } 117 } 118 119 @TargetApi(19) 120 private void handleImageOnKitKat (Intent data){ 121 String imagePath = null; 122 Uri uri = data.getData(); 123 if(DocumentsContract.isDocumentUri(MainActivity.this,uri)){ 124 //如果是document类型的Uri,则通过document id 处理 125 String docId = DocumentsContract.getDocumentId(uri); 126 if("com.android.providers.media.documents".equals(uri.getAuthority())){ 127 String id = docId.split(":")[1]; 128 String selection = MediaStore.Images.Media._ID + "=" + id; 129 imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); 130 }else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){ 131 Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId)); 132 imagePath = getImagePath(contentUri, null); 133 } 134 }else if("content".equalsIgnoreCase(uri.getScheme())){ 135 //如果是content类型的Uri,则使用普通的方式处理 136 imagePath = getImagePath(uri, null); 137 }else if("file".equalsIgnoreCase(uri.getScheme())){ 138 //如果是file类型的Uri,直接获取图片路径即可 139 imagePath = uri.getPath(); 140 } 141 displayImage(imagePath); 142 } 143 144 private void handleImageBeforeKitKat (Intent data){ 145 Uri uri = data.getData(); 146 String imagePath = getImagePath(uri, null); 147 displayImage(imagePath); 148 149 } 150 151 private String getImagePath(Uri uri, String selection){ 152 String path = null; 153 Cursor cursor = getContentResolver().query(uri, null, selection, null, null); 154 if(cursor != null){ 155 if(cursor.moveToFirst()){ 156 path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); 157 } 158 cursor.close(); 159 } 160 return path; 161 } 162 163 private void displayImage(String imagePath){ 164 if(imagePath != null){ 165 Bitmap bitmap = BitmapFactory.decodeFile(imagePath); 166 picture.setImageBitmap(bitmap); 167 }else { 168 Toast.makeText(MainActivity.this,"获取图片失败",Toast.LENGTH_SHORT).show(); 169 } 170 } 171 }