0

デバイスのカメラを使用して写真を撮ることができるアプリケーションがあります。EXTRA_OUTPUT を割り当てずに ACTION_IMAGE_CAPTURE インテントを開始し、デフォルトの場所に作成されたファイルを file.renameTo を使用して独自のカスタムの場所に移動します。私のコードは次のようなものです:

/* Start camera activity without EXTRA_OUTPUT */
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, _REQUESTCODE_ATTACH_CAMERA);

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK) {
        switch(requestCode) {
            case _REQUESTCODE_ATTACH_CAMERA:
                /* Get path to most recently added image */
                final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA };
                final String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
                Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, null, null, imageOrderBy);
                String fullPath = "";
                if(imageCursor.moveToFirst()){
                    fullPath = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
                    imageCursor.close();
                }

                File f = Environment.getExternalStorageDirectory();
                f = new File(f.getAbsolutePath() + File.separator + "DCIM" + File.separator + MY_APP_NAME;
                if(!f.exists()) {
                    f.mkdirs();
                }

                /* Create new file based on name of most recently created image */
                File oldFile = new File(fullPath);
                String newPath = f.getAbsolutePath() + File.separator + oldFile.getName() ;

                /* Move file with renameTo */
                oldFile.renameTo(new File(newPath));

                break;
            ...
        }
    }
}

これらはすべて非常にうまく機能しますが、奇妙なことが 1 つあります。私のアプリには、電話のギャラリーから既存の画像を選択できる別のボタンがあります。そのコードは次のようになります。

Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
galleryIntent.setType("image/*");
activity.startActivityForResult(galleryIntent, _REQUESTCODE_ATTACH_GALLERY);

これも機能しますが、上記のコードを使用してカメラで写真を撮り、ギャラリーから別の画像を選択しようとすると、ギャラリーに空白の「壊れたリンク」タイプのアイテムがあり、コンテンツが含まれておらず、選択できません。 . これらは、renameTo を使用して撮影および移動された写真に対応しているようです。ファイル名を LogCat に投稿するコードを onActivityResult に入れると、ログに記録される名前は、対応する以前に移動されたファイルの名前と同じになります。File オブジェクトを作成しようとしたり、何らかの方法でそのファイル名にアクセスしようとすると、null オブジェクトが生成され、強制的に閉じられます。

奇妙な点は、これらの「壊れたリンク」ファイルの証拠が Eclipse DDMS にも、ルート ブラウザーを使用している場合の電話自体にもなく、SD カードを再マウントすると消えることです。

カメラでキャプチャした後に画像を移動する理由は、携帯電話のギャラリー ストレージが不要な画像でいっぱいになるのを避けるためです。これらの空の「壊れたリンク」タイプのファイルはストレージ スペースを占有していないように見えますが、ギャラリーをブラウズしようとするエンド ユーザーにとっては非常に煩わしいものです。ここで何が起こっているのか、またはこの問題を解決する方法について何か考えがある人はいますか?

編集: これはギャラリーがどのように見えるかを示す写真で、「壊れたリンク」タイプの画像が表示されています. これらのいずれかが私のアプリを使用して撮影されたすべての写真に表示され、SD カードを再マウントするとすべて消えます。 スクリーンショット表示

4

1 に答える 1

2

このSOスレッドのおかげで、私は解決策を発見しました。メディアコンテンツ用に保持されているテーブルがあるため、実際にはこのように動作することは理にかなっています。したがって、テーブルに通知せずに何かを削除すると、間違いなく「リンク切れ」タイプのシナリオが作成されます。

究極の解決策は、contentResolver.deleteを使用して、コンテンツリゾルバー内のファイルへの参照を削除することですが、機能することがわかった2つの異なる方法があります。

/* Moving with renameTo */
//Use the same exact code as I had before (shortened for brevity) to move the file
oldFile.renameTo(newFile);
//Get URI from contentResolver using file Id from cursor
Uri oldUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media._ID)));
//Delete old file
getContentResolver().delete(oldUri, null, null);

この方法でURIを取得する必要があるのは、ストレージ内のその場所へのパスではなく、contentResolver内の画像への参照が必要になるためです。この方法では、ファイルを移動してからそのファイルに対して削除関数を呼び出して、コンテンツリゾルバーをだましてファイルへのリンクを削除するため、一部の人にとっては汚い感じがする可能性があります。必要に応じて、renameToを使用せずに実行できるため、delete(...)を呼び出すと実際に画像が削除されます。

/* Moving with streams */
//Get streams
InputStream in = new FileInputStream(oldFile);
OutputStream out = new FileOutputStream(newFile);
byte[] buffer = new byte[1024];
int bytesRead = 0;
//Read old file into new file
while((bytesRead = in.read(buffer)) > 0) {
    out.write(buffer, 0, bytesRead);
}

//Get URI from contentResolver using file Id from cursor
Uri oldUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media._ID)));
//Delete old file
getContentResolver().delete(oldUri, null, null);

contentResolver.deleteの呼び出しはどちらの方法でも同じですが、画像が既に削除されている場合でも機能することを指摘したいと思います。

この間に私は、同じ問題を抱えている人が将来これに遭遇した場合に備えて、ここにも投稿することに気づかなかった問題の解決策を発見しました。新しい場所からデバイスギャラリーで画像を選択可能に保つには、変更が行われたことをメディアスキャナーに通知する必要があります。これを行うために私が見つけた2つの方法があります:

/* This is the only way that I know of to handle multiple new files at once. I 
   really would use this sparingly, however, since it will rescan the entire 
   SD Card. Not only could this take a long time if the user has a lot of files 
   on their card, it will also show a notification so it is not exactly a 
   transparent operation. */
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));

/* You *could* do multiple files with this by passing in the path for each one 
   in the array of Strings, however an instance of this will get called for each 
   one rather than it doing them all at once. Likewise, your onScanCompleted 
   (if you choose to include one) will get called once for each file in the list. 
   So really, while this is much better for a small number of files, if you plan 
   on scanning a very large amount then the full rescan above would probably be 
   a better option. */
MediaScannerConnection.scanFile(context, new String[]{ newFilePathAsString }, null, 
    new MediaScannerConnection.OnScanCompletedListener() { 
        public void onScanCompleted(String path, Uri uri) {
            //This executes when scanning is completed
        }
    }
);
于 2012-11-20T16:58:41.407 に答える