Get/pick an image from Android's built-in Gallery app programmatically

前端 未结 19 1187
终归单人心
终归单人心 2020-11-22 00:49

I am trying to open an image / picture in the Gallery built-in app from inside my application.

I have a URI of the picture (the picture is located on the SD card).

相关标签:
19条回答
  • 2020-11-22 01:08

    This is a complete solution. I've just updated this example code with the information provided in the answer below by @mad. Also check the solution below from @Khobaib explaining how to deal with picasa images.

    Update

    I've just reviewed my original answer and created a simple Android Studio project you can checkout from github and import directly on your system.

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

    (note that the multiple file selection still needs work)

    Single Picture Selection

    With support for images from file explorers thanks to user 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();
        }
    
    }
    

    Selecting Multiple Pictures

    Since someone requested that information in a comment and it's better to have information gathered.

    Set an extra parameter EXTRA_ALLOW_MULTIPLE on the intent:

    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    

    And in the Result handling check for that parameter:

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

    Note that this is only supported by API level 18+.

    0 讨论(0)
  • 2020-11-22 01:08

    this is my revisit to this topic, gathering all the information here, plus from other relevant stack overflow questions. It returns images from some provider, while handling out-of-memory conditions and image rotation. It supports gallery, picasa and file managers, like drop box. Usage is simple: as input, the constructor receives the content resolver and the uri. The output is the final bitmap.

    /**
     * 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;
        }
    }
    

    References:

    • 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?
    0 讨论(0)
  • 2020-11-22 01:09

    I went through the solution from @hcpl & @mad. hcpl's solution supports well for local image in the gallery & mad provided a better solution on top of that - it helps to load OI/Astro/Dropbox image as well. But in my app, while working on picasa library that's now integrated in Android Gallery, both solution fail.

    I searched & analyzed a bit & eventually have come with a better & elegant solution that overcomes this limitation. Thanks to Dimitar Darazhanski for his blog, that helped me in this case, I modified a bit to make it easier to understand. Here is my solution goes -

    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
    }
    

    Check it & let me know if there's some issue with it. I have tested it & it works well in every case.

    Hope this will help everyone.

    0 讨论(0)
  • 2020-11-22 01:11

    There are two useful tutorials about image picker with downloadable source code here:

    How to Create Android Image Picker

    How to Select and Crop Image on Android

    However, the app will be forced to close sometime, you can fix it by adding android:configChanges attribute into main activity in Manifest file like as:

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

    It seems that the camera API lost control with orientation so this will help it. :)

    0 讨论(0)
  • 2020-11-22 01:14

    Please find the answer for the selecting single image from gallery

    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();
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 01:16

    basis with the above code, I reflected the code like below, may be it's more suitable:

    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;
    }
    
    0 讨论(0)
提交回复
热议问题