web-dev-qa-db-ja.com

カメラの意図の写真が撮影された後のギャラリー画像の削除

私はこれがさまざまな方法で尋ねられたことを知っていますが、それでもギャラリーの画像をデフォルトのフォルダーから削除することはできないようです。ファイルを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();
    }
}
64

私のアプリケーションでは、写真を撮るインテントを呼び出す必要があります。写真をギャラリーに置くことはできませんが、代わりに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();
}

アクティビティには、現在のファイルに関する情報を保存する3つの変数があります。文字列(パス)、ファイル変数、およびそのファイルへの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());
            }
         }
      }
   }      
}
70
Paul

誰かがこの問題のより簡単な回避策を探しているなら、ここに私が問題を解決した方法があります。

キャプチャボタンがあり、それが押されるとインテントが送信されます。追加したのは、イメージメディアストアから最後の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でテストしました。

別のサイドノート、私はもともとパラメーターとして* _ID *ではなく* DATE_TAKEN *を使用しましたが、エミュレーターでインテントを介してキャプチャされた画像の一部にミル秒* DATE_TAKEN *があったというバグがあるようです倍に1000を掛けたため、* _ ID *に切り替えました。

15
Emil Davtyan

これにより、ギャラリーからファイルが削除されます。

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 Android 2.3)では、ACTION_IMAGE_CAPTUREでEXTRA_OUTPUTを使用する必要があります。作業。

15
ElYeante

さて、私が思う全体の問題は、あなたが起こっていない別のアプリケーションから特定の結果を期待しているという事実から来ています。これは(明確にするため)-

  • アプリケーションは画像をキャプチャしないため、写真を撮ることができるアクティビティを起動します。
  • 写真を保存する場所を他のアプリケーションに伝えます
  • 指定した場所に保存された画像を使用します。
  • しかし、問題は、他のアプリケーションも別の場所に画像を保存しているため、それを望まないことです。

次に、画像をキャプチャしている他のアプリケーションの観点から見てみましょう。 2つのシナリオを考えてみましょう(可能な場合)-

  • 最初に、ファイル名または画像を保存する場所のオプションを使用してアプリケーションを起動します。
  • 次に、fileNameや画像の保存場所などの追加情報なしでアプリケーションが起動されます(キャプチャアプリがメニューから直接起動された場合に発生する可能性があります)

ここで理想的なのは、fileNameや場所などの追加情報がある場合に、そうでない場合は自分の場所を使用できるようにする必要があるということです。しかし、悲しいかな私たちは理想的な世界から来たものであり、カメラアプリケーションを起動したときに何が起こるべきかについて何も書かれていないため、カメラアプリの開発者は独自の解釈を持ちます。

デバイスごとに異なるカメラアプリケーションがデフォルトとして設定されているため(はい、通常、ストックカメラは交換されます)、得られる結果は異なります。

そのため、カメラによっては別の場所に1か所だけ保存される場合があり、カメラフォルダーにも保存される場合があります(カメラアプリケーションは常にキャプチャした画像をカメラフォルダーに保存し、別の場所に保存することはアプリケーション)

FileNameをカメラアプリケーションに渡すだけの場合、アプリケーションは写真を1枚撮っただけで復帰しますか私はそうは思いません。それでは、このようなシナリオでは何をすべきでしょうか?それはすべて非常に曖昧または灰色の領域です。

カメラフォルダーに保存したくない場合は、最近キャプチャした画像のファイル名を取得できるかどうかを確認し、アプリケーションからdelete itを選択します。または、保存する必要がある場所をカメラアプリケーションに指示せずに、ファイル名を取得して必要な場所に移動するだけです。

ファイル名を取得できない場合は、他のアプリケーション開発者に任せてください。 (ちょっと!それは彼のせいでもない、彼は彼が望むようにそれを設計しただけ!)

5
bluefalcon

あなたがやりたいことはできないと思います。それは悲しいですが、私は別の答えを見つけることができません。

Googleカメラの実装を使用する場合、正常に機能します。 EXTRA_OUTPUTが指定されている場合、ギャラリーに写真を保存しません。

ただし、他のデバイスに直面すると、まったく異なる動作をする可能性があります。それは、HTC、LGおよび多分他のいくつかはカスタムカメラの実装を持ち、それについて何もできないからです。君はそれが要る。

