Android - บันทึกภาพลงในแกลเลอรี่


96

ฉันมีแอพที่มีแกลเลอรีรูปภาพและฉันต้องการให้ผู้ใช้บันทึกลงในแกลเลอรีของเขาเอง ฉันได้สร้างเมนูตัวเลือกด้วยเสียงเดียว "บันทึก" เพื่ออนุญาต แต่ปัญหาคือ ... ฉันจะบันทึกภาพลงในแกลเลอรีได้อย่างไร

นี่คือรหัสของฉัน:

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

ฉันไม่แน่ใจในส่วนนี้ของรหัส:

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

บันทึกลงในแกลเลอรีถูกต้องหรือไม่ ขออภัยรหัสไม่ทำงาน :(


คุณได้แก้ไขปัญหานี้แล้วหรือยัง? คุณช่วยแบ่งปันกับฉันได้ไหม
user3233280

ฉันกำลังมีปัญหาเดียวกันstackoverflow.com/questions/21951558/…
user3233280

สำหรับผู้ที่ยังคงประสบปัญหาในการบันทึกไฟล์อาจเป็นเพราะ URL ของคุณมีอักขระที่ผิดกฎหมายเช่น "?", ":" และ "-" ให้ลบออกและควรใช้งานได้ นี่เป็นข้อผิดพลาดทั่วไปในอุปกรณ์ต่างประเทศและตัวเลียนแบบ Android ดูข้อมูลเพิ่มเติมได้ที่นี่: stackoverflow.com/questions/11394616/…
ChallengeAccepted

คำตอบที่ได้รับการยอมรับนั้นล้าสมัยเล็กน้อยในปี 2019 ฉันได้เขียนคำตอบที่อัปเดตแล้วที่นี่: stackoverflow.com/questions/36624756/…
Bao Lei

คำตอบ:


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

รหัสเดิมจะเพิ่มรูปภาพที่ส่วนท้ายของแกลเลอรี หากคุณต้องการแก้ไขวันที่ให้ปรากฏที่จุดเริ่มต้นหรือข้อมูลเมตาอื่น ๆ โปรดดูรหัสด้านล่าง (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;
        }
    }
}

25
สิ่งนี้จะบันทึกภาพ แต่เมื่อคุณถ่ายภาพด้วยกล้องจะบันทึกไว้ที่ด้านบน ฉันจะบันทึกภาพไปที่ด้านบนสุดของแกลเลอรีได้อย่างไร
eric.itzhak

20
โปรดทราบว่าคุณต้องเพิ่ม <use-permission android: name = "android.permission.WRITE_EXTERNAL_STORAGE" /> ใน manifext.xml ของคุณด้วย
Kyle Clegg

3
รูปภาพจะไม่ถูกบันทึกไว้ที่ด้านบนสุดของแกลเลอรีเนื่องจาก insertImage ภายในไม่ได้เพิ่มข้อมูลเมตาวันใด ๆ โปรดดู GIST นี้: gist.github.com/0242ba81d7ca00b475b9.gitเป็นสำเนาที่ถูกต้องของเมธอด insertImage แต่จะเพิ่มวันที่ meta เพื่อให้แน่ใจว่ามีการเพิ่มภาพที่ด้านหน้าของแกลเลอรี
S-K


2
จะเปลี่ยนชื่อโฟลเดอร์ไฟล์ได้อย่างไร?
Kopi Bryant

49

จริงๆแล้วคุณสามารถบันทึกภาพได้ทุกที่ หากคุณต้องการบันทึกในพื้นที่สาธารณะเพื่อให้แอปพลิเคชันอื่นเข้าถึงได้ให้ใช้รหัสนี้:

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

รูปไม่ไปที่อัลบั้ม ในการดำเนินการนี้คุณต้องเรียกการสแกน:

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

คุณสามารถดูข้อมูลเพิ่มเติมได้ที่https://developer.android.com/training/camera/photobasics.html#TaskGallery


1
นี่เป็นวิธีง่ายๆที่ดีเนื่องจากเราไม่จำเป็นต้องเปลี่ยนการใช้งานทั้งหมดและเราสามารถสร้างโฟลเดอร์ที่กำหนดเองสำหรับแอพได้
Hugo Gresse

2
ส่งออกอากาศอาจจะเสียทรัพยากรเมื่อคุณสามารถสแกนเฉพาะไฟล์: stackoverflow.com/a/5814533/43051
Jérémy Reynaud

2
คุณส่งบิตแมปไปที่ใด
Daniel Reyhanian

1
Environment.getExternalStoragePublicDirectoryและIntent.ACTION_MEDIA_SCANNER_SCAN_FILEเลิกใช้แล้วตอนนี้ ...
Michael Abyzov

22

