Android手机多媒体——拍照和相册

怀抱观古今,寝食展戏谑。这篇文章主要讲述Android手机多媒体——拍照和相册相关的知识,希望能为你提供帮助。
【Android手机多媒体——拍照和相册】一 拍照功能
1.布局文件:在线性布局中设置一个按钮,用来启动拍照功能,设置一个ImageView用来展示图像

1 < LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2android:layout_width="match_parent" 3android:layout_height="match_parent" 4android:orientation="vertical"> 5 6< Button 7android:id="@+id/take_photo" 8android:layout_width="match_parent" 9android:layout_height="wrap_content" 10android:text="take photo" /> 11 12< ImageView 13android:id="@+id/picture" 14android:layout_width="wrap_content" 15android:layout_height="wrap_content" 16android:layout_gravity="center_horizontal" /> 17 < /LinearLayout>

2.主函数:
1 public class MainActivity extends AppCompatActivity { 2 3private Button takePhoto; 4private ImageView picture; 5public static final int TAKE_PHOTO = 1; 6private Uri imageUri; 7 8@Override 9protected void onCreate(Bundle savedInstanceState) { 10super.onCreate(savedInstanceState); 11setContentView(R.layout.activity_main); 12 13takePhoto = (Button) findViewById(R.id.take_photo); 14picture = (ImageView) findViewById(R.id.picture); 15takePhoto.setOnClickListener(new View.OnClickListener() { 16@Override 17public void onClick(View view) { 18/*创建FIle对象用于存储拍照后的图片, 19* getExternalCacheDir()用来获得应用关联目录地址,用于存储图片*/ 20File outputImage = new File(getExternalCacheDir(), "output_image.jpg"); 21try { 22if (outputImage.exists()) {//如果outputImage存在,则删除原有outputImage 23outputImage.delete(); 24} 25outputImage.createNewFile(); //创建新的文件 26} catch (IOException e) { 27e.printStackTrace(); 28} 29 30/*在Android7.0以后 就不将uri代表的真实存储路径暴露出来了, 31* 取而代之的是getURIForFile对其进行封装的uri,三个参数是context,唯一的字符串,File对象*/ 32if (Build.VERSION.SDK_INT > = 24){ 33imageUri = FileProvider.getUriForFile(MainActivity.this, 34"com.example.cameraalbumtest.fileprovider",outputImage); 35}else { 36imageUri = Uri.fromFile(outputImage); 37} 38 39/*隐式intent,只有能够相应intent活动的activity会被调用 40* 然后使用putExtra方法传输一个指定位置imageUri,使拍照后的图像存储到该位置*/ 41Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); 42intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); 43startActivityForResult(intent,TAKE_PHOTO); 44} 45}); 46} 47 48/*startActivityForResult之后回调到这个方法中,传入相关参数 49* 如果是拍照请求码,先判断是否拍照成功,成功的话通过BitmapFactory.decodeStream将其转化为bitmap格式 50* 在picture中显示出来*/ 51@Override 52protected void onActivityResult(int requestCode,int resultCode,Intent data){ 53switch (requestCode){ 54case TAKE_PHOTO: 55if(resultCode == RESULT_OK){ 56try{ 57//拍照成功,将图片显示出来 58Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); 59picture.setImageBitmap(bitmap); 60} catch (FileNotFoundException e) { 61e.printStackTrace(); 62} 63} 64break; 65default: 66break; 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 2android:name="android.support.v4.content.FileProvider" 3android:authorities="com.example.cameraalbumtest.fileprovider" 4android:exported="false" 5android:grantUriPermissions="true"> 6< meta-data 7android:name="android.support.FILE_PROVIDER_PATHS" 8android: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 5name="my_images" 6path=""/> 7 < /paths>

 
二 相册功能
在布局文件中新增了一个按钮,表示相册中选择照片
< Button android:id="@+id/choose_from_album" android:text="Choose album" android:layout_width="match_parent" android:layout_height="wrap_content" />

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

 

    推荐阅读