¿Cómo elegir una imagen de la galería (tarjeta SD) para mi aplicación?

343

Esta pregunta se hizo originalmente para Android 1.6.

Estoy trabajando en las opciones de fotos en mi aplicación.

Tengo un botón y un ImageView en mi actividad. Cuando hago clic en el botón, redirigiría a la galería y podría seleccionar una imagen. La imagen seleccionada aparecería en mi ImageView.

Praveen
fuente
1
mira esta respuesta, publiqué un código mejorado allí para manejar las selecciones de los administradores de archivos también stackoverflow.com/questions/2169649/…
mad

Respuestas:

418

Respuesta actualizada, casi 5 años después:

El código en la respuesta original ya no funciona de manera confiable, ya que las imágenes de varias fuentes a veces regresan con un URI de contenido diferente, es decir, en content://lugar de hacerlo file://. Una mejor solución es simplemente usar context.getContentResolver().openInputStream(intent.getData()), ya que eso devolverá un InputStream que puede manejar a su elección.

Por ejemplo, BitmapFactory.decodeStream()funciona perfectamente en esta situación, ya que también puede usar las opciones y el campo inSampleSize para reducir muestras de imágenes grandes y evitar problemas de memoria.

Sin embargo, cosas como Google Drive devuelven los URI a las imágenes que aún no se han descargado. Por lo tanto, debe realizar el código getContentResolver () en un subproceso en segundo plano.


Respuesta original:

Las otras respuestas explicaron cómo enviar la intención, pero no explicaron bien cómo manejar la respuesta. Aquí hay un código de muestra sobre cómo hacerlo:

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

Después de esto, tiene la imagen seleccionada almacenada en "yourSelectedImage" para hacer lo que quiera. Este código funciona al obtener la ubicación de la imagen en la base de datos ContentResolver, pero eso por sí solo no es suficiente. Cada imagen tiene aproximadamente 18 columnas de información, que van desde su ruta de archivo hasta la 'última fecha de modificación' hasta las coordenadas GPS de donde se tomó la foto, aunque muchos de los campos no se utilizan realmente.

Para ahorrar tiempo, ya que no necesita los otros campos, la búsqueda del cursor se realiza con un filtro. El filtro funciona especificando el nombre de la columna que desea, MediaStore.Images.Media.DATA, que es la ruta, y luego asigna esa cadena [] a la consulta del cursor. La consulta del cursor regresa con la ruta, pero no sabe en qué columna está hasta que usa el columnIndexcódigo. Eso simplemente obtiene el número de la columna en función de su nombre, el mismo utilizado en el proceso de filtrado. Una vez que tienes eso, finalmente puedes decodificar la imagen en un mapa de bits con la última línea de código que di.

Steve Haley
fuente
44
debe comprobarse la existencia de cursor.moveToFirst (): if (cursor.moveToFirst ()) {hacer algo con los datos del cursor}
mishkin
14
En lugar del cursor, debe obtener la imagen de esta manera: Bitmap b = MediaStore.Images.Media.getBitmap (this.getContentResolver (), selectedImage);
Luigi Agosti
44
Luigi, si el mapa de bits es grande, MediaStore.Images.Media.getBitmap () puede causar excepciones OutOfMemory. El método de Steve permite reducir la imagen antes de cargarla en la memoria.
Frank Harper
99
esto no funciona para mí, obtengo un valor nulo de cursor.getString (columnIndex);
Alexis Pautrot
99
Tenga cuidado con este método: el nombre del archivo será 'nulo' cuando el usuario seleccione una foto de un álbum picasa o de la aplicación Fotos de Google+.
Ciske Boekelo
315
private static final int SELECT_PHOTO = 100;

Intento de inicio

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

Resultado del proceso

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

Alternativamente, también puede reducir la resolución de su imagen para evitar errores de 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
fuente
8
poner un jpeg de 1.5MB en mi pequeña vista de imagen de 100px por 100px resultó en un error de memoria insuficiente de la VM. La disminución de resolución solucionó ese problema :-)
Alguien en algún lugar
1
Hola. ¿No deberían cerrarse ambas corrientes?
Denis Kniazhev
Hola @ siamii ... Seguí tu código ... pero funciona parcialmente para mí ... :( cuando la imagen se selecciona de la galería en la sección de imágenes capturadas, entonces está dando error json, pero cuando la imagen se selecciona de la galería en la sección de bluetooth se accede a la imagen y se envía al servidor ... ¿podría consultar este enlace y sugerirme alguna solución? ... stackoverflow.com/questions/29234409/image-is-not-uploaded
Prabs
La sección sobre cómo encontrar la escala podría escribirse como:int scale = 1; for ( ; bfOptions.outWidth / scale > TARGET_SIZE && bfOptions.outWidth > TARGET_SIZE; scale*=2);
The_Rafi
@siamii Dónde y cómo llamar a este método -------- decodeUri
Akshay Kumar
87

Tienes que comenzar la intención de la galería para obtener un resultado.

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

Luego onActivityForResult, llame intent.getData()para obtener el Uri de la imagen. Entonces necesita obtener la imagen del ContentProvider.

Estanque Robby
fuente
¿En qué se diferencia ACTION_PICK de ACTION_GET_CONTENT en otras dos respuestas?
penguin359 05 de
44
Con ACTION_PICK usted especifica un URI específico y con ACTION_GET_CONTENT especifica un mime_type. Utilicé ACTION_PICK porque la pregunta era específicamente imágenes de la SDCARD y no todas las imágenes.
Robby Pond
2
Frio. Esto es exactamente lo que necesitaba y funcionó a las
mil maravillas
@WilliamKinaan ACTIVITY_SELECT_IMAGE es cualquier valor int que especifique para identificar qué resultado espera recibir. Luego se devuelve en onActivityResult (int requestCode, int resultCode, Intent data) como 'requestCode'.
Fydo
@Fydo Me di cuenta de eso más tarde, gracias
William Kinaan
22

Aquí hay un código probado para imagen y video. Funcionará para todas las API menores de 19 y mayores de 19 también.

Imagen:

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

Vídeo:

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
fuente
14

Haga esto para iniciar la galería y permitir que el usuario elija una imagen:

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

Luego, en su onActivityResult()uso, el URI de la imagen que se devuelve para establecer la imagen en su ImageView.

Mark B
fuente
3
Esto no funcionará para los dispositivos Android 4.4. Lanzará la pantalla de documentos recientes.
Noundla Sandeep
2
Aquí hay una solución para KitKat: stackoverflow.com/a/26690628/860488
Morten Holmgaard el
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 Más
fuente
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
fuente
Aquí hay una solución para KitKat: stackoverflow.com/a/26690628/860488
Morten Holmgaard el
4

Por algunas razones, todas las respuestas en este hilo, en el onActivityResult()intento de postprocesar lo recibido Uri, como obtener la ruta real de la imagen y luego usarla BitmapFactory.decodeFile(path)para obtenerla Bitmap.

Este paso es innecesario. La ImageViewclase tiene un método llamado setImageURI(uri). Pase su uri y ya debería haber terminado.

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

Para ver un ejemplo de trabajo completo, puede consultar aquí: http://androidbitmaps.blogspot.com/2015/04/loading-images-in-android-part-iii-pick.html

PD:
Obtener el Bitmapen una variable separada tendría sentido en los casos en que la imagen a cargar es demasiado grande para caber en la memoria, y una operación de reducción de escala es necesaria para evitar OurOfMemoryError, como se muestra en la respuesta @siamii.

Andy Res
fuente
3

llame al método chooseImage like-

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


}
KUMAR CENIZADO Tiwary
fuente