こんにちは、まっさん(@Tera_Msaki)です。
この記事はAndroidスマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Javaでの開発経験、XML構文規則、Androidのアプリ開発経験がある方を対象としています。
Androidのアプリ開発でお役にたててれば、嬉しいです。
(これからAndroidのアプリ開発やJavaでの開発を始めたい方への案内は、記事の最後で紹介します)
◎テーマ
Androidスマホのアプリ開発でカメラで撮影した画像ファイルの向きを考慮してファイルコピーする。
◎ポイント
カメラで撮影した画像ファイルには、Exifという画像ファイルに関するメタデータが含まれています。
Andoroidスマホのアプリ開発で、画像ファイルからBitmapを生成して、ImageViewにセットした場合、画像の向きが正しく表示されないことがあります。
画像の向きを正しく表示するためには、画像ファイルからExif情報を取得して、正しい向きに変換する必要があります。
◎依存関係の宣言
モジュールの build.gradle
ファイルに依存関係を追加します。
:
dependencies {
implementation 'androidx.exifinterface:exifinterface:1.3.3'
:
}
◎画像ファイルからExif情報を取得して、正しい向きに変換
画像ファイルからExif情報を取得して、Bitmapを正しい向きに変換します。
public void binaryFileCopy(Uri inFileUri, String outFile, int type) {
try (InputStream inputStream = context.getContentResolver().openInputStream(inFileUri)) {
ExifInterface exifInterface = new ExifInterface(inputStream);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(context.getContentResolver().openFileDescriptor(inFileUri, "r").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をExif情報に応じて、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を使用して設定しています。
出力ファイルは、ファイル名と出力先区分でファイル指定(File)して、Bitmapをファイル指定先に出力しています。
◎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;
});

◎これからAndroidのアプリ開発やJavaでの開発を始めたい方へ
初めてのAndroidのアプリ開発では、アプリケーション開発経験がない方や、
アプリケーション開発経験がある方でも、JavaやC#などのオブジェクト指向言語が初めての方は、
書籍などによる独学ではアプリ開発できるようになるには、
かなりの時間がかかりますので、オンラインスクールでの習得をおススメします。



コメント