65

我知道这已经以许多不同的方式被问过,但我似乎仍然无法从默认文件夹中删除图库图像。我将文件正确保存到 SD 卡,我可以很好地删除该文件,但在文件夹 Camera 下显示的默认图库图像文件不会删除。

我希望在返回活动后删除图像,因为该文件已经存储在 SD 卡下的/Coupon2.

有什么建议么?

public void startCamera() {
    Log.d("ANDRO_CAMERA", "Starting camera on the phone...");

    mManufacturerText = (EditText) findViewById(R.id.manufacturer);
    String ManufacturerText = mManufacturerText.getText().toString();
    String currentDateTimeString = new Date().toString();

    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    File filedir = new File(Environment.getExternalStorageDirectory()+"/Coupon2");
    filedir.mkdirs();

    File file = new File(Environment.getExternalStorageDirectory()+"/Coupon2", ManufacturerText+"-test.png");
    outputFileUri = Uri.fromFile(file);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);

    startActivityForResult(intent, CAMERA_PIC_REQUEST);
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CAMERA_PIC_REQUEST && resultCode == -1) {  
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.putExtra("crop", "true");
        intent.putExtra("scale", "true");

        intent.putExtra("return-data", false);
        intent.setDataAndType(outputFileUri, "image/*");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
        startActivityForResult(intent, CAMERA_CROP_REQUEST);
    }else { 
        SetImage();
        saveState();
    }
}
4

13 回答 13

70

我的应用程序要求我调用拍照的意图。照片不能在图库中,而必须在 SD 卡上的特定目录中。

最初我只使用了 EXTRA_OUTPUT,但我很快发现了以下内容: - 一些设备完全使用它并跳过图库。- 有些设备完全忽略它,只使用图库。- 有些设备真的很糟糕,将全尺寸图像保存到画廊,只将缩略图保存到我想要的位置。(HTC你知道你是谁......)

所以,我不能在完成后盲目地删除图库文件。最后添加的照片可能是也可能不是我要删除的照片。此外,我之后可能必须复制该文件替换我自己的文件。因为我的活动是 2000 行,而且我的公司不希望发布我们所有的代码,所以我只发布了执行此操作所涉及的方法。希望这会有所帮助。

另外,我会说,这是我的第一个 Android 应用程序。如果有一种我不知道的更好的方法来做到这一点,我不会感到惊讶,但这对我有用!

所以,这是我的解决方案:

首先,在我的应用程序上下文中,我定义了一个变量,如下所示:

public ArrayList<String> GalleryList = new ArrayList<String>();

接下来,在我的活动中,我定义了一个方法来获取图库中所有照片的列表:

private void FillPhotoList()
{
   // initialize the list!
   app.GalleryList.clear();
   String[] projection = { MediaStore.Images.ImageColumns.DISPLAY_NAME };
   // intialize the Uri and the Cursor, and the current expected size.
   Cursor c = null; 
   Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
   //
   // Query the Uri to get the data path.  Only if the Uri is valid.
   if (u != null)
   {
      c = managedQuery(u, projection, null, null, null);
   }

   // If we found the cursor and found a record in it (we also have the id).
   if ((c != null) && (c.moveToFirst())) 
   {
      do 
      {
        // Loop each and add to the list.
        app.GalleryList.add(c.getString(0));
      }     
      while (c.moveToNext());
   }
}

这是为我的新图像返回唯一文件名的方法:

private String getTempFileString()
{
   // Only one time will we grab this location.
   final File path = new File(Environment.getExternalStorageDirectory(), 
         getString(getApplicationInfo().labelRes));
   //
   // If this does not exist, we can create it here.
   if (!path.exists())
   {
      path.mkdir();
   }
   //
   return new File(path, String.valueOf(System.currentTimeMillis()) + ".jpg").getPath();
}

我的 Activity 中有三个变量为我存储有关当前文件的信息。字符串(路径)、文件变量和该文件的 URI:

public static String sFilePath = ""; 
public static File CurrentFile = null;
public static Uri CurrentUri = null;

我从不直接设置这些,我只在文件路径上调用一个设置器:

public void setsFilePath(String value)
{
   // We just updated this value. Set the property first.
   sFilePath = value;
   //
   // initialize these two
   CurrentFile = null;
   CurrentUri = null;
   //
   // If we have something real, setup the file and the Uri.
   if (!sFilePath.equalsIgnoreCase(""))
   {
      CurrentFile = new File(sFilePath);
      CurrentUri = Uri.fromFile(CurrentFile);
   }
}

现在我打电话给拍照的意图。

public void startCamera()
{
   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   // Specify the output. This will be unique.
   setsFilePath(getTempFileString());
   //
   intent.putExtra(MediaStore.EXTRA_OUTPUT, CurrentUri);
   //
   // Keep a list for afterwards
   FillPhotoList();
   //
   // finally start the intent and wait for a result.
   startActivityForResult(intent, IMAGE_CAPTURE);
}

完成此操作后,活动又回来了,这是我的代码:

protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
   if (requestCode == IMAGE_CAPTURE)
   {
      // based on the result we either set the preview or show a quick toast splash.
      if (resultCode == RESULT_OK)
      {
         // This is ##### ridiculous.  Some versions of Android save
         // to the MediaStore as well.  Not sure why!  We don't know what
         // name Android will give either, so we get to search for this
         // manually and remove it.  
         String[] projection = { MediaStore.Images.ImageColumns.SIZE,
                                 MediaStore.Images.ImageColumns.DISPLAY_NAME,
                                 MediaStore.Images.ImageColumns.DATA,
                                 BaseColumns._ID,};
         //    
         // intialize the Uri and the Cursor, and the current expected size.
         Cursor c = null; 
         Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
         //
         if (CurrentFile != null)
         {               
            // Query the Uri to get the data path.  Only if the Uri is valid,
            // and we had a valid size to be searching for.
            if ((u != null) && (CurrentFile.length() > 0))
            {
               c = managedQuery(u, projection, null, null, null);
            }
            //   
            // If we found the cursor and found a record in it (we also have the size).
            if ((c != null) && (c.moveToFirst())) 
            {
               do 
               {
                  // Check each area in the gallary we built before.
                  boolean bFound = false;
                  for (String sGallery : app.GalleryList)
                  {
                     if (sGallery.equalsIgnoreCase(c.getString(1)))
                     {
                        bFound = true;
                        break;
                     }
                  }
                  //       
                  // To here we looped the full gallery.
                  if (!bFound)
                  {
                     // This is the NEW image.  If the size is bigger, copy it.
                     // Then delete it!
                     File f = new File(c.getString(2));

                     // Ensure it's there, check size, and delete!
                     if ((f.exists()) && (CurrentFile.length() < c.getLong(0)) && (CurrentFile.delete()))
                     {
                        // Finally we can stop the copy.
                        try
                        {
                           CurrentFile.createNewFile();
                           FileChannel source = null;
                           FileChannel destination = null;
                           try 
                           {
                              source = new FileInputStream(f).getChannel();
                              destination = new FileOutputStream(CurrentFile).getChannel();
                              destination.transferFrom(source, 0, source.size());
                           }
                           finally 
                           {
                              if (source != null) 
                              {
                                 source.close();
                              }
                              if (destination != null) 
                              {
                                 destination.close();
                              }
                           }
                        }
                        catch (IOException e)
                        {
                           // Could not copy the file over.
                           app.CallToast(PhotosActivity.this, getString(R.string.ErrorOccured), 0);
                        }
                     }
                     //       
                     ContentResolver cr = getContentResolver();
                     cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                        BaseColumns._ID + "=" + c.getString(3), null);
                     break;                        
                  }
               } 
               while (c.moveToNext());
            }
         }
      }
   }      
}
于 2011-07-06T16:56:36.243 回答
18

这将从图库中删除文件:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == CAMERA_PIC_REQUEST && resultCode == RESULT_OK) { 

        /* Copy the file to other directory or whatever you want */

        // mContext is the context of the activity
        mContext.getContentResolver().delete(data.getData(), null, null);
    }
 }

关于 EXTRA_OUTPUT 不是标准行为。我认为这种伪算法应该适用于每种情况:

1) 不要使用EXTRA_OUTPUT。图像/照片始终会转到图库位置。

2) 将文件从图库位置复制到所需位置。

3)从图库中删除(使用上面的代码)文件。

