以编程方式从 Android 的内置图库应用程序中获取/选择图像

Posted

技术标签:

【中文标题】以编程方式从 Android 的内置图库应用程序中获取/选择图像【英文标题】:Get/pick an image from Android's built-in Gallery app programmatically 【发布时间】:2011-01-11 07:06:58 【问题描述】:

我正在尝试从我的应用程序内部打开图库内置应用程序中的图像/图片。

我有图片的 URI(图片位于 SD 卡上)。

你有什么建议吗?

【问题讨论】:

我已更新我的答案以提供更多测试代码以确保您正确检索结果。 看看我的回答,它是对 hcpl 代码的更新,它也适用于 astro 文件管理器和 oi 文件管理器。 有人应该更新问题“从 android 的...获取/选择图像”。当前问题解释为我有图像,我想通过默认图库应用显示它。 @Vikas,看来你是对的。我不记得一年多前我到底试图完成什么,以及为什么所有答案(包括我选择作为解决方案的答案)实际上都回答了不同的问题...... 其实我也不知道完全换个问题对不对。有 36 人将该问题添加到他们的收藏夹... 【参考方案1】:

这是一个完整的解决方案。我刚刚使用@mad 在下面的答案中提供的信息更新了这个示例代码。另请查看@Khobaib 提供的以下解决方案,说明如何处理 picasa 图像。

更新

我刚刚查看了我的原始答案并创建了一个简单的 Android Studio 项目,您可以从 github 签出并直接在您的系统上导入。

https://github.com/hanscappelle/SO-2169649

(请注意,多文件选择仍然需要工作)

单张图片选择

感谢用户 mad,支持来自文件浏览器的图像。

public class BrowsePictureActivity extends Activity 

    // this is the action code we use in our intent, 
    // this way we know we're looking at the response from our own action
    private static final int SELECT_PICTURE = 1;

    private String selectedImagePath;

    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        findViewById(R.id.Button01)
                .setOnClickListener(new OnClickListener() 

                    public void onClick(View arg0) 

                        // in onCreate or any event where your want the user to
                        // select a file
                        Intent intent = new Intent();
                        intent.setType("image/*");
                        intent.setAction(Intent.ACTION_GET_CONTENT);
                        startActivityForResult(Intent.createChooser(intent,
                                "Select Picture"), SELECT_PICTURE);
                    
                );
    

    public void onActivityResult(int requestCode, int resultCode, Intent data) 
        if (resultCode == RESULT_OK) 
            if (requestCode == SELECT_PICTURE) 
                Uri selectedImageUri = data.getData();
                selectedImagePath = getPath(selectedImageUri);
            
        
    

    /**
     * helper to retrieve the path of an image URI
     */
    public String getPath(Uri uri) 
            // just some safety built in 
            if( uri == null ) 
                // TODO perform some logging or show user feedback
                return null;
            
            // try to retrieve the image from the media store first
            // this will only work for images selected from gallery
            String[] projection =  MediaStore.Images.Media.DATA ;
            Cursor cursor = managedQuery(uri, projection, null, null, null);
            if( cursor != null )
                int column_index = cursor
                .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                cursor.moveToFirst();
                String path = cursor.getString(column_index);
                cursor.close();
                return path;
            
            // this is our fallback here
            return uri.getPath();
    


选择多张图片

由于有人在评论中要求提供该信息,因此最好收集信息。

在意图上设置一个额外的参数EXTRA_ALLOW_MULTIPLE

intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);

并在结果处理中检查该参数:

if (Intent.ACTION_SEND_MULTIPLE.equals(data.getAction()))
        && Intent.hasExtra(Intent.EXTRA_STREAM)) 
    // retrieve a collection of selected images
    ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
    // iterate over these images
    if( list != null ) 
       for (Parcelable parcel : list) 
         Uri uri = (Uri) parcel;
         // TODO handle the images one by one here
       
   
 

请注意,这仅受 API 级别 18+ 支持。

【讨论】:

使用 Astro 文件管理器时它不起作用。任何想法为什么? @hcpl 谢谢你的回答。你能告诉我如何获取多张图片吗...? 获取单张图片似乎不再适用于最新的 Android 版本 4.4 (KitKat)。查询中的 _data 列返回空值。 @hcpl 你的意思不是'Intent.hasExtra',你的意思是'data.hasExtra' - 假设数据是onActivityResult()中的Intent参数。 这段代码在Kitkat之前运行良好,但从那时起就有了文档提供者。在我的回答中,我写了在 kitkat 中做什么。【参考方案2】:

这是对 hcpl 发布的精细代码的更新。但这也适用于 OI 文件管理器、astro 文件管理器和媒体库(已测试)。所以我想它适用于每个文件管理器(除了提到的那些之外,还有很多其他的吗?)。对他写的代码做了一些更正。