実際にはこの質問とは関係ありませんが、ある日、一部のデバイスではCROPの意図が機能しないことがわかります( http://groups.google.com/group/Android-developers/browse_frm/thread/2dd647523926192c/4b6d087073a39607?tvc = 1&pli = 1 )。繰り返しになりますが、必要な場合は自分で作成する必要があります。

2
Fedor

これは簡単に解決できる作業ではありませんが、トリッキーな方法に近づく可能性があります。以下のコードを試してください:

  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);
        }
2
ms shaiju

私もこの問題に苦労していました。ところで、この問題はAndroid バグトラッカーObsoleteとしてマークされています。何らかの理由でAndroidチームはこれをもうバグとは見なしていません。

すべてのソリューション(このスレッドや他のブログ投稿などから)は次のアルゴリズムを形成しているようです:

  1. 最後に撮影した写真の一意の識別子を保存します。
  2. IntentEXTRA_OUTPUTを使用してカメラアクティビティを起動します。
  3. 写真を撮影し、ギャラリーで最後に撮影した写真のIDが変更されたかどうかを確認して、削除するかどうかを決定します。

同様のことが、受け入れられた回答で提案されています。

しかし、私はそのようなアプローチは生産コードでは使用できないことを発見しました。たとえば、簡単な状況をイメージしてみましょう。

最後に撮影した写真のIDをギャラリーに保存し、カメラアクティビティを起動します。ユーザーが一部のデバイスで写真を撮ると、カメラのアクティビティにCancelボタンとOKボタン(またはそのようなもの)を含むダイアログが表示されます。そして、それらの背後にあるロジックは次のとおりです。

  • ユーザーがCancelを押すと、カメラアクティビティに戻り、写真を撮り続けることができます。
  • ユーザー(ユーザー)がOKボタンを押すと、カメラアクティビティは結果をアプリに返します。

ここでの重要な注意事項:一部のデバイス(LGおよびHTCでテスト済み)では、画像を既に保存できますこのダイアログが表示されている間。

ユーザーが何らかの理由でHomeボタンをタップして別のアプリ(たとえば、ユーザーが別の写真を撮ることができる別のカメラアプリ)を起動すると決めた場合を想像してください。そして、そのダイアログがまだ表示されているアプリに戻り、OKを押します。明らかにその場合、あなたのアプリは間違ったファイルを削除します...そしてユーザーを失望させます:(

したがって、多くの調査を行った後、この問題を克服するための最良のアプローチは、独自のカメラエンジンを記述するか、次のようなサードパーティライブラリを使用することであると判断しました。 material camera

1
Stan Mots

こちらをご覧ください -これは、ギャラリーに保存せずにEXTRA_OUTPUTフォルダーに画像を保存するコードです。この方法で、アプリケーションでカメラから直接画像を取得し、撮影した画像を削除していました。

1
barmaley

これは私が使用するコードで、写真を取り、指定された場所に保存します

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

保存されたパスonActivityResultを取得する方法が見つからなかったため、outputFileUriをグローバル変数として宣言する必要がありました。 outputFileUriを渡すことで、画像がカメラフォルダーに保存されているのではなく、指定された場所に保存されていることがわかります。 Nexus1でこのコードを試してみましたが、安価なサムスン製品です。

お役に立てれば

1
Bear
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;
}
0
Vignes

最善の策は、おそらくCameraクラスを直接処理し、コールバックで返されたjpegまたはrawを必要な場所に保存することです。

または、追加したメディアのコンテンツプロバイダーから_idで撮影した画像を削除してみることもできます。クエリでそれを見つけ、ContentResolver.deleteで削除しますが、実装についてはわかりません。

0
HaMMeReD

しばらくこれに苦労した後、私は弾丸を噛み、自分のカメラキャプチャアクティビティを書きました。これはMediaStore.ACTION_IMAGE_CAPTUREソリューションよりもはるかにポータブルで安全であると確信しています。画像は保存した場所に保存され、他の場所には保存されません。関連性のないファイルを誤って削除する危険はありません。また、実際のカメラ機能を要件に正確に適合させることもできます。

移植性の理由から、camera2ではなくCameraクラスを使用しました。

カメラのパラメータ、特に画像サイズ、フォーカスモード、フラッシュモードをすべて設定する必要があり、カメラの起動時にこれらがすべて予期しない状態になる可能性があることに注意してください。

これはコメントとしてではなく、回答として投稿します。これは、私の意見では最小限の労力をもたらす正しい回答だからです。

0
Moritz Both