拍摄相机意图后删除图库图像

时间:2011-06-17 18:30:48

标签: android image android-camera-intent

我知道这有许多不同的方式,但我似乎无法从默认文件夹中删除图库图像。我正确地将文件保存到SD卡,我可以正确删除该文件,但是Camera文件夹下显示的默认图库文件不会删除。

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

有什么建议吗?

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

13 个答案:

答案 0 :(得分: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中有三个变量,用于存储有关当前文件的信息。字符串(路径),File变量和该文件的URI:

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

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

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

答案 1 :(得分:15)

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

我有一个捕获按钮,当它被按下时意图被发送,我添加的是我也从图像mediastore获取最后一个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大于记录后的如果多于一个则删除位于该位置的记录我为相机保存的位置

/*
 * 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 *,这似乎更强大。

答案 2 :(得分:15)

这将删除图库中的文件:

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)从图库中删除(使用上面的代码)文件。

但当然它似乎太完美......在某些设备中(例如原版Galaxy Tab with android 2.3),你必须使用EXTRA_OUTPUT和ACTION_IMAGE_CAPTURE,否则意图不起作用。

答案 3 :(得分:5)

我认为整个问题来自于您期望另一个应用程序发生某种结果但未发生的事实。这是(只是要明确) -

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

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

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

理想情况下,应该发生的事情是,如果有额外的信息,如fileName或location,那么他应该使用该位置,如果没有,那么他可以使用他自己的。但是,唉,我们来自一个理想的世界,因为在你推出相机应用程序时应该发生的事情没有任何结论,相机应用程序开发人员将有自己的解释。

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

因此,在某些相机中,它可能只保存在其他地方,也可能会保存在相机文件夹中(因为相机应用程序可能始终将捕获的图像保存在相机文件夹中并将其保存在不同的位置申请奖金)

如果您只是将一个fileName传递给相机应用程序,应该在拍摄一张照片后返回该应用程序?我想不是。那么在这种情况下应该怎么做?这一切都很模糊或灰色。

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

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

答案 4 :(得分:2)

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

如果你使用谷歌相机实现它工作正常。它只是没有将照片存储到EXTRA_OUTPUT中的库中。

但是当你面对其他一些设备时,他们可能会做一些完全不同的事情。这是因为HTC,LG和其他一些人都有自定义Camera实现,你无法做任何事情。您可以保持原样,也可以编写自己的相机,以完全符合您的需要。

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

答案 5 :(得分:2)

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

  1. 在调用相机意图之前以毫秒为单位保存当前时间。
  2. OnActivityResult查询在步骤1中拍摄日期大于毫秒的图像uri。并删除该文件。多数民众赞成。
  3. 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);
            }
    

答案 6 :(得分:1)

Look here - 这是一段将图片保存到EXTRA_OUTPUT文件夹而不将其保存到图库的代码。这样在我的应用程序中,我习惯直接从相机抓取图片,然后删除拍摄的图片。

答案 7 :(得分: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 folde中,而是保存在指定的位置。我已经在我的Nexus1和廉价的三星产品上试过了这个代码。

希望这有帮助

答案 8 :(得分:1)

我也在努力解决这个问题。顺便说一句,此问题在Android bug tracker中标记为Obsolete。出于某种原因,Android团队不再将此视为一个错误。也许是因为这与制造商有关。

似乎所有解决方案(来自此主题和其他博客文章等)都形成以下算法:

  1. 保存上次拍摄照片的唯一标识符;
  2. 使用Intent;
  3. EXTRA_OUTPUT启动相机活动
  4. 拍摄照片并检查相册中最后拍摄的照片的ID是否已更改,以决定是否应将其删除。
  5. 在接受的答案中提出了类似的东西。

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

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

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

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

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

    经过大量研究后,我认为克服这个问题的最佳方法是编写自己的相机引擎,或者只使用这样的第三方库:material camera

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

经过一段时间的努力,我咬紧牙关,写了我自己的相机caputere活动。我相信这比MediaStore.ACTION_IMAGE_CAPTURE解决方案更便携,更安全。图像将存储在您存储的位置,而不是其他地方,您不会意外删除某些不相关的文件。此外,可以使实际的相机功能完全符合要求。

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

值得注意的是,所有相机参数都应设置,尤其是照片尺寸,对焦模式和闪光模式,启动相机时它们都可能处于意外状态。

将此作为答案发布,而不是评论,因为在我看来,这是正确的答案,导致最小的努力

答案 11 :(得分: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;
}

答案 12 :(得分:0)

以下代码不仅删除图像文件,而且删除删除filw时留下的0Byte文件。我在Android 10中观察到此0Byte鬼文件行为。

CMD