public class BrowsePicture extends Activity 

    //YOU CAN EDIT THIS TO WHATEVER YOU WANT
    private static final int SELECT_PICTURE = 1;

    private String selectedImagePath;
    //ADDED
    private String filemanagerstring;

    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ((Button) findViewById(R.id.Button01))
        .setOnClickListener(new OnClickListener() 

            public void onClick(View arg0) 

                // in onCreate or any event where your want the user to
                // select a file
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent,
                        "Select Picture"), SELECT_PICTURE);
            
        );
    

    //UPDATED
    public void onActivityResult(int requestCode, int resultCode, Intent data) 
        if (resultCode == RESULT_OK) 
            if (requestCode == SELECT_PICTURE) 
                Uri selectedImageUri = data.getData();

                //OI FILE Manager
                filemanagerstring = selectedImageUri.getPath();

                //MEDIA GALLERY
                selectedImagePath = getPath(selectedImageUri);

                //DEBUG PURPOSE - you can delete this if you want
                if(selectedImagePath!=null)
                    System.out.println(selectedImagePath);
                else System.out.println("selectedImagePath is null");
                if(filemanagerstring!=null)
                    System.out.println(filemanagerstring);
                else System.out.println("filemanagerstring is null");

                //NOW WE HAVE OUR WANTED STRING
                if(selectedImagePath!=null)
                    System.out.println("selectedImagePath is the right one for you!");
                else
                    System.out.println("filemanagerstring is the right one for you!");
            
        
    

    //UPDATED!
    public String getPath(Uri uri) 
        String[] projection =  MediaStore.Images.Media.DATA ;
        Cursor cursor = managedQuery(uri, projection, null, null, null);
        if(cursor!=null)
        
            //HERE YOU WILL GET A NULLPOINTER IF CURSOR IS NULL
            //THIS CAN BE, IF YOU USED OI FILE MANAGER FOR PICKING THE MEDIA
            int column_index = cursor
            .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        
        else return null;
    

【讨论】:

如何从 OI 路径中检索位图图像? 看代码。在带有评论的行//现在我们有我们想要的字符串......这就是你所需要的。然后使用 BitmapFactory 类从路径中检索位图 谢谢!你说得很好,从未尝试过其他文件管理器:)。 我也想关闭光标:) 还可以将getPath(Uri uri) 中的else return null; 更改为return uri.getPath(); 并去掉第一个filemanagerstring = selectedImageUri.getPath(); 检查。这样,您只需拨打一个电话 getPath(Uri) 并取回路径(无论使用的是图库还是文件管理器)。【参考方案3】:

hcpl 的方法在 KitKat 之前完美运行,但不适用于 DocumentsProvider API。为此,只需遵循文档提供者的官方 Android 教程:https://developer.android.com/guide/topics/providers/document-provider.html -> 打开文档,位图部分。

我只是使用了 hcpl 的代码并对其进行了扩展:如果具有检索到的图像路径的文件抛出异常,我将调用此函数:

