android - zapisz obraz do galerii

94

Mam aplikację z galerią obrazów i chcę, aby użytkownik mógł ją zapisać w swojej własnej galerii. Stworzyłem menu opcji z pojedynczym "zapisz" głosem, aby to umożliwić, ale problem polega na… jak mogę zapisać obraz w galerii?

to jest mój kod:

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

nie jestem pewien tej części kodu:

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

czy zapisywanie w galerii jest poprawne? niestety kod nie działa :(

Christian Giupponi
źródło
czy rozwiązałeś ten problem? czy możesz podzielić się ze mną
user3233280
Mam też ten sam problem stackoverflow.com/questions/21951558/…
user3233280
Dla tych z Was, którzy nadal mają problemy z zapisaniem pliku, może to być spowodowane tym, że adres URL zawiera niedozwolone znaki, takie jak „?”, „:” I „-” Usuń je i powinno działać. Jest to częsty błąd występujący na obcych urządzeniach i emulatorach Androida. Zobacz więcej na ten temat tutaj: stackoverflow.com/questions/11394616/…
ChallengeAccepted
Zaakceptowana odpowiedź jest trochę nieaktualna w 2019 roku. Zaktualizowałem odpowiedź tutaj: stackoverflow.com/questions/36624756/…
Bao Lei

Odpowiedzi:

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

Poprzedni kod doda obraz na końcu galerii. Jeśli chcesz zmodyfikować datę tak, aby pojawiała się na początku, lub inne metadane, zobacz poniższy kod (Cortesy of 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;
        }
    }
}
sfratini
źródło
23
Spowoduje to zapisanie obrazu, ale do końca galerii, gdy robisz zdjęcie aparatem, jest ono zapisywane na górze. Jak mogę zapisać obraz na górze galerii?
eric.itzhak
19
Zwróć uwagę, że musisz również dodać <using-Permissions android: name = "android.permission.WRITE_EXTERNAL_STORAGE" /> do swojego pliku manifext.xml.
Kyle Clegg
3
Obrazy nie są zapisywane w górnej części galerii, ponieważ wewnętrznie insertImage nie dodaje żadnych metadanych daty. Zobacz ten GIST: gist.github.com/0242ba81d7ca00b475b9.git . Jest to dokładna kopia metody insertImage, ale dodaje metadatę z datą, aby upewnić się, że obraz zostanie dodany na początku galerii.
S-K
6
Oto poprawny link GIST wspomniany powyżej (potrzebny do usunięcia .gitna końcu)
minipif.
2
Jak zmienić nazwę folderu plików?
Kopi Bryant
48

Właściwie możesz zapisać swoje zdjęcie w dowolnym miejscu. Jeśli chcesz zapisać w przestrzeni publicznej, aby każda inna aplikacja miała do niej dostęp, użyj tego kodu:

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

Zdjęcie nie trafia do albumu. Aby to zrobić, musisz wywołać skanowanie:

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

Więcej informacji można znaleźć na https://developer.android.com/training/camera/photobasics.html#TaskGallery

Sigrist
źródło
1
Jest to przyjemne i proste rozwiązanie, ponieważ nie musimy zmieniać całej implementacji i możemy utworzyć niestandardowy folder dla aplikacji.
Hugo Gresse
2
Wysyłanie emisji może być stratą zasobów, gdy można zeskanować tylko plik: stackoverflow.com/a/5814533/43051 .
Jérémy Reynaud
2
Gdzie właściwie przekazujesz bitmapę?
Daniel Reyhanian
Environment.getExternalStoragePublicDirectoryi Intent.ACTION_MEDIA_SCANNER_SCAN_FILEsą teraz przestarzałe ...
Michael Abyzov
22

Próbowałem wielu rzeczy, aby to zadziałało na Marshmallow i Lollipop. W końcu przeniosłem zapisane zdjęcie do folderu DCIM (nowa aplikacja Google Photo skanuje obrazy tylko wtedy, gdy najwyraźniej znajdują się w tym folderze)

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