ฉันได้ลองทำหลายอย่างเพื่อให้มันทำงานกับ Marshmallow และ Lollipop ได้ ในที่สุดฉันก็ย้ายรูปภาพที่บันทึกไว้ไปยังโฟลเดอร์ DCIM (แอป Google Photo ใหม่จะสแกนภาพก็ต่อเมื่อเห็นได้ชัดว่าอยู่ในโฟลเดอร์นี้)

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

แล้วรหัสมาตรฐานสำหรับการสแกนไฟล์ที่คุณสามารถหาได้ในเว็บไซต์ 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);
}

โปรดจำไว้ว่าโฟลเดอร์นี้ไม่สามารถมีได้ในทุกอุปกรณ์ในโลกและเริ่มจาก Marshmallow (API 23) คุณต้องขออนุญาต WRITE_EXTERNAL_STORAGE ให้กับผู้ใช้


1
ขอขอบคุณสำหรับข้อมูลเกี่ยวกับ Google Photos
Jérémy Reynaud

1
นี่เป็นทางออกเดียวที่อธิบายได้ดี ไม่มีใครพูดถึงไฟล์นั้นต้องอยู่ในโฟลเดอร์ DCIM ขอขอบคุณ!!!
Predrag Manojlovic

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)ทำเคล็ดลับให้ฉัน ขอบคุณ!
saltandpepper

2
getExternalStoragePublicDirectory()ตอนนี้เลิกใช้งานบน API 29 แล้วต้องใช้ MediaStore
riggaroo

@riggaroo ใช่คุณพูดถูกรีเบคก้าฉันจะอัปเดตคำตอบโดยเร็ว
MatPag

13

ตาม หลักสูตรนี้วิธีที่ถูกต้องในการดำเนินการคือ:

Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    )

สิ่งนี้จะให้เส้นทางรูทสำหรับไดเร็กทอรีแกลเลอรี


ฉันลองใช้รหัสใหม่นี้ แต่มันล้มเหลว java.lang.NoSuchFieldError: android.os.Environment.DIRECTORY_PICTURES
Christian Giupponi

โอเคขอบคุณดังนั้นไม่มีวิธีใดที่จะใส่รูปภาพลงในแกลเลอรีด้วย android <2.2?
Christian Giupponi

สมบูรณ์แบบ - ลิงก์ไปยังเว็บไซต์นักพัฒนา Android โดยตรง วิธีนี้ได้ผลและเป็นวิธีง่ายๆ
ฟิล

1
คำตอบที่ดี แต่จะดีกว่าหากมีการเพิ่มเมธอด "galleryAddPic" จากคำตอบอื่น ๆ ที่นี่เนื่องจากโดยปกติคุณจะต้องการให้แอปคลังภาพสังเกตเห็นรูปภาพใหม่ ๆ
Andrew Koster

1
Environment.getExternalStoragePublicDirectoryเลิกใช้แล้ว ...
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);
}

6

คุณสามารถสร้างไดเร็กทอรีภายในโฟลเดอร์กล้องและบันทึกภาพ หลังจากนั้นคุณสามารถทำการสแกนของคุณได้ มันจะแสดงภาพของคุณในแกลเลอรีทันที

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

ในเกณฑ์นี้นี่คือคำตอบที่ดีที่สุด
Noor Hossain

1

ฉันมาที่นี่ด้วยข้อสงสัยเดียวกัน แต่สำหรับ Xamarin สำหรับ Android ฉันได้ใช้คำตอบ Sigrist เพื่อทำวิธีนี้หลังจากบันทึกไฟล์ของฉัน:

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

และมันช่วยแก้ปัญหาของฉัน Thx Sigrist ฉันใส่ไว้ที่นี่เนื่องจากฉันไม่พบ Answare เกี่ยวกับเรื่องนี้สำหรับ Xamarin และฉันหวังว่ามันจะช่วยคนอื่น


1

ในกรณีของฉันวิธีแก้ปัญหาข้างต้นไม่ได้ผลฉันต้องทำสิ่งต่อไปนี้:

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

เป็นสิ่งที่ดีจริงๆที่รู้เกี่ยวกับตัวเลือกนี้ แต่น่าเสียดายที่ไม่สามารถใช้งานได้กับอุปกรณ์บางรุ่นที่ใช้ Android 6 ดังนั้นการContentProviderแก้ปัญหาที่ดีกว่า
Siarhei

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

รหัสนี้อยู่ในเมธอด onCreate รหัสนี้ใช้สำหรับสร้างไดเรกทอรีของ app_name ตอนนี้สามารถเข้าถึงไดเร็กทอรีนี้ได้โดยใช้แอพตัวจัดการไฟล์เริ่มต้นใน Android ใช้ filePath สตริงนี้ทุกที่ที่ต้องการเพื่อตั้งค่าโฟลเดอร์ปลายทางของคุณ ฉันแน่ใจว่าวิธีนี้ใช้ได้กับ Android 7 เช่นกันเพราะฉันทดสอบแล้วดังนั้นมันสามารถใช้ได้กับ Android เวอร์ชันอื่นด้วย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.