private Bitmap getBitmapFromUri(Uri uri) throws IOException 
        ParcelFileDescriptor parcelFileDescriptor =
             getContentResolver().openFileDescriptor(uri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        parcelFileDescriptor.close();
        return image;

在 Nexus 5 上测试。

【讨论】:

真正有用的答案,应该用于从“照片”应用中选择图像。 我在本地和服务器的文件上都试过这个,对两者都有效。【参考方案4】:

根据上面的代码,我反映的代码如下,可能更合适:

public String getPath(Uri uri) 
    String selectedImagePath;
    //1:MEDIA GALLERY --- query from MediaStore.Images.Media.DATA
    String[] projection =  MediaStore.Images.Media.DATA ;
    Cursor cursor = managedQuery(uri, projection, null, null, null);
    if(cursor != null)
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        selectedImagePath = cursor.getString(column_index);
    else
        selectedImagePath = null;
    

    if(selectedImagePath == null)
        //2:OI FILE Manager --- call method: uri.getPath()
        selectedImagePath = uri.getPath();
    
    return selectedImagePath;

【讨论】:

【参考方案5】:

我查看了@hcpl 和@mad 的解决方案。 hcpl 的解决方案很好地支持图库中的本地图像,并且 mad 在此之上提供了更好的解决方案 - 它也有助于加载 OI/Astro/Dropbox 图像。但在我的应用中,在处理现已集成到 Android 图库中的 picasa 库时,两种解决方案都失败了。

我进行了一些搜索和分析,最终提出了一个更好、更优雅的解决方案来克服这个限制。感谢Dimitar Darazhanski 的博客,在这种情况下帮助了我,我做了一些修改以使其更容易理解。这是我的解决方案-

public class BrowsePicture extends Activity 

//YOU CAN EDIT THIS TO WHATEVER YOU WANT
private static final int SELECT_PICTURE = 1;

private String selectedImagePath;
//ADDED
private String filemanagerstring;

public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ((Button) findViewById(R.id.Button01))
    .setOnClickListener(new OnClickListener() 

        public void onClick(View arg0) 

            // in onCreate or any event where your want the user to
            // select a file
            Intent intent = new Intent();
            intent.setType("image/*");
            intent.setAction(Intent.ACTION_GET_CONTENT);
            startActivityForResult(Intent.createChooser(intent,
                    "Select Picture"), SELECT_PICTURE);
        
    );


public void onActivityResult(int requestCode, int resultCode, Intent data) 
    if (resultCode == RESULT_OK) 
        if (requestCode == SELECT_PICTURE) 
            Uri selectedImageUri = data.getData();
            Log.d("URI VAL", "selectedImageUri = " + selectedImageUri.toString());
            selectedImagePath = getPath(selectedImageUri);

            if(selectedImagePath!=null)         
                // IF LOCAL IMAGE, NO MATTER IF ITS DIRECTLY FROM GALLERY (EXCEPT PICASSA ALBUM),
                // OR OI/ASTRO FILE MANAGER. EVEN DROPBOX IS SUPPORTED BY THIS BECAUSE DROPBOX DOWNLOAD THE IMAGE 
                // IN THIS FORM - file:///storage/emulated/0/Android/data/com.dropbox.android/...
                System.out.println("local image"); 
            
            else
                System.out.println("picasa image!");
                loadPicasaImageFromGallery(selectedImageUri);
            
        
    



// NEW METHOD FOR PICASA IMAGE LOAD
private void loadPicasaImageFromGallery(final Uri uri) 
    String[] projection =   MediaColumns.DATA, MediaColumns.DISPLAY_NAME ;
    Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
    if(cursor != null) 
        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
        if (columnIndex != -1) 
            new Thread(new Runnable() 
                // NEW THREAD BECAUSE NETWORK REQUEST WILL BE MADE THAT WILL BE A LONG PROCESS & BLOCK UI
                // IF CALLED IN UI THREAD 
                public void run() 
                    try 
                        Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
                        // THIS IS THE BITMAP IMAGE WE ARE LOOKING FOR.
                     catch (Exception ex) 
                        ex.printStackTrace();
                    
                
            ).start();
        
    
    cursor.close();



public String getPath(Uri uri) 
    String[] projection =   MediaColumns.DATA;
    Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
    if(cursor != null) 
        //HERE YOU WILL GET A NULLPOINTER IF CURSOR IS NULL
        //THIS CAN BE, IF YOU USED OI FILE MANAGER FOR PICKING THE MEDIA
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndexOrThrow(MediaColumns.DATA);
        String filePath = cursor.getString(columnIndex);
        cursor.close();
        return filePath;
    
    else 
        return uri.getPath();               // FOR OI/ASTRO/Dropbox etc

检查一下,如果有问题,请告诉我。我已经对其进行了测试,并且在每种情况下都运行良好。

希望对大家有所帮助。

【讨论】:

【参考方案6】:

假设您的 SD 卡目录中有一个 image 文件夹,仅用于图片。

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// tells your intent to get the contents
// opens the URI for your image directory on your sdcard
intent.setType("file:///sdcard/image/*"); 
startActivityForResult(intent, 1);

然后,您可以决定要对活动中的内容做什么。

这是一个检索图像路径名的示例,使用您的代码对此进行测试,以确保您可以处理返回的结果。您可以根据需要更改代码以更好地满足您的需求。

protected final void onActivityResult(final int requestCode, final int
                     resultCode, final Intent i) 
    super.onActivityResult(requestCode, resultCode, i);

  // this matches the request code in the above call
  if (requestCode == 1) 
      Uri _uri = i.getData();

    // this will be null if no image was selected...
    if (_uri != null) 
      // now we get the path to the image file
     cursor = getContentResolver().query(_uri, null,
                                      null, null, null);
     cursor.moveToFirst();
     String imageFilePath = cursor.getString(0);
     cursor.close();
     
   

我的建议是尝试让检索图像正常工作,我认为问题在于访问 sdcard 上的图像的内容。看看Displaying images on sd card

如果您可以启动并运行它,可能通过提供正确提供程序的示例,您应该能够为您的代码找到解决方法。

通过使用您的进度更新此问题来让我了解最新情况。祝你好运

【讨论】:

@Anthony,感谢您的回复。不幸的是,它对我不起作用。我得到下一个错误:android.content.ActivityNotFoundException: No Activity found to handle Intent act=android.intent.action.GET_CONTENT typ=file:///sdcard/images/* 您需要致电startActivityforResult 并提供活动。这就是我所说的决定下一步做什么,我的错。 它仍然不起作用...我检查该文件夹是否存在并且该文件夹内是否有一个图像文件。我打电话给startActivityForResult(intent, 1);,但仍然出现此错误...此代码位于活动之外,但我有对该活动的引用并在该引用上调用startActivityForResult 方法-也许这就是原因? 不,不应该是这个原因,你传入的1是什么?试试IMAGE_PICK 第二个参数对我来说只是一些东西,不是吗?这只是一个 int,将与结果一起传回给我。还尝试了Intent.ACTION_PICK 而不是Intent.ACTION_GET_CONTENTIMAGE_PICK 是什么意思?没有这样的常数。我也试过intent.setData(Uri.fromFile(new File("/sdcard/image/")));。我尝试了所有可能的组合,但似乎没有任何效果......【参考方案7】:

这是我对这个主题的重新审视,在这里收集所有信息,以及其他相关的堆栈溢出问题。它从一些提供者返回图像,同时处理内存不足的情况和图像旋转。它支持图库、picasa 和文件管理器,如 drop box。用法很简单:作为输入,构造函数接收内容解析器和 uri。输出是最终的位图。

/**
 * Creates resized images without exploding memory. Uses the method described in android
 * documentation concerning bitmap allocation, which is to subsample the image to a smaller size,
 * close to some expected size. This is required because the android standard library is unable to
 * create a reduced size image from an image file using memory comparable to the final size (and
 * loading a full sized multi-megapixel picture for processing may exceed application memory budget).
 */

public class UserPicture 
    static int MAX_WIDTH = 600;
    static int MAX_HEIGHT = 800;
    Uri uri;
    ContentResolver resolver;
    String path;
    Matrix orientation;
    int storedHeight;
    int storedWidth;

    public UserPicture(Uri uri, ContentResolver resolver) 
        this.uri = uri;
        this.resolver = resolver;
    

    private boolean getInformation() throws IOException 
        if (getInformationFromMediaDatabase())
            return true;

        if (getInformationFromFileSystem())
            return true;

        return false;
    

    /* Support for gallery apps and remote ("picasa") images */
    private boolean getInformationFromMediaDatabase() 
        String[] fields =  Media.DATA, ImageColumns.ORIENTATION ;
        Cursor cursor = resolver.query(uri, fields, null, null, null);

        if (cursor == null)
            return false;

        cursor.moveToFirst();
        path = cursor.getString(cursor.getColumnIndex(Media.DATA));
        int orientation = cursor.getInt(cursor.getColumnIndex(ImageColumns.ORIENTATION));
        this.orientation = new Matrix();
        this.orientation.setRotate(orientation);
        cursor.close();

        return true;
    

    /* Support for file managers and dropbox */
    private boolean getInformationFromFileSystem() throws IOException 
        path = uri.getPath();

        if (path == null)
            return false;

        ExifInterface exif = new ExifInterface(path);
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                                               ExifInterface.ORIENTATION_NORMAL);

        this.orientation = new Matrix();
        switch(orientation) 
            case ExifInterface.ORIENTATION_NORMAL:
                /* Identity matrix */
                break;
            case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                this.orientation.setScale(-1, 1);
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                this.orientation.setRotate(180);
                break;
            case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                this.orientation.setScale(1, -1);
                break;
            case ExifInterface.ORIENTATION_TRANSPOSE:
                this.orientation.setRotate(90);
                this.orientation.postScale(-1, 1);
                break;
            case ExifInterface.ORIENTATION_ROTATE_90:
                this.orientation.setRotate(90);
                break;
            case ExifInterface.ORIENTATION_TRANSVERSE:
                this.orientation.setRotate(-90);
                this.orientation.postScale(-1, 1);
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                this.orientation.setRotate(-90);
                break;
        

        return true;
    

    private boolean getStoredDimensions() throws IOException 
        InputStream input = resolver.openInputStream(uri);
        Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options);

        /* The input stream could be reset instead of closed and reopened if it were possible
           to reliably wrap the input stream on a buffered stream, but it's not possible because
           decodeStream() places an upper read limit of 1024 bytes for a reset to be made (it calls
           mark(1024) on the stream). */
        input.close();

        if (options.outHeight <= 0 || options.outWidth <= 0)
            return false;

        storedHeight = options.outHeight;
        storedWidth = options.outWidth;

        return true;
    

    public Bitmap getBitmap() throws IOException 
        if (!getInformation())
            throw new FileNotFoundException();

        if (!getStoredDimensions())
            throw new InvalidObjectException(null);

        RectF rect = new RectF(0, 0, storedWidth, storedHeight);
        orientation.mapRect(rect);
        int width = (int)rect.width();
        int height = (int)rect.height();
        int subSample = 1;

        while (width > MAX_WIDTH || height > MAX_HEIGHT) 
            width /= 2;
            height /= 2;
            subSample *= 2;
        

        if (width == 0 || height == 0)
            throw new InvalidObjectException(null);

        Options options = new Options();
        options.inSampleSize = subSample;
        Bitmap subSampled = BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options);

        Bitmap picture;
        if (!orientation.isIdentity()) 
            picture = Bitmap.createBitmap(subSampled, 0, 0, options.outWidth, options.outHeight,
                                          orientation, false);
            subSampled.recycle();
         else
            picture = subSampled;

        return picture;
    