但当然它似乎太完美了……在某些设备中(例如带有 android 2.3 的原始 Galaxy Tab),您必须将 EXTRA_OUTPUT 与 ACTION_IMAGE_CAPTURE 一起使用,否则意图不起作用。

于 2013-01-30T12:39:45.207 回答
15

如果有人正在寻找更简单的解决方法来解决这个问题,这就是我解决问题的方法。

我有一个捕获按钮,当它被按下时,意图被发送,我添加的是我也去从图像媒体存储中获取最后一个 id 并存储它:

/**
 * Gets the last image id from the media store
 * @return
 */
private int getLastImageId(){
    final String[] imageColumns = { MediaStore.Images.Media._ID };
    final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
    final String imageWhere = null;
    final String[] imageArguments = null;
    Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, imageWhere, imageArguments, imageOrderBy);
    if(imageCursor.moveToFirst()){
        int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
        imageCursor.close();
        return id;
    }else{
        return 0;
    }
}

然后,当活动返回时,我运行此代码以检查捕获前的最后一个图像 ID,然后在捕获后查询图像的 ID 大于记录的 ID,如果大于则删除位于我为相机指定的位置的记录保存在。

/*
 * Checking for duplicate images
 * This is necessary because some camera implementation not only save where you want them to save but also in their default location.
 */
final String[] imageColumns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.SIZE, MediaStore.Images.Media._ID };
final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
final String imageWhere = MediaStore.Images.Media._ID+">?";
final String[] imageArguments = { Integer.toString(MyActivity.this.captureLastId) };
Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, imageWhere, imageArguments, imageOrderBy);
if(imageCursor.getCount()>1){
    while(imageCursor.moveToNext()){
        int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
        String path = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
        Long takenTimeStamp = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN));
        Long size = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.SIZE));
        if(path.contentEquals(MyActivity.this.capturePath)){
            // Remove it
            ContentResolver cr = getContentResolver();
            cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media._ID + "=?", new String[]{ Long.toString(id) } );
            break;
        }
    }               
}
imageCursor.close();

对我来说,这是一个更简单的解决方案,我在遇到此问题的 HTC 上进行了测试。

另一个旁注,我最初使用 *DATE_TAKEN* 而不是 *_ID* 作为参数,但似乎存在一些错误,即在模拟器上通过意图捕获的一些图像的毫秒 *DATE_TAKEN* 时间乘以 1000 所以我切换到 *_ID* ,这似乎更加强大。

于 2011-12-19T00:15:13.367 回答
5

好吧,我认为整个问题来自这样一个事实,即您期望来自另一个没有发生的应用程序的特定结果。这是(只是为了清楚)-

  • 启动一个可以拍照的活动,因为您的应用程序不捕获图像。
  • 告诉其他应用程序您要将图片保存在哪里
  • 在您指定的位置使用保存的图像。
  • 但问题是其他应用程序也将图像保存在不同的位置,这是您不希望的。

现在让我们从另一个应用程序的角度来看它,即捕获图像。让我们考虑两种情况(这是可能的) -

  • 首先,使用文件名或要保存图像的位置选项启动应用程序。
  • 其次,应用程序在没有任何额外信息的情况下启动,例如文件名或要保存图像的位置。(如果直接从菜单启动捕获应用程序,可能会发生这种情况)

现在理想情况下应该发生的是,如果有额外的信息,如文件名或位置,那么他应该使用那个位置,如果没有,那么他可以使用他自己的。但遗憾的是,我们来自一个理想的世界,因为对于启动相机应用程序时应该发生的事情没有什么是一成不变的,相机应用程序开发人员将有自己的解释。

由于不同的设备将不同的相机应用程序设置为默认设置(是的,通常会更换库存相机),因此获得的结果/将有所不同。

因此,在某些相机中,它可能仅保存在一个位置,而其他位置也可能保存在相机文件夹中(因为相机应用程序可能总是将捕获的图像保存在相机文件夹中,并将其保存在不同的位置是由应用程序)

如果您只是将文件名传递给相机应用程序,应用程序应该在拍摄一张照片后返回吗?我想不是。那么在这种情况下应该怎么做呢?这一切都非常模棱两可或灰色地带。

