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

一 拍照功能

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" />

 

主函数中整体处理逻辑流程:

  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 
 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 }

 

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

上一篇:jquery中append与appendTo方法区别


下一篇:移动端的视口/像素基本知识