参考资料:

http://developer.android.com/training/displaying-bitmaps/index.html Get/pick an image from Android's built-in Gallery app programmatically Strange out of memory issue while loading an image to a Bitmap object Set image orientation using ExifInterface https://gist.github.com/9re/1990019 how to get bitmap information and then decode bitmap from internet-inputStream?

【讨论】:

【参考方案8】:

这里有两个关于图像选择器的有用教程和可下载的源代码:

How to Create Android Image Picker

How to Select and Crop Image on Android

但是,应用程序有时会被强制关闭,您可以通过在 Manifest 文件的主要活动中添加 android:configChanges 属性来修复它,如下所示:

<activity android:name=".MainActivity"
                  android:label="@string/app_name" android:configChanges="keyboardHidden|orientation" >

似乎相机 API 失去了对方向的控制,所以这会有所帮助。 :)

【讨论】:

【参考方案9】:

要显示图像和视频,试试这个:

    Intent intent = new Intent();
    intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, 1);
    startActivityForResult(Intent.createChooser(intent,"Wybierz plik"), SELECT_FILE);

【讨论】:

【参考方案10】:

以下解决方案也适用于 2.3(Gingerbread)-4.4(Kitkat)、5.0(Lollipop) 和 6.0(Marshmallow):-

第一步打开图库选择图片的代码:

