android - salva l'immagine nella galleria


91

Ho un'app con una galleria di immagini e desidero che l'utente possa salvarla nella sua galleria. Ho creato un menu di opzioni con una sola voce "salva" per consentirlo, ma il problema è ... come posso salvare l'immagine nella galleria?

questo è il mio codice:

@Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle item selection
            switch (item.getItemId()) {
            case R.id.menuFinale:

                imgView.setDrawingCacheEnabled(true);
                Bitmap bitmap = imgView.getDrawingCache();
                File root = Environment.getExternalStorageDirectory();
                File file = new File(root.getAbsolutePath()+"/DCIM/Camera/img.jpg");
                try 
                {
                    file.createNewFile();
                    FileOutputStream ostream = new FileOutputStream(file);
                    bitmap.compress(CompressFormat.JPEG, 100, ostream);
                    ostream.close();
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                }



                return true;
            default:
                return super.onOptionsItemSelected(item);
            }
        }

non sono sicuro di questa parte del codice:

File root = Environment.getExternalStorageDirectory();
                File file = new File(root.getAbsolutePath()+"/DCIM/Camera/img.jpg");

è corretto salvare nella galleria? purtroppo il codice non funziona :(


hai risolto questo problema? puoi condividere con me
user3233280

Inoltre sto avendo lo stesso problema stackoverflow.com/questions/21951558/...
user3233280

Per quelli di voi che hanno ancora problemi a salvare il file, potrebbe essere perché l'URL contiene caratteri illegali come "?", ":" E "-" Rimuovili e dovrebbe funzionare. Questo è un errore comune nei dispositivi esterni e negli emulatori Android. Vedi di più su di esso qui: stackoverflow.com/questions/11394616/…
ChallengeAccepted

La risposta accettata è un po 'obsoleta nel 2019. Ho scritto una risposta aggiornata qui: stackoverflow.com/questions/36624756/…
Bao Lei

Risposte:


168
MediaStore.Images.Media.insertImage(getContentResolver(), yourBitmap, yourTitle , yourDescription);

Il codice precedente aggiungerà l'immagine alla fine della galleria. Se si desidera modificare la data in modo che appaia all'inizio o qualsiasi altro metadata, vedere il codice seguente (per gentile concessione di SK, samkirton ):

https://gist.github.com/samkirton/0242ba81d7ca00b475b9

/**
 * Android internals have been modified to store images in the media folder with 
 * the correct date meta data
 * @author samuelkirton
 */
public class CapturePhotoUtils {

    /**
     * A copy of the Android internals  insertImage method, this method populates the 
     * meta data with DATE_ADDED and DATE_TAKEN. This fixes a common problem where media 
     * that is inserted manually gets saved at the end of the gallery (because date is not populated).
     * @see android.provider.MediaStore.Images.Media#insertImage(ContentResolver, Bitmap, String, String)
     */
    public static final String insertImage(ContentResolver cr, 
            Bitmap source, 
            String title, 
            String description) {

        ContentValues values = new ContentValues();
        values.put(Images.Media.TITLE, title);
        values.put(Images.Media.DISPLAY_NAME, title);
        values.put(Images.Media.DESCRIPTION, description);
        values.put(Images.Media.MIME_TYPE, "image/jpeg");
        // Add the date meta data to ensure the image is added at the front of the gallery
        values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
        values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());

        Uri url = null;
        String stringUrl = null;    /* value to be returned */

        try {
            url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

            if (source != null) {
                OutputStream imageOut = cr.openOutputStream(url);
                try {
                    source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
                } finally {
                    imageOut.close();
                }

                long id = ContentUris.parseId(url);
                // Wait until MINI_KIND thumbnail is generated.
                Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
                // This is for backward compatibility.
                storeThumbnail(cr, miniThumb, id, 50F, 50F,Images.Thumbnails.MICRO_KIND);
            } else {
                cr.delete(url, null, null);
                url = null;
            }
        } catch (Exception e) {
            if (url != null) {
                cr.delete(url, null, null);
                url = null;
            }
        }

        if (url != null) {
            stringUrl = url.toString();
        }

        return stringUrl;
    }

    /**
     * A copy of the Android internals StoreThumbnail method, it used with the insertImage to
     * populate the android.provider.MediaStore.Images.Media#insertImage with all the correct
     * meta data. The StoreThumbnail method is private so it must be duplicated here.
     * @see android.provider.MediaStore.Images.Media (StoreThumbnail private method)
     */
    private static final Bitmap storeThumbnail(
            ContentResolver cr,
            Bitmap source,
            long id,
            float width, 
            float height,
            int kind) {

        // create the matrix to scale it
        Matrix matrix = new Matrix();

        float scaleX = width / source.getWidth();
        float scaleY = height / source.getHeight();

        matrix.setScale(scaleX, scaleY);

        Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
            source.getWidth(),
            source.getHeight(), matrix,
            true
        );

        ContentValues values = new ContentValues(4);
        values.put(Images.Thumbnails.KIND,kind);
        values.put(Images.Thumbnails.IMAGE_ID,(int)id);
        values.put(Images.Thumbnails.HEIGHT,thumb.getHeight());
        values.put(Images.Thumbnails.WIDTH,thumb.getWidth());

        Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);

        try {
            OutputStream thumbOut = cr.openOutputStream(url);
            thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
            thumbOut.close();
            return thumb;
        } catch (FileNotFoundException ex) {
            return null;
        } catch (IOException ex) {
            return null;
        }
    }
}

