Comment choisir une image de la galerie (carte SD) pour mon application?

343

Cette question a été initialement posée pour Android 1.6.

Je travaille sur les options de photos dans mon application.

J'ai un bouton et une ImageView dans mon activité. Lorsque je clique sur le bouton, il est redirigé vers la galerie et je peux sélectionner une image. L'image sélectionnée apparaîtrait dans mon ImageView.

Praveen
la source
1
coup d' œil à cette réponse, j'ai posté un code améliorée là pour choix de la poignée de gestionnaires de fichiers aussi stackoverflow.com/questions/2169649/...
fou

Réponses:

418

Réponse mise à jour, près de 5 ans plus tard:

Le code dans la réponse d'origine ne fonctionne plus de manière fiable, car les images provenant de diverses sources retournent parfois avec un URI de contenu différent, c'est-à-dire content://plutôt que file://. Une meilleure solution consiste à simplement utiliser context.getContentResolver().openInputStream(intent.getData()), car cela retournera un InputStream que vous pouvez gérer comme vous le souhaitez.

Par exemple, BitmapFactory.decodeStream()fonctionne parfaitement dans cette situation, car vous pouvez également utiliser les champs Options et inSampleSize pour sous-échantillonner des images volumineuses et éviter les problèmes de mémoire.

Cependant, des choses comme Google Drive retournent les URI aux images qui n'ont pas encore été téléchargées. Par conséquent, vous devez exécuter le code getContentResolver () sur un thread d'arrière-plan.


Réponse originale:

Les autres réponses ont expliqué comment envoyer l'intention, mais elles n'ont pas bien expliqué comment gérer la réponse. Voici quelques exemples de code sur la façon de procéder:

protected void onActivityResult(int requestCode, int resultCode, 
       Intent imageReturnedIntent) {
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent); 

    switch(requestCode) { 
    case REQ_CODE_PICK_IMAGE:
        if(resultCode == RESULT_OK){  
            Uri selectedImage = imageReturnedIntent.getData();
            String[] filePathColumn = {MediaStore.Images.Media.DATA};

            Cursor cursor = getContentResolver().query(
                               selectedImage, filePathColumn, null, null, null);
            cursor.moveToFirst();

            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            String filePath = cursor.getString(columnIndex);
            cursor.close();


            Bitmap yourSelectedImage = BitmapFactory.decodeFile(filePath);
        }
    }
}

Après cela, vous avez l'image sélectionnée stockée dans "yourSelectedImage" pour faire ce que vous voulez. Ce code fonctionne en obtenant l'emplacement de l'image dans la base de données ContentResolver, mais cela ne suffit pas en soi. Chaque image contient environ 18 colonnes d'informations, allant de son chemin de fichier jusqu'à la date de la dernière modification aux coordonnées GPS de l'endroit où la photo a été prise, bien que de nombreux champs ne soient pas réellement utilisés.

Pour gagner du temps car vous n'avez pas réellement besoin des autres champs, la recherche par curseur se fait avec un filtre. Le filtre fonctionne en spécifiant le nom de la colonne souhaitée, MediaStore.Images.Media.DATA, qui est le chemin d'accès, puis en donnant cette chaîne [] à la requête du curseur. La requête de curseur revient avec le chemin d'accès, mais vous ne savez pas dans quelle colonne elle se trouve jusqu'à ce que vous utilisiez le columnIndexcode. Cela obtient simplement le numéro de la colonne en fonction de son nom, le même que celui utilisé dans le processus de filtrage. Une fois que vous avez cela, vous pouvez enfin décoder l'image en bitmap avec la dernière ligne de code que j'ai donnée.