public static final int PICK_IMAGE = 1;
private void takePictureFromGalleryOrAnyOtherFolder() 

    Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE);

第二步获取onActivityResult中数据的代码:

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) 
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == Activity.RESULT_OK) 
               if (requestCode == PICK_IMAGE) 
                    Uri selectedImageUri = data.getData();
                    String imagePath = getRealPathFromURI(selectedImageUri);
                   //Now you have imagePath do whatever you want to do now
                 //end of inner if
             //end of outer if
      

 public String getRealPathFromURI(Uri contentUri) 
        //Uri contentUri = Uri.parse(contentURI);

        String[] projection =  MediaStore.Images.Media.DATA ;
        Cursor cursor = null;
        try 
            if (Build.VERSION.SDK_INT > 19) 
                // Will return "image:x*"
                String wholeID = DocumentsContract.getDocumentId(contentUri);
                // Split at colon, use second item in the array
                String id = wholeID.split(":")[1];
                // where id is equal to
                String sel = MediaStore.Images.Media._ID + "=?";

                cursor = context.getContentResolver().query(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        projection, sel, new String[]  id , null);
             else 
                cursor = context.getContentResolver().query(contentUri,
                        projection, null, null, null);
            
         catch (Exception e) 
            e.printStackTrace();
        

        String path = null;
        try 
            int column_index = cursor
                    .getColumnIndex(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            path = cursor.getString(column_index).toString();
            cursor.close();
         catch (NullPointerException e) 
            e.printStackTrace();
        
        return path;
    

【讨论】:

它对我有用 我正在使用 Nexus 5 和 Marshmallow。您使用的是哪部手机。 我也使用了 google nexus,但无法获取我的画廊选择的图像的图像名称或路径我得到了 null java.lang.SecurityException: Permission Denial: 从 pid=31332 读取 com.android.providers.media.MediaProvider uri content://media/external/images/media,uid=11859 需要 android。 permission.READ_EXTERNAL_STORAGE 或 grantUriPermission() 收到此错误 您已经添加了显示READ_EXTERNAL_STORAGE的权限 是的,我已经在清单中添加了权限,但我没有在运行时在 java 文件中添加任何权限【参考方案11】:

以防万一;我这样做是为了获取位图:

InputStream is = context.getContentResolver().openInputStream(imageUri);
Bitmap bitmap = BitmapFactory.decodeStream(is);

【讨论】:

这是最简单的答案(也是唯一对我有用的答案)。做得很好!【参考方案12】:

以上答案是正确的。我遇到了一个不同的问题,在 HTC M8 中,我的应用程序在从图库中选择图像时崩溃。我得到图像路径的空值。我使用以下解决方案进行了修复和优化。在 onActivityResult 方法中

protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    super.onActivityResult(requestCode, resultCode, data);
    if ((requestCode == RESULT_LOAD_IMAGE) && (resultCode == RESULT_OK)) 
     if (data != null) 

            Uri selectedImageUri = null;
            selectedImageUri = data.getData();
            String[] filePathColumn =  MediaStore.Images.Media.DATA ;

            Cursor imageCursor = mainActivity.getContentResolver().query(
                    selectedImageUri, filePathColumn, null, null, null);

            if (imageCursor == null) 
                return;
            

            imageCursor.moveToFirst();
            int columnIndex = imageCursor.getColumnIndex(filePathColumn[0]);
            picturePath = imageCursor.getString(columnIndex);
            if (picturePath == null) 
                picturePath = selectedImageUri.getPath();
                String wholeID = DocumentsContract
                        .getDocumentId(selectedImage);

                // Split at colon, use second item in the array
                String id = wholeID.split(":")[1];

                String[] column =  MediaStore.Images.Media.DATA ;

                // where id is equal to
                String sel = MediaStore.Images.Media._ID + "=?";

                Cursor cursor = mainActivity.getContentResolver().query(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        column, sel, new String[]  id , null);

                columnIndex = cursor.getColumnIndex(column[0]);

                if (cursor.moveToFirst()) 
                    picturePath = cursor.getString(columnIndex);
                

                cursor.close();
            
            picturePathAbs = new File(picturePath).getAbsolutePath();
            imageCursor.close();
        

【讨论】:

我遇到了类似的问题,并且 picturePath 始终为 null 。我尝试了你的解决方案但不起作用,而且 getDocumentId 需要> API 19【参考方案13】:
package com.ImageConvertingDemo;

import java.io.BufferedInputStream;
import java.io.FileInputStream;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.EditText;
import android.widget.ImageView;

public class MyActivity extends Activity 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        EditText tv = (EditText)findViewById(R.id.EditText01);
        ImageView iv = (ImageView)findViewById(R.id.ImageView01);
        FileInputStream in;
        BufferedInputStream buf;
            try 
            
                in = new FileInputStream("/sdcard/smooth.png");
                buf = new BufferedInputStream(in,1070);
                System.out.println("1.................."+buf);
                byte[] bMapArray= new byte[buf.available()];
                tv.setText(bMapArray.toString());
                buf.read(bMapArray);
                Bitmap bMap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);

                /*for (int i = 0; i < bMapArray.length; i++) 
                
                System.out.print("bytearray"+bMapArray[i]);
                */
                iv.setImageBitmap(bMap);
                //tv.setText(bMapArray.toString());
                //tv.setText(buf.toString());
                if (in != null) 
                
                    in.close();
                
                if (buf != null) 
                
                    buf.close();
                

             
            catch (Exception e) 
            
                Log.e("Error reading file", e.toString());
            
    

