PROGRAMMING
Kotlin
- 外部(共有)ストレージの Media ファイルへのアクセス
- 外部(共有)ストレージの Document ファイルへのアクセス
- 内部(App固有)ストレージへのアクセス
javascript
- ページャーを作る(簡易版)
- ページャーを作る 4/4
(クロスブラウザ対策)
(ページャー表示用メソッド)
(ページャーの使い方) - ページャーを作る 3/4
(イベント登録) - ページャーを作る 2/4
(スタイルの設定) - ページャーを作る 1/4
(ページャー用関数を準備)
(各要素を生成) - 自作カレンダーを作る 6/6
(11.~14.各種メソッド)
(15.簡単な使い方) - 自作カレンダーを作る 5/6
(09.祝休日の判定)
(10.閏年の判定) - 自作カレンダーを作る 4/6
(08.イベントの設定) - 自作カレンダーを作る 3/6
(07.スタイルの設定) - 自作カレンダーを作る 2/6
(05.カレンダーのマスのみ作成)
(06.各マスへ年月・日付を埋め込み) - 自作カレンダーを作る 1/6
(01.カレンダーの基を準備)
(02.各月の日数や年月日の区切り文字)
(03.前月・次月指定時の処理)
(04.前月・次月部分の年月を算出) - JS-Image-Resizerを使って画像を高画質で縮小
- ファイルを作成しローカルに保存
- モーダルウィンドウをクラス化
- ストップウォッチを作る
- XMLHttpRequest Lebel2を試してみる
- jQuery:Tableで親・子・兄弟要素を取得
- 文字列をセパレータ文字で分割し配列で返す関数
- forループのカウンタを使用している関数の定義
- クロージャの使いどころ
- JSONPでクロスドメイン
- AjaxでJSON形式のデータを扱う
- jsファイルからjsファイルを呼び出す
- jQuery:モーダルウィンドウを作ってみる
PHP
Java
- IntelliJ.Gradle.JavaFX(6) プラグイン org.beryx.jlink を使ってみる
- IntelliJ.Gradle.JavaFX(5) NSISでインストーラーを作る
- IntelliJ.Gradle.JavaFX(4) exewrapで実行可能jarをexe化する
- IntelliJ.Gradle.JavaFX(3) Jlink でカスタム JRE を作る
- IntelliJ.Gradle.JavaFX(2) すべての依存対象を含んだ Fatjar を作る
- IntelliJ.Gradle.JavaFX(1) アプリケーションを作る
- javaのコンパイルと実行
- イメージを回転する
- Exif情報を簡単に取得できるライブラリ
- 高画質で画像縮小
- JDBCによるデータベース操作
jetpack compose 外部(共有)ストレージの Media ファイルへのアクセス
androidで、端末内にファイルを保存したり読み込んだりする方法がよく分からなかったので整理してみました。
特に、内部ストレージ、外部ストレージ、アプリ固有のストレージ、共有ストレージ、メディア用途、その他の用途、
と種類が多く分かりずらいです。
また、Api のバージョンで permission の要・不要も変わります。
何回かに分けてストレージへのアクセス方法を確認してみます。
≪開発環境≫
windows11
andriod studio koala
≪実機デバッグ≫
android13
ストレージの構成
google のサイトなど色々調べ、大まかに分けてみたのが次の表です。
区分1 | 区分2 | 区分3 | アクセス |
内部ストレージ | アプリ固有のストレージ | 自分のみOK | |
外部ストレージ | アプリ固有のストレージ Android11以降作成不可 | 自分のみOK | |
共有ストレージ | メディア用途 | 他のアプリからもOK | |
メディア以外用途 | 他のアプリからもOK |
他のアプリで作られたファイル (LINEの画像など) にアクセスする以外は、パーミッションは不要のようです。
imageファイルへの書き込み
書き込み 方法1
// 外部ストレージ(共有ストレージ)に書き込み(Media) Direct ============================
val context = LocalContext.current
Button(
onClick = {
// 保存されるコンテンツURI(DCIM, Pictures ディレクトリ = MediaStoreのテーブル)
val collection = if (Build.VERSION.SDK_INT >= 29) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
// 保存されるファイルの情報
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "image2.jpg") // このファイル名で保存
put(MediaStore.Images.Media.MIME_TYPE, "image/jpg") // image/png
put(MediaStore.Images.Media.IS_PENDING, true)
// 保存場所に専用のフォルダを作成する場合
// put(
// MediaStore.Images.ImageColumns.RELATIVE_PATH,
// Environment.DIRECTORY_PICTURES + "/MY_PICTURES/")
}
// 登録されたファイルを指しているUriオブジェクト.このUriにデータwriteを行うことができる
val contentUri = contentResolver.insert(collection, contentValues)
// セットされたUriにファイルを書き込む
contentResolver.openFileDescriptor(contentUri!!, "w", null).use {
FileOutputStream(it!!.fileDescriptor).use { outputStream ->
val resources = context.resources
// コピー元の画像
val imageInputStream = resources.openRawResource(R.raw.image2)
while (true) {
val data = imageInputStream.read()
if (data == -1) {
break
}
outputStream.write(data)
}
imageInputStream.close()
outputStream.close()
}
}
contentValues.clear()
if (Build.VERSION.SDK_INT >= 29) {
contentResolver.update(contentUri, contentValues.apply {
put(MediaStore.Images.Media.IS_PENDING, false)
}, null, null)
} else {
contentResolver.update(contentUri, contentValues, null, null)
}
Toast.makeText(context, "ファイルが作成されました。", Toast.LENGTH_SHORT).show()
}
) {
Text(text = "共有(Media)書き込み/Direct")
}
書き込み 方法2
// 外部ストレージ(共有ストレージ)から読み込み(Media) Direct ==========================
// 他のアプリが作成した画像にはパーミッションが必要
val context = LocalContext.current
Button(
onClick = {
// DCIM, Pictures ディレクトリ MediaStoreのテーブル
val collection = if (Build.VERSION.SDK_INT >= 29) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
// 読み込む列の指定。nullならすべての列を読み込む
val projection = arrayOf(
MediaStore.Images.Media._ID, // URIそのものの取得に必要
MediaStore.Images.Media.DISPLAY_NAME, // 拡張子が付いたファイル名
MediaStore.Images.Media.SIZE // ファイルサイズ
)
// 行の絞り込みの指定.nullならすべての行を読み込む(where句)
val selection = "${MediaStore.Images.Media.DISPLAY_NAME} = ?"
// selectionの?を置き換える引数
val selectionArgs = arrayOf("image2.jpg") // ファイル名を指定しているの一つだけ取得
// 並び順。nullなら指定なし "${MediaStore.Images.Media.DATE_ADDED} DESC"
val sortOrder = null
contentResolver.query(
collection, projection, selection, selectionArgs, sortOrder
)?.use { cursor ->
// 必要な情報が格納されている列番号を取得する、0から始まるインデックスで返す
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
// 複数ファイル取得の場合。順にカーソルを動かしながら、情報を取得していく
// while (cursor.moveToNext()) {
// val id = cursor.getLong(idColumn)
// // IDからURIを取得して
// val uri = ContentUris.withAppendedId(collection, id)
// uri ごとに色々処理する
// uris.add(uri) // 処理例1 リストに格納
// uri.getBitmapOrNull(context.contentResolver)?.let { bitmap: Bitmap ->
// imageBitmap = bitmap // 処理例2 ただ上書きしてるだけ
// }
// }
// 単一ファイル取得の場合
cursor.moveToFirst()
val id = cursor.getLong(idColumn)
// IDからURIを取得
val uri2 = ContentUris.withAppendedId(collection, id)
// coil で表示するか bitmap にして表示する
uri2.getBitmapOrNull(context.contentResolver)?.let { bitmap: Bitmap ->
imageBitmap = bitmap
}
}
}
) {
Text(text = "共有(Media)読み込み/Direct")
}
imageファイルからの読み込み
読み込み 方法1
// 外部ストレージ(共有ストレージ)から読み込み(Media) Picker ===============
val context = LocalContext.current
var imageBitmap by remember { mutableStateOf<Bitmap?>(null) }
val openImgLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { imageUri: Uri? ->
imageUri?.getBitmapOrNull(context.contentResolver)?.let { bitmap: Bitmap ->
// 取得したBitmapのサイズを変更
// imageBitmap = Bitmap.createScaledBitmap(bitmap, 500, 500, true)
// サイズをそのまま
imageBitmap = bitmap
}
}
Button(
onClick = {
openImgLauncher.launch("image/*")
}
) {
Text(text = "共有(Media)読み込み/Picker")
}
読み込み 方法2
// 外部ストレージ(共有ストレージ)から読み込み(Media) Picker ===============
var uri by remember { mutableStateOf<Uri?>(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
uri = it
}
Button(onClick = {
launcher.launch("image/*")
}) {
Text("Picker coil")
}
// 画像表示
if (uri != null) {
Text("uri=${uri}")
Image(
painter = rememberImagePainter(uri), // coil で表示
contentDescription = null
)
}
まとめ
方法は何通りかありますが、どれを使うべきなのか爺ちゃんには良くわかりませんでした。