Steve Haley
la source
4
curseur.moveToFirst () doit être vérifié pour l'existence: if (cursor.moveToFirst ()) {faire quelque chose avec les données du curseur}
mishkin
14
Au lieu du curseur, vous devriez obtenir l'image de cette manière: Bitmap b = MediaStore.Images.Media.getBitmap (this.getContentResolver (), selectedImage);
Luigi Agosti
4
Luigi, si le bitmap est volumineux MediaStore.Images.Media.getBitmap () peut provoquer des exceptions OutOfMemory. La méthode de Steve permet de réduire l'image avant de la charger en mémoire.
Frank Harper
9
cela ne fonctionne pas pour moi, j'obtiens un null de cursor.getString (columnIndex);
Alexis Pautrot
9
Attention à cette méthode: le nom de fichier sera «nul» lorsque l'utilisateur sélectionne une photo dans un album picasa ou dans l'application Google+ Photos.
Ciske Boekelo
315
private static final int SELECT_PHOTO = 100;

Intention de départ

Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, SELECT_PHOTO);    

Résultat du processus

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) { 
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent); 

    switch(requestCode) { 
    case SELECT_PHOTO:
        if(resultCode == RESULT_OK){  
            Uri selectedImage = imageReturnedIntent.getData();
            InputStream imageStream = getContentResolver().openInputStream(selectedImage);
            Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream);
        }
    }
}

Alternativement, vous pouvez également sous-échantillonner votre image pour éviter les erreurs OutOfMemory.

private Bitmap decodeUri(Uri selectedImage) throws FileNotFoundException {

        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE = 140;

        // Find the correct scale value. It should be the power of 2.
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < REQUIRED_SIZE
               || height_tmp / 2 < REQUIRED_SIZE) {
                break;
            }
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o2);

    }
siamii
la source
8
mettre un jpeg de 1,5 Mo dans ma petite image 100px par 100px a entraîné une erreur de mémoire insuffisante de la machine virtuelle. Le sous-échantillonnage a résolu ce problème :-)
Quelqu'un Quelque part
1
Salut. Les deux flux ne devraient-ils pas être fermés?
Denis Kniazhev
Bonjour @ siamii..J'ai suivi votre code..mais cela fonctionne partiellement pour moi .. :( lorsque l'image est sélectionnée dans la galerie sous la section des images capturées, cela donne une erreur json, mais lorsque l'image est sélectionnée dans la galerie sous la section bluetooth l'image est accédée et envoyée au serveur ... pourriez-vous s'il vous plaît vérifier ce lien et me suggérer une solution s'il vous plaît ... stackoverflow.com/questions/29234409/image-is-not-uploaded
Prabs
La section sur la recherche de l'échelle pourrait être écrite comme int scale = 1; for ( ; bfOptions.outWidth / scale > TARGET_SIZE && bfOptions.outWidth > TARGET_SIZE; scale*=2);
suit
@siamii Où et comment appeler cette méthode -------- decodeUri
Akshay Kumar
87

Vous devez démarrer l'intention de la galerie pour un résultat.

Intent i = new Intent(Intent.ACTION_PICK,
               android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, ACTIVITY_SELECT_IMAGE); 

Ensuite onActivityForResult, appelez intent.getData()pour obtenir l'URI de l'image. Ensuite, vous devez obtenir l'image du ContentProvider.

Robby Pond
la source
En quoi ACTION_PICK diffère-t-il de ACTION_GET_CONTENT utilisé dans deux autres réponses?
penguin359
4
Avec ACTION_PICK, vous spécifiez un URI spécifique et avec ACTION_GET_CONTENT, vous spécifiez un mime_type. J'ai utilisé ACTION_PICK parce que la question était spécifiquement des images de la SDCARD et pas toutes les images.
Robby Pond,
2
Cool. C'est exactement ce dont j'avais besoin et j'ai travaillé comme un charme :) Je me demande d'où vous trouvez ces trucs :)
Jayshil Dave
@WilliamKinaan ACTIVITY_SELECT_IMAGE est toute valeur int que vous spécifiez afin d'identifier le résultat que vous attendez de recevoir. Il est ensuite renvoyé dans onActivityResult (int requestCode, int resultCode, Intent data) en tant que «requestCode».
Fydo
@Fydo J'ai réalisé que plus tard, merci
William Kinaan
22

Voici un code testé pour l'image et la vidéo qui fonctionnera également pour toutes les API inférieures à 19 et supérieures à 19.