现在,如果您不希望将其保存在“相机”文件夹中,请查看是否可以获取最近捕获的图像的文件名,然后将其从您的应用程序中删除。或者不要告诉相机应用程序需要将其保存在哪里,只需获取文件名并将其移动到您想要的位置即可。

如果您没有获得文件名,那么您将受到其他应用程序开发人员的摆布。(嘿!这也不是他的错,他只是按照他想要的方式设计!)

于 2011-06-27T10:03:14.060 回答
2

这不是一项容易解决的任务,但可以采用一种棘手的方法。如果有人需要一个简单且有效的解决方案。试试下面的代码:

  1. 在调用相机意图之前以毫秒为单位保存当前时间。
  2. OnActivityResult 在step1中查询拍摄日期大于毫秒的图片uri。并删除文件。而已。
String[] projection = {MediaStore.Images.ImageColumns.SIZE,
                MediaStore.Images.ImageColumns.DISPLAY_NAME,
                MediaStore.Images.ImageColumns.DATA,
                BaseColumns._ID,MediaStore.Images.ImageColumns.DATE_ADDED};
        final String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
        final String selection = MediaStore.Images.Media.DATE_TAKEN+" > "+mImageTakenTime;
        //// intialize the Uri and the Cursor, and the current expected size.
        Cursor c = null;
        Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        c = getActivity().managedQuery(u, projection, selection, null, imageOrderBy);
        if(null != c && c.moveToFirst()){
            ContentResolver cr = getActivity().getContentResolver();
            cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    BaseColumns._ID + "=" + c.getString(3), null);
        }
于 2015-01-22T11:53:17.147 回答
2

我认为你不能做你想做的事。这很难过,但我找不到其他答案。

如果您使用谷歌相机实现它工作正常。它只是不将照片存储到指定的 EXTRA_OUTPUT 中的画廊。

但是当你面对其他一些设备时,它们可能会做一些完全不同的事情。那是因为 HTC、LG 和其他一些可能有自定义相机实现,而您对此无能为力。您可以保持原样,也可以编写自己的相机,以完全按照您的需要工作。

