有时候,我们想在程序内打开图库,从中挑选图片,并复制图片(或移动图片)到本程序关联目录下。在android 4.4版本中的Uri是经过封装的,所以调用起来需要解析路径,比较麻烦。下面是我实践过的调取图片的方法:
一、打开图库
在打开图库之前,需要动态申请存储空间的访问权限(我的测试手机没有插SD卡)。申请权限之前,一定要先在AndroidManifest.xml中加入这样一条语句(不然无法获得存储空间的访问权限):
放得位置是这样的:
xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.lenovo.testapp"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> intent-filter> activity> application> manifest>
然后,点击按钮动态申请申请存储空间访问权限,如果已经获取权限,可直接打开图库:
@Override public void onClick(View v){ switch (v.getId()) { case R.id.openalbum: /*动态申请访问存储空间的权限*/ if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); }else{ openAlbum(); } break; } } /* *@description 开启系统相册 */ private void openAlbum(){ Intent intent=new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,1); }
实现效果如图1所示:
图 1 打开相册
二、获取图片路径
刚才只是打开了图库,并没有对选择的图片进行任何处理,所以你会发现点击任意一张图片后又回到了前一个界面。现在我们来获取图片的路径。
考虑最后一行代码:startActivityForResult(intent,1);
你可能会好奇startActivityForResult的第二个参数1是干嘛用的,其实1是一个代号,是为了后面处理图片而设的代码。当从相册中选择完图片回到startActivityForResult( )方法时,就会进入onActivityResult( )方法中的1 的case来处理图片。
@Override protected void onActivityResult(int requestCode, int resultCode,Intent data) { switch (requestCode) { case 1: if (resultCode == RESULT_OK) { //判断手机系统版本号 if (Build.VERSION.SDK_INT >= 19) { //4.4以上版本使用这个方法处理图片 handleMediaOnKitKat(data, CHOOSE_PHOTO); } else { //4.4以下系统使用这个方法处理图片 handleImageBeforeKitKat(data, CHOOSE_PHOTO); } } break; } }
/* *@description 4.4以下版本媒体文件路径的获取 */ private void handleImageBeforeKitKat(Intent data,int choice){ Uri uri=data.getData(); String mediaPath=getMediaPath(uri,null,choice); }
/* *@description 4.4版本及以上媒体文件路径的获取 * */ @TargetApi(19) private void handleMediaOnKitKat(Intent data,int choice){ String mediaPath=null; Uri uri=data.getData(); if(DocumentsContract.isDocumentUri(this,uri)){ //如果是document类型的Uri,则通过document id处理 String docId=DocumentsContract.getDocumentId(uri); if("com.android.providers.media.documents".equals(uri.getAuthority())){ String id=docId.split(":")[1];//解析出数字格式的id String selection; if(choice==CHOOSE_PHOTO){ selection= MediaStore.Images.Media._ID+"="+id; mediaPath=getMediaPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection,choice); } }else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){ Uri contentUri= ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId)); mediaPath=getMediaPath(contentUri,null,choice); } }else if("content".equalsIgnoreCase(uri.getScheme())){ //如果是content类型的Uri,则用普通方式处理 mediaPath=getMediaPath(uri,null,choice); }else if("file".equalsIgnoreCase(uri.getScheme())){ //如果是file类型的Uri,直接获取文件路径即可 mediaPath=uri.getPath(); } } /*获取多媒体文件的路径*/ private String getMediaPath(Uri uri,String selection,int choice){ String path=null; Cursor cursor; cursor=getContentResolver().query(uri,null,selection,null,null); if(cursor!=null){ if(cursor.moveToFirst()){ path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } String splitarr[]=path.split("/"); if(choice==CHOOSE_PHOTO); image_name=path.split("/")[splitarr.length-1]; //将text的值设为路径。这里要先将text变量设为全局的,以便在不同的方法中调用。 text.setText(path); return path; }
效果如下图所示:
当然,如果你想把图片显示出来的话,需要用到Bitmap:
我们在private String getMediaPath(Uri uri,String selection,int choice)方法下的text.setText(mediaPath);语句后再添加一条语句,用来显示图片:
displayImage(mediaPath);
displayImage(mediaPath)方法的实现如下所示:
private void displayImage(String mediaPath){ if(mediaPath!=null){ Bitmap bitmap= BitmapFactory.decodeFile(mediaPath); imageView.setImageBitmap(bitmap); }else{ Toast.makeText(this,"获取图片失败",Toast.LENGTH_SHORT).show(); } }
最后的效果是这样的:
附上各个按钮图标的定义和声明:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ public static final int CHOOSE_PHOTO=1; public TextView text; public ImageView imageView;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button image_btn=(Button)findViewById(R.id.openalbum); imageView=(ImageView)findViewById(R.id.image); text=(TextView)findViewById(R.id.text); image_btn.setOnClickListener(this); }
}
三、将图片文件移动到程序关联目录下
在上述操作中,虽然我们已经将图片显示在程序中了,但图片文件依旧是存储在原来的位置下。我们可能会想要将图片文件复制到与应用程序关联的目录中来。我们在private String getMediaPath(Uri uri,String selection,int choice)方法下的
displayImage(String mediaPath);
语句后再添加一条语句,
fileCopy(String fromFile,int choice);
用来复制图片文件。其中,getFilesDir()获取路径,将文件从系统目录下复制到/data/data/< package name >/files/…,
fileCopy(String fromFile,int choice)方法的实现如下:
/* *@description 将指定路径下的文件复制成关联目录下的指定类型文件 *@param fromFile 被复制文件路径 *@param choice 文件类型 *@return */ public int fileCopy(String fromFile,int choice) { try { InputStream fosfrom = new FileInputStream(fromFile); OutputStream fosto= new FileOutputStream(getFilesDir().toString()+"/"+image_name); byte bt[] = new byte[1024]; int c; while ((c = fosfrom.read(bt)) > 0) { fosto.write(bt, 0, c); } fosfrom.close(); fosto.close(); return 0; } catch (Exception ex) { return -1; } }
四、删除原图片文件
如果你想要将图片文件移到程序关联的目录下,并删除原系统目录下的图片文件的话,请继续往下看。
上一节在private String getMediaPath(Uri uri,String selection,int choice)下调用fileCopy(path)实现图片文件的复制,现在我们想升级我们的方法,实现复制并删除原路径下的图片。新建一个方法:copyAndDeletImage(path),该方法的实现如下:
public void copyAndDeleteImage(String path){ if(path!=null){ fileCopy(path); try{ //resolver.delete 能够在删除图库文件后并清空其缓存 ContentResolver resolver = this.getContentResolver(); resolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.DATA+"=\""+path+"\"",null); Toast.makeText(MainActivity.this,"已删除原位置文件", Toast.LENGTH_SHORT).show(); }catch (Exception e){ e.printStackTrace(); Toast.makeText(this,"删除原位置文件出错",Toast.LENGTH_SHORT).show(); } } }
在getImagePath()方法中,将fileCopy(path)换成copyAndDeleteImage(path);即可。
五、完整代码如下:
布局文件:
xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.lenovo.testapp.MainActivity"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/openalbum" android:text="选择照片"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/text"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/image"/> LinearLayout>代码:
package com.example.lenovo.testapp; import android.Manifest; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ public static final int CHOOSE_PHOTO=1; public TextView text; public ImageView imageView; public String image_name; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button image_btn=(Button)findViewById(R.id.openalbum); imageView=(ImageView)findViewById(R.id.image); text=(TextView)findViewById(R.id.text); image_btn.setOnClickListener(this); } @Override public void onClick(View v){ switch (v.getId()) { case R.id.openalbum: /*动态申请访问存储空间的权限*/ if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); }else{ openAlbum(); } break; } } /* *@description 开启系统相册 */ private void openAlbum(){ Intent intent=new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,CHOOSE_PHOTO); } /*响应相册点击事件*/ @Override protected void onActivityResult(int requestCode, int resultCode,Intent data) { switch (requestCode) { case 1: if (resultCode == RESULT_OK) { //判断手机系统版本号 if (Build.VERSION.SDK_INT >= 19) { //4.4以上版本使用这个方法处理图片 handleMediaOnKitKat(data, CHOOSE_PHOTO); } else { //4.4以下系统使用这个方法处理图片 handleImageBeforeKitKat(data, CHOOSE_PHOTO); } } break; } } /* *@description 4.4版本及以上媒体文件路径的获取 * */ @TargetApi(19) private void handleMediaOnKitKat(Intent data,int choice){ String mediaPath=null; Uri uri=data.getData(); if(DocumentsContract.isDocumentUri(this,uri)){ //如果是document类型的Uri,则通过document id处理 String docId=DocumentsContract.getDocumentId(uri); if("com.android.providers.media.documents".equals(uri.getAuthority())){ String id=docId.split(":")[1];//解析出数字格式的id String selection; if(choice==CHOOSE_PHOTO){ selection= MediaStore.Images.Media._ID+"="+id; mediaPath=getMediaPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection,choice); } }else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){ Uri contentUri= ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId)); mediaPath=getMediaPath(contentUri,null,choice); } }else if("content".equalsIgnoreCase(uri.getScheme())){ //如果是content类型的Uri,则用普通方式处理 mediaPath=getMediaPath(uri,null,choice); }else if("file".equalsIgnoreCase(uri.getScheme())){ //如果是file类型的Uri,直接获取文件路径即可 mediaPath=uri.getPath(); } } /*获取多媒体文件的路径*/ private String getMediaPath(Uri uri,String selection,int choice){ String path=null; Cursor cursor; cursor=getContentResolver().query(uri,null,selection,null,null); if(cursor!=null){ if(cursor.moveToFirst()){ path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } String splitarr[]=path.split("/"); if(choice==CHOOSE_PHOTO); image_name=path.split("/")[splitarr.length-1]; //将text的值设为路径。这里要先将text变量设为全局的,以便在不同的方法中调用。 text.setText(path); displayImage(path); copyAndDeleteImage(path); return path; } /* *@description 4.4以下版本媒体文件路径的获取 */ private void handleImageBeforeKitKat(Intent data,int choice){ Uri uri=data.getData(); String mediaPath=getMediaPath(uri,null,choice); } private void displayImage(String mediaPath){ if(mediaPath!=null){ Bitmap bitmap= BitmapFactory.decodeFile(mediaPath); imageView.setImageBitmap(bitmap); }else{ Toast.makeText(this,"获取图片失败",Toast.LENGTH_SHORT).show(); } } /* *@description 将指定路径下的文件复制成关联目录下的指定类型文件 *@param fromFile 被复制文件路径 *@param choice 文件类型 *@return */ public int fileCopy(String fromFile) { try { InputStream fosfrom = new FileInputStream(fromFile); OutputStream fosto= new FileOutputStream(getFilesDir().toString()+"/"+image_name); byte bt[] = new byte[1024]; int c; while ((c = fosfrom.read(bt)) > 0) { fosto.write(bt, 0, c); } fosfrom.close(); fosto.close(); return 0; } catch (Exception ex) { return -1; } } public void copyAndDeleteImage(String path){ if(path!=null){ fileCopy(path); try{ //resolver.delete 能够在删除图库文件后并清空其缓存 ContentResolver resolver = this.getContentResolver(); resolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.DATA+"=\""+path+"\"",null); Toast.makeText(MainActivity.this,"已删除原位置文件", Toast.LENGTH_SHORT).show(); }catch (Exception e){ e.printStackTrace(); Toast.makeText(this,"删除原位置文件出错",Toast.LENGTH_SHORT).show(); } } } }