A następnie standardowy kod do skanowania plików, który można znaleźć również w witrynie 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);
}

Pamiętaj, że ten folder nie może być obecny na każdym urządzeniu na świecie i zaczynając od Marshmallow (API 23), musisz poprosić użytkownika o pozwolenie na WRITE_EXTERNAL_STORAGE.

MatPag
źródło
1
Dziękuję za informacje dotyczące Zdjęć Google.
Jérémy Reynaud
1
To jedyne rozwiązanie dobrze wyjaśniające. Nikt inny nie wspomniał, że plik musi znajdować się w folderze DCIM. Dziękuję Ci!!!
Predrag Manojlovic
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)załatwił sprawę dla mnie. Dzięki!
saltandpepper
2
getExternalStoragePublicDirectory()jest teraz przestarzały w API 29. Należy użyć MediaStore
riggaroo
@riggaroo Tak, masz rację Rebecca, zaktualizuję odpowiedź
JAK
13

Zgodnie z tym kursem , prawidłowym sposobem na to jest:

Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    )

W ten sposób uzyskasz ścieżkę główną do katalogu galerii.

Cédric Julien
źródło
Próbowałem tego nowego kodu, ale spowodował awarię java.lang.NoSuchFieldError: android.os.Environment.DIRECTORY_PICTURES
Christian Giupponi
ok dzięki, więc nie ma możliwości umieszczenia obrazka w galerii z Androidem <2.2?
Christian Giupponi,
Idealnie - link bezpośrednio do strony Android Developer. To zadziałało i było prostym rozwiązaniem.
Phil
1
Dobra odpowiedź, ale byłoby lepiej po dodaniu metody „galleryAddPic” z innych odpowiedzi tutaj, ponieważ zazwyczaj chcesz, aby aplikacja Galeria zauważyła nowe zdjęcia.
Andrew Koster,
Environment.getExternalStoragePublicDirectoryjest już przestarzały ...
Michael Abyzov
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);
}
nitin Sol
źródło
6

Możesz utworzyć katalog w folderze aparatu i zapisać obraz. Następnie możesz po prostu wykonać skanowanie. Natychmiast pokaże Twoje zdjęcie w galerii.

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);
javatar
źródło
w tych kryteriach jest to najlepsza odpowiedź
Noor Hossain
1

Przychodzę tutaj z tymi samymi wątpliwościami, ale w przypadku Xamarin na Androida użyłem odpowiedzi Sigrist, aby wykonać tę metodę po zapisaniu mojego pliku:

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

i to rozwiązało mój problem, Thx Sigrist. Umieściłem to tutaj, ponieważ nie znalazłem oprogramowania answare na ten temat dla Xamarin i mam nadzieję, że może to pomóc innym ludziom.

Slaters
źródło
1

W moim przypadku powyższe rozwiązania nie zadziałały musiałem wykonać następujące czynności:

sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f)));
dc10
źródło
naprawdę dobrze jest wiedzieć o tej opcji, ale niestety nie działa na niektórych urządzeniach z ContentProviderAndroidem 6, więc preferowane rozwiązanie
Siarhei
0
 String filePath="/storage/emulated/0/DCIM"+app_name;
    File dir=new File(filePath);
    if(!dir.exists()){
        dir.mkdir();
    }

Ten kod znajduje się w metodzie onCreate. Ten kod służy do tworzenia katalogu nazwa_aplikacji. Teraz do tego katalogu można uzyskać dostęp za pomocą domyślnej aplikacji do zarządzania plikami w systemie Android. Użyj tego ciągu filePath wszędzie tam, gdzie jest to wymagane do ustawienia folderu docelowego. Jestem pewien, że ta metoda działa również na Androidzie 7, ponieważ testowałem na niej, więc może działać również na innych wersjach Androida.

Bhavik Mehta
źródło