【讨论】:

您在回答之前没有理解问题。此外,您没有包含有关您尝试做什么的任何描述。此外,您的代码违反了一些完善的 Java 编程标准。【参考方案14】:
public class BrowsePictureActivity extends Activity 

    // this is the action code we use in our intent, 
    // this way we know we're looking at the response from our own action
    private static final int SELECT_PICTURE = 1;

    private String selectedImagePath;

    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ((Button) findViewById(R.id.Button01))
                .setOnClickListener(new OnClickListener() 

                    public void onClick(View arg0) 

                        // in onCreate or any event where your want the user to
                        // select a file
                        Intent intent = new Intent();
                        intent.setType("image/*");
                        intent.setAction(Intent.ACTION_GET_CONTENT);
                        startActivityForResult(Intent.createChooser(intent,
                                "Select Picture"), SELECT_PICTURE);
                    
                );
    

    public void onActivityResult(int requestCode, int resultCode, Intent data) 
        if (resultCode == RESULT_OK) 
            if (requestCode == SELECT_PICTURE) 
                Uri selectedImageUri = data.getData();
                selectedImagePath = getPath(selectedImageUri);
            
        
    

    /**
     * helper to retrieve the path of an image URI
     */
    public String getPath(Uri uri) 
            // just some safety built in 
            if( uri == null ) 
                // TODO perform some logging or show user feedback
                return null;
            
            // try to retrieve the image from the media store first
            // this will only work for images selected from gallery
            String[] projection =  MediaStore.Images.Media.DATA ;
            Cursor cursor = managedQuery(uri, projection, null, null, null);
            if( cursor != null )
                int column_index = cursor
                .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                cursor.moveToFirst();
                return cursor.getString(column_index);
            
            // this is our fallback here
            return uri.getPath();
    


【讨论】:

【参考方案15】:

检索特定类型的文件

此示例将获取图像的副本。

static final int REQUEST_IMAGE_GET = 1;

public void selectImage() 
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    if (intent.resolveActivity(getPackageManager()) != null) 
        startActivityForResult(intent, REQUEST_IMAGE_GET);
    


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    if (requestCode == REQUEST_IMAGE_GET && resultCode == RESULT_OK) 
        Bitmap thumbnail = data.getParcelable("data");
        Uri fullPhotoUri = data.getData();
        // Do work with photo saved at fullPhotoUri
        ...
    

打开特定类型的文件

在 4.4 或更高版本上运行时,您请求打开由其他应用管理的文件

static final int REQUEST_IMAGE_OPEN = 1;