Image:

if (Build.VERSION.SDK_INT <= 19) {
                        Intent i = new Intent();
                        i.setType("image/*");
                        i.setAction(Intent.ACTION_GET_CONTENT);
                        i.addCategory(Intent.CATEGORY_OPENABLE);
                        startActivityForResult(i, 10);
                    } else if (Build.VERSION.SDK_INT > 19) {
                        Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                        startActivityForResult(intent, 10);
                    }

Vidéo:

if (Build.VERSION.SDK_INT <= 19) {
                        Intent i = new Intent();
                        i.setType("video/*");
                        i.setAction(Intent.ACTION_GET_CONTENT);
                        i.addCategory(Intent.CATEGORY_OPENABLE);
                        startActivityForResult(i, 20);
                    } else if (Build.VERSION.SDK_INT > 19) {
                        Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
                        startActivityForResult(intent, 20);
                    }    

.

     @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {               
            if (requestCode == 10) {
                Uri selectedImageUri = data.getData();
                String selectedImagePath = getRealPathFromURI(selectedImageUri);
            } else if (requestCode == 20) {
                Uri selectedVideoUri = data.getData();
                String selectedVideoPath = getRealPathFromURI(selectedVideoUri);
            }
        }
     }

     public String getRealPathFromURI(Uri uri) {
            if (uri == null) {
                return null;
            }
            String[] projection = {MediaStore.Images.Media.DATA};
            Cursor cursor = getActivity().getContentResolver().query(uri, projection, null, null, null);
            if (cursor != null) {
                int column_index = cursor
                        .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                cursor.moveToFirst();
                return cursor.getString(column_index);
            }
            return uri.getPath();
        }
Muhammad Umair Shafique
la source
14

Faites cela pour lancer la galerie et permettre à l'utilisateur de choisir une image:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, IMAGE_PICK);

Ensuite, dans votre onActivityResult()utilisation, l'URI de l'image qui est retournée pour définir l'image sur votre ImageView.

Mark B
la source
3
Cela ne fonctionnera pas pour les appareils Android 4.4. Il lancera l'écran des documents récents.
Noundla Sandeep
2
Voici un correctif pour KitKat: stackoverflow.com/a/26690628/860488
Morten Holmgaard
11
public class EMView extends Activity {
ImageView img,img1;
int column_index;
  Intent intent=null;
// Declare our Views, so we can access them later
String logo,imagePath,Logo;
Cursor cursor;
//YOU CAN EDIT THIS TO WHATEVER YOU WANT
private static final int SELECT_PICTURE = 1;

 String selectedImagePath;
//ADDED
 String filemanagerstring;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    img= (ImageView)findViewById(R.id.gimg1);



    ((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
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK) {
        if (requestCode == SELECT_PICTURE) {
            Uri selectedImageUri = data.getData();

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

            //MEDIA GALLERY
            selectedImagePath = getPath(selectedImageUri);


            img.setImageURI(selectedImageUri);

           imagePath.getBytes();
           TextView txt = (TextView)findViewById(R.id.title);
           txt.setText(imagePath.toString());


           Bitmap bm = BitmapFactory.decodeFile(imagePath);

          // img1.setImageBitmap(bm);



        }

    }

}

//UPDATED!
public String getPath(Uri uri) {
String[] projection = { MediaColumns.DATA };
Cursor cursor = managedQuery(uri, projection, null, null, null);
column_index = cursor
        .getColumnIndexOrThrow(MediaColumns.DATA);
cursor.moveToFirst();
 imagePath = cursor.getString(column_index);

return cursor.getString(column_index);
}

}
Sheetal Plus
la source
8
public class BrowsePictureActivity extends Activity {
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) {

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

public String getPath(Uri uri) {

        if( uri == null ) {
            return null;
        }

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

        return uri.getPath();
}

}
Muhammad Usman Ghani
la source
Voici un correctif pour KitKat: stackoverflow.com/a/26690628/860488
Morten Holmgaard
4