实际上它与这个问题无关,但有一天你会发现 CROP 意图在某些设备上不起作用(http://groups.google.com/group/android-developers/browse_frm/thread/2dd647523926192c/4b6d087073a39607 ?tvc=1&pli=1 )。因此,如果您需要它,您将不得不自己编写它。

于 2011-06-27T14:54:27.577 回答
1

这是我使用的代码,它拍摄一张照片并将其保存在指定的位置

Uri outputFileUri;

public void takePhoto() {
    File directory = new File(Environment.getExternalStorageDirectory()
            + "/HI-Tech" + "/");

    if (!directory.exists()) {
        directory.mkdir();
    }

    int count = 0;
    if (directory != null) {
        File[] files = directory.listFiles();
        if (files != null) {
            count = files.length;
        }
    }
    count++;
    String imagePath = "IMAGE_" + count + ".jpg";
    File file = new File(directory, imagePath);
    outputFileUri = Uri.fromFile(file);

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
    startActivityForResult(intent, JobActivity.TAKE_PIC);
}

然后我处理响应。

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    try {
        if (resultCode == RESULT_OK) {
            if (requestCode == JobActivity.TAKE_PIC) {
                Bitmap bitmap = decodeFile(new File(outputFileUri.getPath()), 160, 120);
    } catch (Exception e) {
        Log.e("Error", "Unable to set thumbnail", e);
    }
}

我不得不将 outputFileUri 声明为全局变量,因为我发现无法获取保存的路径 onActivityResult。通过传递 outputFileUri,您会注意到图像没有保存在 Camera 文件夹中,而是保存在指定的位置。我已经在我的 Nexus1 和一个便宜的三星设备上试过这个代码。

希望这可以帮助

于 2011-06-23T23:44:28.180 回答
1

看这里- 这是一段将图片保存到EXTRA_OUTPUT文件夹而不将其保存到图库的代码。通过这种方式,在我的应用程序中,我曾经直接从相机中抓取照片,然后删除拍摄的照片。

于 2011-06-23T05:02:54.930 回答
1

我也在为这个问题而苦苦挣扎。顺便说一句,这个问题Obsolete在 Android bug tracker中被标记为。出于某种原因,Android 团队不再认为这是一个错误。也许是因为这与制造商有关。

似乎所有解决方案(来自这个线程和其他博客文章等)都形成了以下算法:

  1. 保存最后一张照片的唯一标识符;
  2. Intent使用with启动相机活动EXTRA_OUTPUT
  3. 拍照并检查图库中最后一张照片的 id 是否已更改,以决定是否应将其删除。

在接受的答案中提出了类似的建议。

但我发现这种方法不能用于生产代码。例如,让我们想象一个简单的情况:

您将最后拍摄的照片的一些 id 存储在图库中并启动相机活动。当用户在某些设备上拍照时,相机活动会显示一个带有CancelOK按钮(或类似的东西)的对话框。它们背后的逻辑如下:

  • 如果用户按下Cancel他返回到相机活动并可以继续拍照。
  • 如果他/她(用户)按下OK按钮,相机活动会将结果返回给您的应用程序。

这里的重要说明:事实证明,在某些设备上(我在 LG 和 HTC 上测试过),在显示此对话框时图像已经保存。

现在想象一下,如果用户出于某种原因决定点击Home按钮并启动另一个应用程序(例如,另一个用户可以拍摄不同照片的相机应用程序!)。然后返回您的应用程序,在该应用程序中仍然显示该对话框并按下OK。显然,在这种情况下,您的应用程序将删除错误的文件......并使用户失望:(

因此,经过大量研究后,我决定克服这个问题的最佳方法是编写自己的相机引擎,或者只使用一些第三方库,例如:material camera

于 2016-11-08T19:24:02.880 回答
0

您最好的选择可能是直接处理 Camera 类,然后将回调中返回的 jpeg 或 raw 存储在您想要的位置。

或者,您可以尝试在添加后从媒体内容提供商中删除按 _id 拍摄的图像。只需通过查询找到它并通过 ContentResolver.delete 将其删除,但不确定实现。

于 2011-06-27T21:54:01.930 回答
0

在为此苦苦挣扎了一段时间后,我硬着头皮写了自己的camera caputere活动。我相信这比MediaStore.ACTION_IMAGE_CAPTURE解决方案更便携、更安全。图像将存储在您存储它的地方,而不是其他地方,您不会有意外删除一些不相关文件的危险。此外,可以使实际的相机功能完全符合要求。

出于便携性的原因,我使用了Camera类,而不是camera2。

需要注意的是,相机参数都要设置好,尤其是图片大小、对焦模式和闪光灯模式,在启动相机时都可能会处于意外状态。

将其发布为答案,而不是评论,因为在我看来,这是正确的答案,只需付出最少的努力

于 2017-03-15T11:42:51.443 回答
0
protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    if (requestCode == CAMERA_REQUEST && resultCode == RESULT_OK) {

    Bitmap photo = (Bitmap) data.getExtras().get("data");
    Uri tempUri = Utils.getUri(getActivity(), photo);
    File finalFile = new File(getRealPathFromURI(tempUri));
    finalFile.delete();
    }  
}


public String getRealPathFromURI (Uri contentUri) {
    String path = null;
    String[] proj = { MediaStore.MediaColumns.DATA };
    Cursor cursor = getActivity().getContentResolver().query(contentUri, proj, null, null, null);
    if (cursor.moveToFirst()) {
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
        path = cursor.getString(column_index);
    }
    cursor.close();
    return path;
}
于 2018-04-16T12:23:44.313 回答
0

下面的代码不仅删除了图像文件,还删除了删除 filw 时留下的 0Byte 文件。我在 Android 10 中观察到了这种 0Byte 鬼文件行为。

    val fileToDelete = File(photoUri.path)
    if (fileToDelete.exists()) {
        if (fileToDelete.delete()) {
            if (fileToDelete.exists()) {
                fileToDelete.canonicalFile.delete()
                if (fileToDelete.exists()) {
                    getApplicationContext().deleteFile(fileToDelete.name)
                }
            }
            Log.e("File", "Deleted " + savedPhotoUri?.path)
        } else {
            Log.e("File", "not Deleted " + savedPhotoUri?.path)
        }
    }
于 2020-04-17T11:27:41.587 回答