【问题标题】:android - save image into galleryandroid - 将图像保存到画廊
【发布时间】:2012-01-23 12:49:23
【问题描述】:

我有一个带有图片库的应用程序,我希望用户可以将其保存到自己的图库中。 我创建了一个带有单个语音“保存”的选项菜单以允许这样做,但问题是...如何将图像保存到图库中?

这是我的代码:

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

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

【问题讨论】:

标签: java android imageview gallery save-image


【解决方案1】:
MediaStore.Images.Media.insertImage(getContentResolver(), yourBitmap, yourTitle , yourDescription);

之前的代码会将图片添加到图库末尾。如果您想修改日期以使其出现在开头或任何其他元数据,请参阅下面的代码(Cortesy of 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;
        }
    }
}

【讨论】:

  • 这会保存图像,但到画廊的最后,虽然当你用相机拍照时它会保存在顶部。如何将图像保存到图库顶部?
  • 请注意,您还必须将 添加到您的 manifext.xml。
  • 图像不会保存在图库顶部,因为在内部 insertImage 不会添加任何日期元数据。请参阅此 GIST:gist.github.com/0242ba81d7ca00b475b9.git 它是 insertImage 方法的精确副本,但它添加了日期元日期以确保将图像添加到画廊的前面。
  • Here is the correct GIST link mentioned above(需要去掉末尾的.git
  • MediaStore.Images.Media.insertImage(...) 已被弃用。
【解决方案2】:

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

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

【讨论】:

  • 这是一个不错的简单解决方案,因为我们不需要更改整个实现,并且我们可以为应用创建自定义文件夹。
  • 当您只能扫描文件时发送广播可能会浪费资源:stackoverflow.com/a/5814533/43051
  • 你实际上在哪里传递位图?
  • Environment.getExternalStoragePublicDirectoryIntent.ACTION_MEDIA_SCANNER_SCAN_FILE 现在已弃用...
【解决方案3】:

我已经尝试了很多方法来让它在 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 site too 中找到。

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

【讨论】:

  • 感谢您提供有关 Google 相册的信息。
  • 这是唯一一个解释得很好的解决方案。没有人提到该文件必须在 DCIM 文件夹中。谢谢!!!
  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) 为我解决了问题。谢谢!
  • getExternalStoragePublicDirectory() 现在在 API 29 上已弃用。需要使用 MediaStore
  • @riggaroo 是的,你是对的,丽贝卡,我会尽快更新答案
【解决方案4】:

根据this course,正确的做法是:

Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    )

这将为您提供图库目录的根路径。

【讨论】:

  • 我尝试了这个新代码,但它崩溃了 java.lang.NoSuchFieldError: android.os.Environment.DIRECTORY_PICTURES
  • 好的,谢谢,所以没有办法在 android
  • 完美 - 直接指向 Android 开发者网站的链接。这很有效,而且是一个简单的解决方案。
  • 不错的答案,但如果在此处的其他答案中添加“galleryAddPic”方法会更好,因为您通常希望 Gallery 应用程序注意到新图片。
  • Environment.getExternalStoragePublicDirectory 已被弃用...
【解决方案5】:
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);
    

    【讨论】:

    • 在这个标准中这是最好的答案
    • 您的代码中的 finalBitmap 是在哪里初始化的......示例中缺少它
    • @MichealPaccione 显然,您需要将图像放在位图中。如果您在创建需要保存的图像的位图对象时遇到问题,那就另当别论了。尝试先解决它。
    【解决方案7】:

    这对我有用:

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

    【讨论】:

      【解决方案8】:

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

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

      【讨论】:

        【解决方案9】:

        在我的情况下,上述解决方案不起作用,我必须执行以下操作:

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

        【讨论】:

        • 很高兴知道这个选项,但不幸的是不适用于某些带有 android 6 的设备,所以ContentProvider 更可取的 solytion
        【解决方案10】:
         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 上运行。

        【讨论】:

          【解决方案11】:

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

           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.this,new String[]{file.getPath()},new String[] {"image/jpeg"},null);

          轰!!!您现在可以在您的图库中获取您保存的图像。

          【讨论】:

            【解决方案12】:

            注意:对于Build.VERSION.SDK_INT

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

            这是 Q(29 级)之前的 Android 版本的一个错误。 29 级及更高版本直接保存到照片库。

            Android 清单 XML

            <!-- Adding Read External Storage Permission -->
            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
            <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
            

            保存功能

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-08-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-18
              相关资源
              最近更新 更多