public void selectImage() 
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.setType("image/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    // Only the system receives the ACTION_OPEN_DOCUMENT, so no need to test.
    startActivityForResult(intent, REQUEST_IMAGE_OPEN);


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    if (requestCode == REQUEST_IMAGE_OPEN && resultCode == RESULT_OK) 
        Uri fullPhotoUri = data.getData();
        // Do work with full size photo saved at fullPhotoUri
        ...
    

Original source

【讨论】:

【参考方案16】:

除了以前的答案,如果您在获取正确路径(如 AndroZip)时遇到问题,您可以使用:

  public String getPath(Uri uri ,ContentResolver contentResolver) 
        String[] projection =   MediaStore.MediaColumns.DATA;
        Cursor cursor;
        try
            cursor = contentResolver.query(uri, projection, null, null, null);
         catch (SecurityException e)
            String path = uri.getPath();
            String result = tryToGetStoragePath(path);
            return  result;
        
        if(cursor != null) 
            //HERE YOU WILL GET A NULLPOINTER IF CURSOR IS NULL
            //THIS CAN BE, IF YOU USED OI FILE MANAGER FOR PICKING THE MEDIA
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
            String filePath = cursor.getString(columnIndex);
            cursor.close();
            return filePath;
        
        else
            return uri.getPath();               // FOR OI/ASTRO/Dropbox etc
    

    private String tryToGetStoragePath(String path) 
        int actualPathStart = path.indexOf("//storage");
        String result = path;

        if(actualPathStart!= -1 && actualPathStart< path.length())
            result = path.substring(actualPathStart+1 , path.length());

        return result;
    

【讨论】:

这里需要知道图片的uri吗?如果我只想从图库中选择任意图像怎么办? 您将从图库中收到的 Intent 将为您提供网址【参考方案17】:

请找到从图库中选择单个图像的答案

import android.app.Activity;
import android.net.Uri;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class PickImage extends Activity 

    Button btnOpen, btnGet, btnPick;
    TextView textInfo1, textInfo2;
    ImageView imageView;

    private static final int RQS_OPEN_IMAGE = 1;
    private static final int RQS_GET_IMAGE = 2;
    private static final int RQS_PICK_IMAGE = 3;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_pick);
        btnOpen = (Button)findViewById(R.id.open);
        btnGet = (Button)findViewById(R.id.get);
        btnPick = (Button)findViewById(R.id.pick);
        textInfo1 = (TextView)findViewById(R.id.info1);
        textInfo2 = (TextView)findViewById(R.id.info2);
        imageView = (ImageView) findViewById(R.id.image);

        btnOpen.setOnClickListener(btnOpenOnClickListener);
        btnGet.setOnClickListener(btnGetOnClickListener);
        btnPick.setOnClickListener(btnPickOnClickListener);
    

    View.OnClickListener btnOpenOnClickListener = new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("image/*");

            startActivityForResult(intent, RQS_OPEN_IMAGE);
        
    ;

    View.OnClickListener btnGetOnClickListener = new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_GET_CONTENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("image/*");

            startActivityForResult(intent, RQS_OPEN_IMAGE);
        
    ;

    View.OnClickListener btnPickOnClickListener = new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            Intent intent = new Intent(Intent.ACTION_PICK,
                    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            startActivityForResult(intent, RQS_PICK_IMAGE);
        
    ;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) 
        if (resultCode == Activity.RESULT_OK) 


            if (requestCode == RQS_OPEN_IMAGE ||
                    requestCode == RQS_GET_IMAGE ||
                    requestCode == RQS_PICK_IMAGE) 

                imageView.setImageBitmap(null);
                textInfo1.setText("");
                textInfo2.setText("");

                Uri mediaUri = data.getData();
                textInfo1.setText(mediaUri.toString());
                String mediaPath = mediaUri.getPath();
                textInfo2.setText(mediaPath);

                //display the image
                try 
                    InputStream inputStream = getBaseContext().getContentResolver().openInputStream(mediaUri);
                    Bitmap bm = BitmapFactory.decodeStream(inputStream);

                   ByteArrayOutputStream stream = new ByteArrayOutputStream();
                    byte[] byteArray = stream.toByteArray();

                    imageView.setImageBitmap(bm);

                 catch (FileNotFoundException e) 
                    e.printStackTrace();
                
            
        
    

【讨论】:

【参考方案18】:

从图库或相机中打开图像的最快方式。

原文参考:get image from gallery in android programmatically

以下方法将从图库或相机接收图像并将其显示在 ImageView 中。选定的图像将在内部存储。

xml 代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:orientation="vertical"
    tools:context="com.exampledemo.parsaniahardik.uploadgalleryimage.MainActivity">

    <Button
        android:layout_
        android:layout_
        android:id="@+id/btn"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Capture Image and upload to server" />

    <TextView
        android:layout_
        android:layout_
        android:text="Below image is fetched from server"
        android:layout_marginTop="5dp"
        android:textSize="23sp"
        android:gravity="center"
        android:textColor="#000"/>

    <ImageView
        android:layout_
        android:layout_
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:scaleType="fitXY"
        android:src="@mipmap/ic_launcher"
        android:id="@+id/iv"/>

</LinearLayout>

JAVA类

import android.content.Intent;
import android.graphics.Bitmap;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.androidquery.AQuery;
import org.json.JSONException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.HashMap;

public class MainActivity extends AppCompatActivity implements AsyncTaskCompleteListener

    private ParseContent parseContent;
    private Button btn;
    private ImageView imageview;
    private static final String IMAGE_DIRECTORY = "/demonuts_upload_camera";
    private final int CAMERA = 1;
    private AQuery aQuery;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        parseContent = new ParseContent(this);
        aQuery = new AQuery(this);

        btn = (Button) findViewById(R.id.btn);
        imageview = (ImageView) findViewById(R.id.iv);

        btn.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(intent, CAMERA);
            
        );

    

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) 

        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == this.RESULT_CANCELED) 
            return;
        
        if (requestCode == CAMERA) 
            Bitmap thumbnail = (Bitmap) data.getExtras().get("data");
            String path = saveImage(thumbnail);
            try 
                uploadImageToServer(path);
             catch (IOException e) 
                e.printStackTrace();
             catch (JSONException e) 
                e.printStackTrace();
            
        
    

    private void uploadImageToServer(final String path) throws IOException, JSONException 

        if (!AndyUtils.isNetworkAvailable(MainActivity.this)) 
            Toast.makeText(MainActivity.this, "Internet is required!", Toast.LENGTH_SHORT).show();
            return;
        

        HashMap<String, String> map = new HashMap<String, String>();
        map.put("url", "https://demonuts.com/Demonuts/JsonTest/Tennis/uploadfile.php");
        map.put("filename", path);
        new MultiPartRequester(this, map, CAMERA, this);
        AndyUtils.showSimpleProgressDialog(this);
    

    @Override
    public void onTaskCompleted(String response, int serviceCode) 
        AndyUtils.removeSimpleProgressDialog();
        Log.d("res", response.toString());
        switch (serviceCode) 

            case CAMERA:
                if (parseContent.isSuccess(response)) 
                    String url = parseContent.getURL(response);
                    aQuery.id(imageview).image(url);
                
        
    

    public String saveImage(Bitmap myBitmap) 
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes);
        File wallpaperDirectory = new File(
                Environment.getExternalStorageDirectory() + IMAGE_DIRECTORY);
        // have the object build the directory structure, if needed.
        if (!wallpaperDirectory.exists()) 
            wallpaperDirectory.mkdirs();
        

        try 
            File f = new File(wallpaperDirectory, Calendar.getInstance()
                    .getTimeInMillis() + ".jpg");
            f.createNewFile();
            FileOutputStream fo = new FileOutputStream(f);
            fo.write(bytes.toByteArray());
            MediaScannerConnection.scanFile(this,
                    new String[]f.getPath(),
                    new String[]"image/jpeg", null);
            fo.close();
            Log.d("TAG", "File Saved::--->" + f.getAbsolutePath());

            return f.getAbsolutePath();
         catch (IOException e1) 
            e1.printStackTrace();
        
        return "";
    