22
Questo salva l'immagine, ma alla fine della galleria, però, quando scatti una foto con la fotocamera, viene salvata in alto. Come posso salvare l'immagine in cima alla galleria?
eric.itzhak

19
Tieni presente che devi anche aggiungere <uses-permission android: name = "android.permission.WRITE_EXTERNAL_STORAGE" /> al tuo manifext.xml.
Kyle Clegg

3
Le immagini non vengono salvate nella parte superiore della galleria perché internamente insertImage non aggiunge alcun metadato della data. Si prega di consultare questo GIST: gist.github.com/0242ba81d7ca00b475b9.git è una copia esatta del metodo insertImage ma aggiunge la metadata della data per garantire che l'immagine venga aggiunta nella parte anteriore della galleria.
S-K

1
@ S-K'I non riesco ad accedere a quell'URL. Per favore aggiornalo e aggiornerò la mia risposta in modo che abbia entrambe le opzioni. Cheers
sfratini

6
Ecco il collegamento GIST corretto di cui sopra (necessario per rimuovere il .gitalla fine)
minipif

48

In realtà, puoi salvare la tua immagine in qualsiasi posto. Se vuoi salvare in uno spazio pubblico, in modo che qualsiasi altra applicazione possa accedere, usa questo codice:

storageDir = new File(
    Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    ), 
    getAlbumName()
);

L'immagine non viene inserita nell'album. Per fare ciò, è necessario chiamare una scansione:

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

Puoi trovare maggiori informazioni su https://developer.android.com/training/camera/photobasics.html#TaskGallery


1
Questa è una soluzione semplice e carina in quanto non è necessario modificare l'intera implementazione e possiamo creare una cartella personalizzata per le app.
Hugo Gresse

2
L'invio di una trasmissione può essere uno spreco di risorse quando è possibile scansionare solo un file: stackoverflow.com/a/5814533/43051 .
Jérémy Reynaud

2
Dove passi effettivamente la bitmap?
Daniel Reyhanian

22

Ho provato molte cose per lasciare che questo funzionasse su Marshmallow e Lollipop. Alla fine ho finito per spostare l'immagine salvata nella cartella DCIM (nuove immagini di scansione dell'app Google Foto solo se apparentemente sono all'interno di questa cartella)

public static File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
         .format(System.currentTimeInMillis());
    File storageDir = new File(Environment
         .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/");
    if (!storageDir.exists())
        storageDir.mkdirs();
    File image = File.createTempFile(
            timeStamp,                   /* prefix */
            ".jpeg",                     /* suffix */
            storageDir                   /* directory */
    );
    return image;
}

E poi il codice standard per la scansione dei file che puoi trovare anche nel sito di Google Developers .

