こんにちは、まっさん(@Tera_Msaki)です。
この記事はAndroidスマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Javaでの開発経験、XML構文規則、Androidのアプリ開発経験がある方を対象としています。
Androidのアプリ開発でお役にたててれば、嬉しいです。
(これからAndroidのアプリ開発やJavaでの開発を始めたい方への案内は、記事の最後で紹介します)
デジタルカメラで撮影した画像ファイルには、Exif という画像ファイルに関するメタデータが含まれています。
Andoroid アプリ開発で、画像ファイルから Bitmap を生成して、ImageView にセットした場合、画像の向きが正しく表示されない ことがあります。
画像の向きを正しく表示するためには、画像ファイルから Exif を取得して、正しい向きに変換する必要があります。
Exif
◎ポイント
Exif(Exchangeable image file format)は、日本電子工業振興協会 ( JEIDA )で規格化された写真用のメタデータを含む画像ファイルフォーマットです。
カメラの機種や撮影時の条件情報を画像に埋め込んで、ビューワやフォトレタッチなどで応用します。
画像データの向きに対応したファイルコピーでは、画像ファイルから、Exif を読み出し、画像データを向きに応じて、正しい向きに回転させて、ファイルコピーします。
◎依存関係の宣言
モジュールの build.gradle
ファイルに依存関係を追加します。
:
dependencies {
implementation 'androidx.exifinterface:exifinterface:1.3.5'
:
}
※パージョン1.3.5 は、2022年11月時点のものです
◎画像ファイルからExif情報を取得して、正しい向きに回転する
画像ファイルから画像データ(Bitmap)と Exif を取得します。
Matrix を使用して、Exif の orientation(方向)で Bitmap を変換(回転)します。
public void binaryFileCopy(Uri inFileUri, String outFile, int type) {
try (InputStream inputStream = context.getContentResolver().openInputStream(inFileUri);
ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(inFileUri, "r")) {
ExifInterface exifInterface = new ExifInterface(inputStream);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(parcelFileDescriptor.getFileDescriptor());
Bitmap bitmap2;
float resizeScale;
float width = Math.min(bitmap.getWidth(), 1920);
Matrix matrix = new Matrix();
matrix.reset();
switch(orientation) {
case 2: // ORIENTATION_FLIP_HORIZONTAL 左右反転
resizeScale = width / bitmap.getWidth();
matrix.postScale(resizeScale, -resizeScale);
matrix.postTranslate(0, bitmap.getHeight() * resizeScale);
break;
case 3: // ORIENTATION_ROTATE_180 180度回転
resizeScale = width / bitmap.getWidth();
matrix.postRotate(180, bitmap.getWidth() / 2f, bitmap.getHeight() / 2f);
matrix.postScale(resizeScale, resizeScale);
break;
case 4: // ORIENTATION_FLIP_VERTICAL 上下反転
resizeScale = width / bitmap.getWidth();
matrix.setScale(-resizeScale, resizeScale);
matrix.postTranslate(bitmap.getWidth() * resizeScale,0);
break;
case 5: // ORIENTATION_TRANSPOSE 270度回転+上下反転
resizeScale = width / bitmap.getHeight();
matrix.postRotate(270, 0, 0);
matrix.postScale(resizeScale, -resizeScale);
break;
case 6: // ORIENTATION_ROTATE_90 90度回転
resizeScale = width / bitmap.getHeight();
matrix.postRotate(90, 0, 0);
matrix.postScale(resizeScale, resizeScale);
matrix.postTranslate(bitmap.getHeight() * resizeScale, 0);
break;
case 7: // ORIENTATION_TRANSVERSE 90度回転+90度反転
resizeScale = width / bitmap.getHeight();
matrix.postRotate(90, 0, 0);
matrix.postScale(resizeScale, -resizeScale);
matrix.postTranslate(bitmap.getHeight() * resizeScale, bitmap.getWidth() * resizeScale);
break;
case 8: // ORIENTATION_ROTATE_270 270度回転
resizeScale = width / bitmap.getHeight();
matrix.postRotate(270, 0, 0);
matrix.postScale(resizeScale, resizeScale);
matrix.postTranslate(0, bitmap.getWidth() * resizeScale);
break;
default: // ORIENTATION_NORMAL 変更不要
resizeScale = width / bitmap.getWidth();
matrix.preScale(resizeScale, resizeScale);
}
bitmap2 = Bitmap.createBitmap(bitmap, 0, 0,bitmap.getWidth(),bitmap.getHeight(), matrix, true);
setFile(outFile, type);
writeFileBitmap(bitmap2);
} catch (IOException e) {
Log.d(TAG, Objects.requireNonNull(e.getMessage()));
e.printStackTrace();
}
}
ファイルコピー関数では、Intent 使用して指定した画像ファイルを Uri、出力ファイルは、ファイル名と出力先区分を引数としています。
①画像ファイルの Exif を取得して、Bitmap に展開
②展開した Bitmap を Matrix を使用して正しき向きに変換して、ファイル出力
※画像ファイルのサイズが大きいため、HDサイズに縮小しています
◎Bitmapをファイル出力
private final String[] type = new String[] {
Environment.DIRECTORY_DOCUMENTS,
Environment.DIRECTORY_DOWNLOADS,
Environment.DIRECTORY_PICTURES
};
private final File[] path = new File[type.length];
private File file;
private final Context context;
:
//コンストラクタ
public ExternalStorageWriter(Context context) {
this.context = context;
setContext(context);
for (int i=0; i< type.length; i++) {
path[i] = context.getExternalFilesDir(type[i]);
}
propertiesFile = new File(context.getFilesDir(),String.format("%s.properties", context.getString(R.string.app_name)));
}
//ファイル指定
public boolean setFile(String fileName, int type) {
file = new File(path[type], fileName);
return file.exists();
}
:
//ファイル書込(bitmap)
public void writeFileBitmap(Bitmap bitmap) {
if (isExternalStorageWritable()) {
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
} catch (Exception e) {
Log.d(TAG, Objects.requireNonNull(e.getMessage()));
e.printStackTrace();
}
}
}
スマホ本体の外部ストレージをハンドリングする ExternalStorageWriter クラスです。
コンストラクタでファイルの出力先となる外部ストレージのパス(File)を、getExternalFilesDir を使用して設定しています。
ファイル名と出力先区分で出力ファイルを指定して、Bitmap を指定した出力ファイルにJPEG方式で圧縮して出力しています。
◎Intent使用して画像ファイルを指定する実装
Intent 使用して画像ファイルを指定する場合、activityResultLauncher を使用して、指定した画像ファイルの Uri を受け取ります。
public String BACK_GROUND = "stocker.jpeg";
private ImageView imageView;
public ActionMenuView actionMenuView;
:
private final ActivityResultLauncher<Intent> activityResultLauncher= registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
if (result.getData() != null) {
switch (itemId) {
:
case 6: // 背景
new ExternalStorageWriter(context).binaryFileCopy(result.getData().getData(), BACK_GROUND, 2);
ExternalStorageReader externalStorageReader = new ExternalStorageReader(context);
externalStorageReader.setFile(BACK_GROUND,2);
imageView.setImageBitmap(externalStorageReader.ReadFileBitmap());
break;
default:
}
}
}
});
:
private void createView() {
// アクションメニュー
actionMenuView = findViewById(R.id.menu);
:
actionMenuView.getMenu().add(Menu.NONE, 6, Menu.NONE, context.getString(R.string.menu_Background));
:
actionMenuView.setOnMenuItemClickListener(menuItem -> {
itemId = menuItem.getItemId();
Intent intent;
switch (itemId) {
:
case 6:
// 背景設定
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
activityResultLauncher.launch(intent);
break;
:
}
return false;
});
今回は、ここまでです。
参考 : Exifinterface
誤字脱字、意味不明でわかりづらい、
もっと詳しく知りたいなどのご意見は、
このページの最後にあるコメントか、
こちらから、お願いいたします♪
ポチッとして頂けると、
次のコンテンツを作成する励みになります♪
これからAndroidのアプリ開発やJavaでの開発を始めたい方へ
アプリケーション開発経験がない方や、アプリケーション開発経験がある方でも、Java や C# などのオブジェクト指向言語が初めての方は、Android のアプリ開発ができるようになるには、かなりの時間がかかります。オンラインスクールでの習得を、強くおススメします。
副業でアプリケーション開発と考えているなら、おススメです。
無料説明会に参加して、話を聞くだけでもためになるよ♪
未経験者からシステムエンジニアを目指すのに最適かと、
まずは無料相談から♪
未経験者からプログラマーを目指すのに最適かと、
まずは無料カウンセリングから♪
カリキュラムとサポートがしっかりしています、
お得なキャンペーンとかいろいろやっています♪
ゲーム系に強いスクール、UnityやUnrealEngineを習得するのに最適かと、
まずは無料オンライン相談から♪
参考になったら、💛をポッチとしてね♪
コメント欄