【讨论】:

【参考方案19】:

这是我的例子,可能与你的情况不完全一样。


假设你从你的 API 提供者那里得到 base64 格式,给它一个文件名和文件扩展名,把它保存到文件系统中的某个位置。

public static void shownInBuiltInGallery(final Context ctx, String strBase64Image, final String strFileName, final String strFileExtension)

new AsyncTask<String, String, File>() 
    @Override
    protected File doInBackground(String... strBase64Image) 

        Bitmap bmpImage = convertBase64StringToBitmap(strBase64Image[0], Base64.NO_WRAP);

        if(bmpImage == null) 
            cancel(true);
            return null;
        

        byte[] byImage = null;

        if(strFileExtension.compareToIgnoreCase(FILE_EXTENSION_JPG) == 0) 
            byImage = convertToJpgByte(bmpImage); // convert bitmap to binary for latter use
         else if(strFileExtension.compareToIgnoreCase(FILE_EXTENSION_PNG) == 0)
            byImage = convertToPngByte(bmpImage); // convert bitmap to binary for latter use
         else if(strFileExtension.compareToIgnoreCase(FILE_EXTENSION_BMP) == 0)
            byImage = convertToBmpByte(bmpImage); // convert bitmap to binary for latter use
         else 
            cancel(true);
            return null;
        

        if(byImage == null) 
            cancel(true);
            return null;
        

        File imageFolder = ctx.getExternalCacheDir();

        if(imageFolder.exists() == false)
            if(imageFolder.mkdirs() == false)
                cancel(true);
                return null;
            
        

        File imageFile = null;

        try 
            imageFile = File.createTempFile(strFileName, strFileExtension, imageFolder);
         catch (IOException e)
            e.printStackTrace();
        

        if(imageFile == null)
            cancel(true);
            return null;
        

        if (imageFile.exists() == true) 
            if(imageFile.delete() == false)
                cancel(true);
                return null;
            
        

        FileOutputStream fos = null;

        try 
            fos = new FileOutputStream(imageFile.getPath());
            fos.write(byImage);
            fos.flush();
            fos.close();
         catch (java.io.IOException e) 
            e.printStackTrace();
         finally 
            fos = null;
        

        return imageFile;
    

    @Override
    protected void onPostExecute(File file) 
        super.onPostExecute(file);

            String strAuthority = ctx.getPackageName() + ".provider";
            Uri uriImage = FileProvider.getUriForFile(ctx, strAuthority, file);

            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(uriImage, "image/*");
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            ctx.startActivity(intent);

    
.execute(strBase64Image);

不要忘记首先在 AndroidManifest.xml 中设置适当的文件提供程序

        <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="$applicationId.provider"
        android:exported="false"
        android:grantUriPermissions="true">

        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
    </provider>

文件路径是.../res/xml/file_path.xml中的xml

<?xml version="1.0" encoding="utf-8"?>

<external-files-path name="external_files" path="Accessory"/>

<external-path name="ex_Download" path="Download/" />
<external-path name="ex_Pictures" path="Pictures/" />

<external-files-path name="my_Download" path="Download/" />
<external-files-path name="my_Pictures" path="Pictures/" />
<external-cache-path name="my_cache" path="." />

<files-path name="private_Download" path="Download/" />
<files-path name="private_Pictures" path="Pictures/" />
<cache-path name="private_cache" path="." />


长话短说,首先准备好文件提供程序,将 Uri 传递给 Intent 以获得已知且可访问的图片源,否则,将图片保存在所需位置,然后将位置(作为 Uri)传递给 Intent。

【讨论】:

以上是关于以编程方式从 Android 的内置图库应用程序中获取/选择图像的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 中以编程方式将图像文件从图库复制到另一个文件夹

Android - 以编程方式访问相机和图库图标和标签

允许以编程方式访问图库照片

将多张图片从图库上传到android中的服务器

如何在android中以编程方式上传到服务器之前减小视频的大小[关闭]

在 Android 图库中选择多张图片