public static void addPicToGallery(Context context, String photoPath) {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(photoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    context.sendBroadcast(mediaScanIntent);
}

Ricorda che questa cartella non può essere presente in tutti i dispositivi al mondo e che a partire da Marshmallow (API 23), è necessario richiedere il permesso a WRITE_EXTERNAL_STORAGE all'utente.


1
Grazie per le informazioni relative a Google Foto.
Jérémy Reynaud

1
Questa è l'unica soluzione che spiega bene. Nessun altro ha detto che il file deve essere nella cartella DCIM. Grazie!!!
Predrag Manojlovic

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)ha fatto il trucco per me. Grazie!
saltandpepper

2
getExternalStoragePublicDirectory()è ora deprecato su API 29. È necessario utilizzare MediaStore
riggaroo

@riggaroo Sì, hai ragione Rebecca, aggiornerò la risposta al più presto
MatPag

13

Secondo questo corso , il modo corretto per farlo è:

Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    )

Questo ti darà il percorso di root per la directory della galleria.


ho provato questo nuovo codice ma si è bloccato java.lang.NoSuchFieldError: android.os.Environment.DIRECTORY_PICTURES
Christian Giupponi

ok grazie, quindi non c'è modo di mettere un'immagine nella galleria con android <2.2?
Christian Giupponi

Perfetto: un collegamento direttamente al sito Web degli sviluppatori Android. Questo ha funzionato ed è stata una soluzione semplice.
Phil

1
Buona risposta, ma sarebbe meglio con l'aggiunta del metodo "galleryAddPic" dalle altre risposte qui, poiché di solito vorrai che l'app Galleria noti nuove immagini.
Andrew Koster

11
private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

6

È possibile creare una directory all'interno della cartella della fotocamera e salvare l'immagine. Dopodiché, puoi semplicemente eseguire la scansione. Mostrerà immediatamente la tua immagine nella galleria.

String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()+ "/Camera/Your_Directory_Name";
File myDir = new File(root);
myDir.mkdirs();
String fname = "Image-" + image_name + ".png";
File file = new File(myDir, fname);
System.out.println(file.getAbsolutePath());
if (file.exists()) file.delete();
    Log.i("LOAD", root + fname);
    try {
        FileOutputStream out = new FileOutputStream(file);
        finalBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
        out.flush();
        out.close();
    } catch (Exception e) {
       e.printStackTrace();
    }

MediaScannerConnection.scanFile(context, new String[]{file.getPath()}, new String[]{"image/jpeg"}, null);

in questo criterio questa è la migliore risposta
Noor Hossain

1

Vengo qui con lo stesso dubbio ma per Xamarin per Android, ho usato la risposta Sigrist per fare questo metodo dopo aver salvato il mio file:

private void UpdateGallery()
{
    Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
    Java.IO.File file = new Java.IO.File(_path);
    Android.Net.Uri contentUri = Android.Net.Uri.FromFile(file);
    mediaScanIntent.SetData(contentUri);
    Application.Context.SendBroadcast(mediaScanIntent);
} 

e ha risolto il mio problema, Thx Sigrist. L'ho messo qui perché non ho trovato una risposta su questo per Xamarin e spero che possa aiutare altre persone.


1

Nel mio caso le soluzioni di cui sopra non hanno funzionato ho dovuto fare quanto segue:

sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f)));

è davvero bello sapere di questa opzione, ma sfortunatamente non funziona su alcuni dispositivi con Android 6, quindi soluzione ContentProviderpreferibile
Siarhei

0
 String filePath="/storage/emulated/0/DCIM"+app_name;
    File dir=new File(filePath);
    if(!dir.exists()){
        dir.mkdir();
    }

Questo codice è nel metodo onCreate. Questo codice serve per creare una directory di app_name. Ora è possibile accedere a questa directory utilizzando l'app di gestione file predefinita in Android. Utilizzare questa stringa filePath dove necessario per impostare la cartella di destinazione. Sono sicuro che questo metodo funziona anche su Android 7 perché l'ho testato, quindi può funzionare anche su altre versioni di Android.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.