一 拍照功能
1.布局文件:在线性布局中设置一个按钮,用来启动拍照功能,设置一个ImageView用来展示图像
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical"> 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 <ImageView 13 android:id="@+id/picture" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:layout_gravity="center_horizontal" /> 17 </LinearLayout>
2.主函数:
1 public class MainActivity extends AppCompatActivity { 2 3 private Button takePhoto; 4 private ImageView picture; 5 public static final int TAKE_PHOTO = 1; 6 private Uri imageUri; 7 8 @Override 9 protected void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 setContentView(R.layout.activity_main); 12 13 takePhoto = (Button) findViewById(R.id.take_photo); 14 picture = (ImageView) findViewById(R.id.picture); 15 takePhoto.setOnClickListener(new View.OnClickListener() { 16 @Override 17 public void onClick(View view) { 18 /*创建FIle对象用于存储拍照后的图片, 19 * getExternalCacheDir()用来获得应用关联目录地址,用于存储图片*/ 20 File outputImage = new File(getExternalCacheDir(), "output_image.jpg"); 21 try { 22 if (outputImage.exists()) { //如果outputImage存在,则删除原有outputImage 23 outputImage.delete(); 24 } 25 outputImage.createNewFile(); //创建新的文件 26 } catch (IOException e) { 27 e.printStackTrace(); 28 } 29 30 /*在Android7.0以后 就不将uri代表的真实存储路径暴露出来了, 31 * 取而代之的是getURIForFile对其进行封装的uri,三个参数是context,唯一的字符串,File对象*/ 32 if (Build.VERSION.SDK_INT >= 24){ 33 imageUri = FileProvider.getUriForFile(MainActivity.this, 34 "com.example.cameraalbumtest.fileprovider",outputImage); 35 }else { 36 imageUri = Uri.fromFile(outputImage); 37 } 38 39 /*隐式intent,只有能够相应intent活动的activity会被调用 40 * 然后使用putExtra方法传输一个指定位置imageUri,使拍照后的图像存储到该位置*/ 41 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); 42 intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); 43 startActivityForResult(intent,TAKE_PHOTO); 44 } 45 }); 46 } 47 48 /*startActivityForResult之后回调到这个方法中,传入相关参数 49 * 如果是拍照请求码,先判断是否拍照成功,成功的话通过BitmapFactory.decodeStream将其转化为bitmap格式 50 * 在picture中显示出来*/ 51 @Override 52 protected void onActivityResult(int requestCode,int resultCode,Intent data){ 53 switch (requestCode){ 54 case TAKE_PHOTO: 55 if(resultCode == RESULT_OK){ 56 try{ 57 //拍照成功,将图片显示出来 58 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); 59 picture.setImageBitmap(bitmap); 60 } catch (FileNotFoundException e) { 61 e.printStackTrace(); 62 } 63 } 64 break; 65 default: 66 break; 67 } 68 } 69 }
函数主要可以分为三部分。首先对按键,图像,uri进行声明,并定义一个常量Take_Photo为1。
1.第一部分为创建file部分
创建一个outputImage,是file(文件)类型,存放的是应用关联目录的地址,这个目录是应用的临时文件目录,所有也不需要申请sd卡存放权限,
然后进行判断,如果outputImage中存在文件,即有拍照过的照片储存,则删除原有的outputImage。然后创建一个新的文件。
2.第二部分为Intent部分
首先进行判断,在Android7.0以后 就不将uri代表的真实存储路径暴露出来了,取而代之的是fileProvider提供器中getURIForFile()方法对其进行封装的uri,该方法的三个参数是context,唯一的字符串,File对象,如果是7.0之前的话,则可以直接通过Uri.fromFile()对file直接进行解析。
然后创建Intent,隐式intent,只有能够响应intent活动的activity会被调用。然后intent中传输的是一个uri标识,即:使用putExtra方法传输一个指定位置imageUri,使拍照后的图像存储到该位置。
然后使用 startActivityForResult()方法启动intent。
3.第三部分为intent的回调部分
startActivityForResult之后回调到这个方法中,传入相关参数。
如果是拍照请求码,先判断是否拍照成功,成功的话通过BitmapFactory.decodeStream将其转化为bitmap格式,在picture中显示出来。
3.权限声明
因为在第二部分对uri进行封装的时候使用到了FileProvider提供器,所以需要对其在AndroidManifest.xml中进行相关注册。
1 <provider 2 android:name="android.support.v4.content.FileProvider" 3 android:authorities="com.example.cameraalbumtest.fileprovider" 4 android:exported="false" 5 android:grantUriPermissions="true"> 6 <meta-data 7 android:name="android.support.FILE_PROVIDER_PATHS" 8 android:resource="@xml/file_paths" /> 9 </provider>
其中Android:name是固定的,authorities值与之前对uri封装是的唯一标识符(标黄)是相同的,使用<meta-data>来指定uri的共享路径。引用了一个 @xml/file_paths 资源。
在res中新建xml文件夹,新建一个file类型的file_paths.xml文件:用来指定uri共享,path中为空即整个SD卡都共享。
1 <?xml version="1.0" encoding="utf-8"?> 2 <!--suppress ALL --> 3 <paths xmlns:android="http://schemas.android.com/apk/res/android"> 4 <external-path 5 name="my_images" 6 path=""/> 7 </paths>
二 相册功能
在布局文件中新增了一个按钮,表示相册中选择照片
<Button android:id="@+id/choose_from_album" android:text="Choose album" android:layout_width="match_parent" android:layout_height="wrap_content" />
主函数中整体处理逻辑流程:
- 在主函数中,为选择相册按钮添加点击事件,
- 先是一个申请授权访问SD卡的操作,(先判断是否有权限,没有权限进行询问,有权限下一步。 询问时,同意进行下一步,不同意则弹出反馈信息)
- 获得授权之后会执行openAlbum方法,构造一个Intent对象,设置相应的动作。调用startActivityForResult()方法就可以访问相册选择照片了,该方法第二个参数为Choose photo是一个常量2。
- 选择完图片后,进入回调函数onActivityForResult(),进入到Choose Photo的case来进行处理,这是进行一个判断,判断手机的版本。4.4和4.4以上的版本调用 handleImageOnKitKat()方法处理,以下的调用 handleImageBeforeKitKat()方法处理。
- handleImageOnKitKat()方法逻辑主要是如何解析封装过的Uri,分为三种大情况。如果是document类型的话,使用document id进行处理;如果不是就用普通方法来处理。然后用解析后的uri和条件语句调用getImagePath()方法来获取真实路径。最后调用displayImage()方法进行展示
- handleImageBeforeKitKat()方法不需要解析,直接将uri传入getImagePath()方法获取真实路径。然后调用displayImage()进行展示。
- getImagePath()方法通过uri和条件语句来获取真实路径。
- displayImage()方法,如果路径不为空,则将其转化为bitmap格式,然后在imageView中进行展示,否则弹出反馈信息。
1 package com.example.cameraalbumtest; 2 3 import android.Manifest; 4 import android.annotation.TargetApi; 5 import android.content.ContentUris; 6 import android.content.Intent; 7 import android.content.pm.PackageManager; 8 import android.database.Cursor; 9 import android.graphics.Bitmap; 10 import android.graphics.BitmapFactory; 11 import android.net.Uri; 12 import android.os.Build; 13 import android.provider.DocumentsContract; 14 import android.provider.MediaStore; 15 import android.support.v4.app.ActivityCompat; 16 import android.support.v4.content.ContextCompat; 17 import android.support.v4.content.FileProvider; 18 import android.support.v7.app.AppCompatActivity; 19 import android.os.Bundle; 20 import android.view.View; 21 import android.widget.Button; 22 import android.widget.ImageView; 23 import android.widget.Toast; 24 25 import java.io.File; 26 import java.io.FileNotFoundException; 27 import java.io.IOException; 28 29 public class MainActivity extends AppCompatActivity { 30 31 private Button takePhoto; 32 private Button choosePhoto; 33 private ImageView picture; 34 public static final int TAKE_PHOTO = 1; 35 public static final int CHOOSE_PHOTO = 2; 36 private Uri imageUri; 37 38 @Override 39 protected void onCreate(Bundle savedInstanceState) { 40 super.onCreate(savedInstanceState); 41 setContentView(R.layout.activity_main); 42 43 takePhoto = (Button) findViewById(R.id.take_photo); 44 picture = (ImageView) findViewById(R.id.picture); 45 takePhoto.setOnClickListener(new View.OnClickListener() { 46 @Override 47 public void onClick(View view) { 48 /*创建FIle对象用于存储拍照后的图片, 49 * getExternalCacheDir()用来获得应用关联目录地址,用于存储图片*/ 50 File outputImage = new File(getExternalCacheDir(), "output_image.jpg"); 51 try { 52 if (outputImage.exists()) { //如果outputImage存在,则删除原有outputImage 53 outputImage.delete(); 54 } 55 outputImage.createNewFile(); //创建新的文件 56 } catch (IOException e) { 57 e.printStackTrace(); 58 } 59 60 /*在Android7.0以后 就不将uri代表的真实存储路径暴露出来了, 61 * 取而代之的是getURIForFile对其进行封装的uri,三个参数是context,唯一的字符串,File对象*/ 62 if (Build.VERSION.SDK_INT >= 24) { 63 imageUri = FileProvider.getUriForFile(MainActivity.this, 64 "com.example.cameraalbumtest.fileprovider", outputImage); 65 } else { 66 imageUri = Uri.fromFile(outputImage); 67 } 68 69 /*隐式intent,只有能够响应intent活动的activity会被调用 70 * 然后使用putExtra方法传输一个指定位置imageUri,使拍照后的图像存储到该位置*/ 71 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); 72 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); 73 startActivityForResult(intent, TAKE_PHOTO); 74 } 75 }); 76 77 choosePhoto = (Button) findViewById(R.id.choose_from_album); 78 choosePhoto.setOnClickListener(new View.OnClickListener() { 79 @Override 80 public void onClick(View view) { 81 if (ContextCompat.checkSelfPermission(MainActivity.this, 82 Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 83 ActivityCompat.requestPermissions(MainActivity.this, 84 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); 85 } else { 86 openAlbum(); 87 } 88 } 89 }); 90 } 91 92 private void openAlbum() { 93 Intent intent = new Intent("android.intent.action.GET_CONTENT"); 94 intent.setType("image/*"); //读取的文件类型:照片 95 startActivityForResult(intent, CHOOSE_PHOTO); 96 } 97 98 @Override 99 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 100 switch (requestCode) { 101 case 1: 102 if (grantResults.length >= 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 103 openAlbum(); 104 } else { 105 Toast.makeText(this, "未获得相册授权", Toast.LENGTH_LONG).show(); 106 } 107 break; 108 default: 109 break; 110 } 111 } 112 113 /*startActivityForResult之后回调到这个方法中,传入相关参数 114 * 如果是拍照请求码,先判断是否拍照成功,成功的话通过BitmapFactory.decodeStream将其转化为bitmap格式 115 * 在picture中显示出来*/ 116 @Override 117 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 118 switch (requestCode) { 119 case TAKE_PHOTO: 120 if (resultCode == RESULT_OK) { 121 try { 122 //拍照成功,将图片显示出来 123 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); 124 picture.setImageBitmap(bitmap); 125 } catch (FileNotFoundException e) { 126 e.printStackTrace(); 127 } 128 } 129 break; 130 case CHOOSE_PHOTO: 131 if (resultCode == RESULT_OK) { 132 //判断手机版本 133 if (Build.VERSION.SDK_INT >= 19) { 134 //4.4和以上的系统使用这个方法处理照片 135 handleImageOnKitKat(data); 136 } else { 137 //4.4以下的系统使用这个方法 138 handleImageBeforeKitKat(data); 139 } 140 } 141 default: 142 break; 143 } 144 } 145 146 @TargetApi(19) 147 private void handleImageOnKitKat(Intent data) { 148 /*因为intent传输来的uri进行了封装,要对其uri进行解析, 149 * 分三种大情况进行解析,解析出图片的文件路径。*/ 150 String imagePath = null; 151 Uri uri = data.getData(); 152 if (DocumentsContract.isDocumentUri(this, uri)) { 153 //如果是document(文件)类型的Uri,则通过document id来处理 154 String docId = DocumentsContract.getDocumentId(uri); 155 if ("com.android.providers.media.documents".equals(uri.getAuthority())) { 156 String id = docId.split(":")[1]; //解析出数字格式的id 157 String selection = MediaStore.Images.Media._ID + "=" + id; 158 imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); 159 } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { 160 Uri contentUri = ContentUris.withAppendedId 161 (Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId)); 162 imagePath = getImagePath(contentUri, null); 163 } 164 } else if ("content".equalsIgnoreCase(uri.getScheme())) { 165 //如果是content类型的uri,则使用普通方法处理 166 imagePath = getImagePath(uri, null); 167 } else if ("file".equalsIgnoreCase(uri.getScheme())) { 168 //如果是file类型的uri,直接获取图片路径就可以 169 imagePath = uri.getPath(); 170 } 171 /*解析完uri,直接根据图片路径展示图片*/ 172 displayImage(imagePath); 173 } 174 175 private void handleImageBeforeKitKat(Intent data) { 176 Toast.makeText(this, "你的Android版本太低", Toast.LENGTH_LONG).show(); 177 Uri uri = data.getData(); 178 String imagePath = getImagePath(uri, null); 179 displayImage(imagePath); 180 } 181 182 private String getImagePath(Uri uri, String selection) { 183 //通过Uri和seletion来获取真实图片地址 184 String path = null; 185 Cursor cursor = getContentResolver().query(uri, null, selection, null, null); 186 if (cursor != null) { 187 if (cursor.moveToFirst()) { 188 path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); 189 } 190 cursor.close(); 191 } 192 return path; 193 } 194 195 /*将图片转化为Bitmap格式,在下面展现出来*/ 196 private void displayImage(String imagePath) { 197 if (imagePath!=null) { 198 Bitmap bitmap = BitmapFactory.decodeFile(imagePath); 199 picture.setImageBitmap(bitmap); 200 }else{ 201 Toast.makeText(this,"无法获得图片",Toast.LENGTH_LONG).show(); 202 } 203 } 204 }