将图像保存到库中



我有一个图片库的应用程序,我希望用户可以将其保存到自己的画廊。我已经创建了一个选项菜单,只有一个声音"保存",但问题是……如何将图像保存到图库中?

这是我的代码:

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

保存到图库是否正确?不幸的是,代码不工作:(

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

前面的代码将在图库的末尾添加图像。如果您想修改日期,使其出现在开头或任何其他元数据,请参阅下面的代码(由S-K提供,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;
        }
    }
}

实际上,你可以把你的图片保存在任何地方。如果您想保存在公共空间,以便任何其他应用程序都可以访问,请使用以下代码:

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

这张照片不能放进相册。为此,您需要调用scan:

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找到更多信息

我在《Marshmallow》和《Lollipop》中尝试了很多方法。最后,我最终将保存的图片移动到DCIM文件夹(新的谷歌照片应用程序扫描图像,只有当它们显然在这个文件夹内)

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

然后是扫描文件的标准代码,你也可以在谷歌开发者网站上找到。

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的权限。

根据本课程,正确的方法是:

Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    )

这将给你画廊目录的根路径

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

您可以在相机文件夹中创建一个目录并保存图像。之后,您可以简单地执行扫描。它会立即显示您的图像在画廊。

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

我是这么做的:

 private fun saveBitmapAsImageToDevice(bitmap: Bitmap?) {
    // Add a specific media item.
    val resolver = this.contentResolver
    val imageStorageAddress = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
    } else {
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    }
    val imageDetails = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "my_app_${System.currentTimeMillis()}.jpg")
        put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
        put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis())
    }
    try {
        // Save the image.
        val contentUri: Uri? = resolver.insert(imageStorageAddress, imageDetails)
        contentUri?.let { uri ->
            // Don't leave an orphan entry in the MediaStore
            if (bitmap == null) resolver.delete(contentUri, null, null)
            val outputStream: OutputStream? = resolver.openOutputStream(uri)
            outputStream?.let { outStream ->
                val isBitmapCompressed =
                    bitmap?.compress(Bitmap.CompressFormat.JPEG, 95, outStream)
                if (isBitmapCompressed == true) {
                    outStream.flush()
                    outStream.close()
                }
            } ?: throw IOException("Failed to get output stream.")
        } ?: throw IOException("Failed to create new MediaStore record.")
    } catch (e: IOException) {
        throw e
    }
}

注意:对于Build.VERSION.>SDK_INT & lt;29、图像必须首先保存在本地磁盘上,这将随着用户保存更多图像而增加应用程序的大小。用户可以稍后在"文件"应用程序中删除图像,但本地图像必须与云中的谷歌照片或亚马逊照片同步。

将图像保存到云是通过让用户在导出后和删除应用程序APK之前打开他们的Google Photos或Amazon Photos应用程序来完成的。如果

这是Android build在Q (Level 29)之前的一个bug。第29级和以后直接保存到照片库。

Android Manifest XML

<!-- Adding Read External Storage Permission -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<<p> 保存函数/strong>
// - Save Image -
@Throws(FileNotFoundException::class)
private fun saveImage(
    bitmap: Bitmap,
    context: Context,
    folderName: String
) {
    if (Build.VERSION.SDK_INT >= 29) {
        val values = ContentValues()
        values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/$folderName")
        values.put(MediaStore.Images.Media.IS_PENDING, true)
        // RELATIVE_PATH and IS_PENDING are introduced in API 29.
        val uri: Uri? = context.contentResolver
            .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        if (uri != null) {
            saveImageToStream(bitmap, context.contentResolver.openOutputStream(uri))
            values.put(MediaStore.Images.Media.IS_PENDING, false)
            context.contentResolver.update(uri, values, null, null)
        }
    } else {
        var dir = File(
            applicationContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
            ""
        )
        // getExternalStorageDirectory is deprecated in API 29
        if (!dir.exists()) {
            dir.mkdirs()
        }
        val date = Date()
        val fullFileName = "myFileName.jpeg"
        val fileName = fullFileName?.substring(0, fullFileName.lastIndexOf("."))
        val extension = fullFileName?.substring(fullFileName.lastIndexOf("."))
        var imageFile = File(
            dir.absolutePath
                .toString() + File.separator
                    + fileName + "_" + Timestamp(date.time).toString()
                    + ".jpg"
        )
        println("imageFile: $imageFile")
        saveImageToStream(bitmap, FileOutputStream(imageFile))
        if (imageFile.getAbsolutePath() != null) {
            val values = ContentValues()
            values.put(MediaStore.Images.Media.DATA, imageFile.absolutePath)
            // .DATA is deprecated in API 29
            context.contentResolver
                .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        }
    }
}
private fun contentValues(): ContentValues? {
    val values = ContentValues()
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
    values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
    values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
    return values
}
private fun saveImageToStream(bitmap: Bitmap, outputStream: OutputStream?) {
    println("saveImageToStream")
    if (outputStream != null) {
        try {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
            outputStream.close()
            // success dialog
            runOnUiThread {
                val successDialog = SuccessDialog.getInstance(null)
                successDialog.show(supportFragmentManager, SuccessDialog.TAG)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            // warning dialog
            runOnUiThread {
                val warningDialog = WarningDialog.getInstance(null)
                warningDialog.show(supportFragmentManager, WarningDialog.TAG)
            }
        }
    }
}

我带着同样的疑问来到这里,但对于Android的Xamarin,我使用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);
} 

,它解决了我的问题,谢谢Sigrist。我把它放在这里,因为我没有找到关于Xamarin的答案,我希望它能帮助其他人。

在我的情况下,上面的解决方案不起作用,我不得不做以下事情:

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

这段代码在onCreate方法中。这段代码用于创建a PP_name目录。现在,这个目录可以在android中使用默认的文件管理器应用程序访问。在需要设置目标文件夹的地方使用这个字符串filePath。我确信这种方法也适用于Android 7,因为我在它上测试过。因此,它也可以在其他版本的android上工作。

只需在保存完成后扫描您的媒体。

 BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
            Bitmap bitmap = drawable.getBitmap();
            File filepath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
            File dir = new File(filepath.getAbsolutePath()+"/Pro Scanner/");
            if(!dir.exists()){
                dir.mkdir();
            }
            File file = new File(dir,System.currentTimeMillis()+"_Pro_Scanner.png");
            try {
                outputStream = new FileOutputStream(file);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                downloadQRCode.setVisibility(View.VISIBLE);
                loadingBar.setVisibility(View.INVISIBLE);
            }
            bitmap.compress(Bitmap.CompressFormat.PNG,100,outputStream);
            Toast.makeText(GenerateQRCodeActivity.this, "QR image saved successfully", Toast.LENGTH_SHORT).show();
            try {
                outputStream.flush();
                outputStream.close();
                loadingBar.setVisibility(View.INVISIBLE);
                downloadDone.setVisibility(View.VISIBLE);
                downloadDone.setAnimation(bottomAnimation);
            } catch (IOException e) {
                downloadQRCode.setVisibility(View.VISIBLE);
                loadingBar.setVisibility(View.INVISIBLE);
                e.printStackTrace();
            }
            MediaScannerConnection.scanFile(GenerateQRCodeActivity.this,new String[]{file.getPath()},new String[] {"image/jpeg"},null);

这些代码是相同的,每个人。如果你尝试下面的代码之后,它将工作。你只需要这一行代码:

MediaScannerConnection.scanFile (GenerateQRCodeActivity。这一点,新String [] {file.getPath()},新String[]{"图像/jpeg"}, null);

繁荣! !您现在可以在您的图库中获得您保存的图像。

最新更新