Pour certaines raisons, toutes les réponses de ce fil, dans le onActivityResult()but de post-traiter le reçu Uri, comme obtenir le vrai chemin de l'image, puis l'utiliser BitmapFactory.decodeFile(path)pour obtenir le Bitmap.

Cette étape est inutile. La ImageViewclasse a une méthode appelée setImageURI(uri). Passez votre uri et vous devriez avoir terminé.

Uri imageUri = data.getData();
imageView.setImageURI(imageUri);

Pour un exemple de travail complet, vous pouvez jeter un œil ici: http://androidbitmaps.blogspot.com/2015/04/loading-images-in-android-part-iii-pick.html

PS:
obtenir le Bitmapdans une variable distincte aurait du sens dans les cas où l'image à charger est trop grande pour tenir dans la mémoire, et une opération de réduction d'échelle est nécessaire pour empêcher OurOfMemoryError, comme indiqué dans la réponse @siamii.

Andy Res
la source
3

appelez la méthode chooseImage comme-

public void chooseImage(ImageView v)
{
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType("image/*");
    startActivityForResult(intent, SELECT_PHOTO);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
    // TODO Auto-generated method stub
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

    if(imageReturnedIntent != null)
    {
        Uri selectedImage = imageReturnedIntent.getData();
    switch(requestCode) { 
    case SELECT_PHOTO:
        if(resultCode == RESULT_OK)
        {
            Bitmap datifoto = null;
            temp.setImageBitmap(null);
            Uri picUri = null;
            picUri = imageReturnedIntent.getData();//<- get Uri here from data intent
             if(picUri !=null){
               try {
                   datifoto = android.provider.MediaStore.Images.Media.getBitmap(this.getContentResolver(),                                 picUri);
                   temp.setImageBitmap(datifoto);
               } catch (FileNotFoundException e) {
                  throw new RuntimeException(e);
               } catch (IOException e) {
                  throw new RuntimeException(e);
               } catch (OutOfMemoryError e) {
                Toast.makeText(getBaseContext(), "Image is too large. choose other", Toast.LENGTH_LONG).show();
            }

        }
        }
        break;

}
    }
    else
    {
        //Toast.makeText(getBaseContext(), "data null", Toast.LENGTH_SHORT).show();
    }
}
Akshay Paliwal
la source
1
#initialize in main activity 
    path = Environment.getExternalStorageDirectory()
            + "/images/make_machine_example.jpg"; #
     ImageView image=(ImageView)findViewById(R.id.image);
 //--------------------------------------------------||

 public void FromCamera(View) {

    Log.i("camera", "startCameraActivity()");
    File file = new File(path);
    Uri outputFileUri = Uri.fromFile(file);
    Intent intent = new Intent(
            android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
    startActivityForResult(intent, 1);

}

public void FromCard() {
    Intent i = new Intent(Intent.ACTION_PICK,
            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, 2);
}

 protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == 2 && resultCode == RESULT_OK
            && null != data) {

        Uri selectedImage = data.getData();
        String[] filePathColumn = { MediaStore.Images.Media.DATA };

        Cursor cursor = getContentResolver().query(selectedImage,
                filePathColumn, null, null, null);
        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String picturePath = cursor.getString(columnIndex);
        cursor.close();

        bitmap = BitmapFactory.decodeFile(picturePath);
        image.setImageBitmap(bitmap);

        if (bitmap != null) {
            ImageView rotate = (ImageView) findViewById(R.id.rotate);

        }

    } else {

        Log.i("SonaSys", "resultCode: " + resultCode);
        switch (resultCode) {
        case 0:
            Log.i("SonaSys", "User cancelled");
            break;
        case -1:
            onPhotoTaken();
            break;

        }

    }

}

protected void onPhotoTaken() {
    // Log message
    Log.i("SonaSys", "onPhotoTaken");
    taken = true;
    imgCapFlag = true;
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 4;
    bitmap = BitmapFactory.decodeFile(path, options);
    image.setImageBitmap(bitmap);


}
ASHISH KUMAR Tiwary
la source