これは新しいAndroid Q Scoped Storage へのリンクです。
this Android Developers Best Practices Blog によると、_storing shared media files
_(私の場合)は MediaStore APIを使用して実行する必要があります。
ドキュメントを掘り下げて、関連する機能が見つかりません。
ここにコトリンでの私の裁判があります:
_val bitmap = getImageBitmap() // I have a bitmap from a function or callback or whatever
val name = "example.png" // I have a name
val picturesDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
// Make sure the directory "Android/data/com.mypackage.etc/files/Pictures" exists
if (!picturesDirectory.exists()) {
picturesDirectory.mkdirs()
}
try {
val out = FileOutputStream(File(picturesDirectory, name))
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
out.flush()
out.close()
} catch(e: Exception) {
// handle the error
}
_
その結果、ベストプラクティスブログで_Android/data/com.mypackage.etc/files/Pictures/example.png
_として説明されているように、私の画像はここに_Storing app-internal files
_として保存されます。
私の質問は:
MediaStore APIを使用して画像を保存する方法
Javaの回答も同様に許容されます。
前もって感謝します!
[〜#〜]編集[〜#〜]
PerracoLabsに感謝
それは本当に役に立ちました!
ただし、さらに3つのポイントがあります。
これが私のコードです:
_val name = "Myimage"
val relativeLocation = Environment.DIRECTORY_PICTURES + File.pathSeparator + "AppName"
val contentValues = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
// without this part causes "Failed to create new MediaStore record" exception to be invoked (uri is null below)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
}
}
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
var stream: OutputStream? = null
var uri: Uri? = null
try {
uri = contentResolver.insert(contentUri, contentValues)
if (uri == null)
{
throw IOException("Failed to create new MediaStore record.")
}
stream = contentResolver.openOutputStream(uri)
if (stream == null)
{
throw IOException("Failed to get output stream.")
}
if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream))
{
throw IOException("Failed to save bitmap.")
}
Snackbar.make(mCoordinator, R.string.image_saved_success, Snackbar.LENGTH_INDEFINITE).setAction("Open") {
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_VIEW
intent.data = contentUri
startActivity(Intent.createChooser(intent, "Select Gallery App"))
}.show()
} catch(e: IOException) {
if (uri != null)
{
contentResolver.delete(uri, null, null)
}
throw IOException(e)
}
finally {
stream?.close()
}
_
1-保存された画像は正しい名前「Myimage.png」を取得しません
「Myimage」と「Myimage.PNG」を使用してみましたが、どちらも機能しませんでした。
画像には常に次のような数字で構成される名前が付けられます。
_1563468625314.jpg
_
これにより、2番目の問題が発生します。
2-ビットマップをjpg
の形式で圧縮していますが、画像はpng
として保存されます。
大きな問題ではありません。なぜ気になるのか。
3- relativeLocationビットにより、Android=未満のデバイスで例外が発生します。Q。「Androidバージョンチェック」のifステートメントで囲んだ後、画像はPictures
フォルダ。
よろしくお願いします。
EDIT 2
変更:
_uri = contentResolver.insert(contentUri, contentValues)
if (uri == null)
{
throw IOException("Failed to create new MediaStore record.")
}
val cursor = contentResolver.query(uri, null, null, null, null)
DatabaseUtils.dumpCursor(cursor)
cursor!!.close()
stream = contentResolver.openOutputStream(uri)
_
ここにログがあります
_I/System.out: >>>>> Dumping cursor Android.content.ContentResolver$CursorWrapperInner@76da9d1
I/System.out: 0 {
I/System.out: _id=25417
I/System.out: _data=/storage/emulated/0/Pictures/1563640732667.jpg
I/System.out: _size=null
I/System.out: _display_name=Myimage
I/System.out: mime_type=image/png
I/System.out: title=1563640732667
I/System.out: date_added=1563640732
I/System.out: is_hdr=null
I/System.out: date_modified=null
I/System.out: description=null
I/System.out: picasa_id=null
I/System.out: isprivate=null
I/System.out: latitude=null
I/System.out: longitude=null
I/System.out: datetaken=null
I/System.out: orientation=null
I/System.out: mini_thumb_magic=null
I/System.out: bucket_id=-1617409521
I/System.out: bucket_display_name=Pictures
I/System.out: width=null
I/System.out: height=null
I/System.out: is_hw_privacy=null
I/System.out: hw_voice_offset=null
I/System.out: is_hw_favorite=null
I/System.out: hw_image_refocus=null
I/System.out: album_sort_index=null
I/System.out: bucket_display_name_alias=null
I/System.out: is_hw_burst=0
I/System.out: hw_rectify_offset=null
I/System.out: special_file_type=0
I/System.out: special_file_offset=null
I/System.out: cam_perception=null
I/System.out: cam_exif_flag=null
I/System.out: }
I/System.out: <<<<<
_
title
が名前と一致していることに気付いたので、追加してみました。
put(MediaStore.Images.ImageColumns.TITLE, name)
それでも機能しませんでした。新しいログは次のとおりです。
_I/System.out: >>>>> Dumping cursor Android.content.ContentResolver$CursorWrapperInner@51021a5
I/System.out: 0 {
I/System.out: _id=25418
I/System.out: _data=/storage/emulated/0/Pictures/1563640934803.jpg
I/System.out: _size=null
I/System.out: _display_name=Myimage
I/System.out: mime_type=image/png
I/System.out: title=Myimage
I/System.out: date_added=1563640934
I/System.out: is_hdr=null
I/System.out: date_modified=null
I/System.out: description=null
I/System.out: picasa_id=null
I/System.out: isprivate=null
I/System.out: latitude=null
I/System.out: longitude=null
I/System.out: datetaken=null
I/System.out: orientation=null
I/System.out: mini_thumb_magic=null
I/System.out: bucket_id=-1617409521
I/System.out: bucket_display_name=Pictures
I/System.out: width=null
I/System.out: height=null
I/System.out: is_hw_privacy=null
I/System.out: hw_voice_offset=null
I/System.out: is_hw_favorite=null
I/System.out: hw_image_refocus=null
I/System.out: album_sort_index=null
I/System.out: bucket_display_name_alias=null
I/System.out: is_hw_burst=0
I/System.out: hw_rectify_offset=null
I/System.out: special_file_type=0
I/System.out: special_file_offset=null
I/System.out: cam_perception=null
I/System.out: cam_exif_flag=null
I/System.out: }
I/System.out: <<<<<
_
また、_date_added
_を名前に変更することはできません。
また、_MediaStore.MediaColumns.DATA
_は非推奨です。
前もって感謝します!
次の方法を試してください。 Android Q:フォルダーが存在しない場合、Qは既にフォルダーを作成します。この例は、Picturesフォルダーに出力するようにハードコードされています。サブフォルダーが必要な場合は、サブ次のフォルダ名:
final String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator + “YourSubforderName”;
圧縮形式は、mime-typeパラメータに関連付ける必要があることを考慮してください。たとえば、JPEG圧縮形式の場合、mime-typeは「image/jpeg」になります。
private void saveBitmap(@NonNull final Context context, @NonNull final Bitmap bitmap,
@NonNull final Bitmap.CompressFormat format, @NonNull final String mimeType,
@NonNull final String displayName) throws IOException
{
final String relativeLocation = Environment.DIRECTORY_PICTURES;
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
final ContentResolver resolver = context.getContentResolver();
OutputStream stream = null;
Uri uri = null;
try
{
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, contentValues);
if (uri == null)
{
throw new IOException("Failed to create new MediaStore record.");
}
stream = resolver.openOutputStream(uri);
if (stream == null)
{
throw new IOException("Failed to get output stream.");
}
if (bitmap.compress(format, 95, stream) == false)
{
throw new IOException("Failed to save bitmap.");
}
}
catch (IOException e)
{
if (uri != null)
{
// Don't leave an Orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
throw e;
}
finally
{
if (stream != null)
{
stream.close();
}
}
}
private void saveImage(Bitmap bitmap, @NonNull String name) throws IOException {
OutputStream fos;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name + ".jpg");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
} else {
String imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
File image = new File(imagesDir, name + ".jpg");
fos = new FileOutputStream(image);
}
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
Objects.requireNonNull(fos).close();
}
画像はルートレベルのピクチャフォルダに保存されます