大きな画像をエラーなしで縮小する [Android]
スマホで撮影した12M(4000x3000)などの「大きなサイズ」の写真をOutOfMemoryException(メモリ不足)のエラーなしで縮小する方法です。
前提条件
今回は内部ストレージにあるファイルを扱うのでFileProviderクラスを使用しています。FileProviderの設定を行ってからコーディングとなります。
目次
1. 作るもの
2. 画面設計
3. FileProviderの設定
3-1. ストレージ権限とプロバイダを定義する
3-2. プロバイダで使用するpathを定義する
4. コーディング
1. 作るもの
内部ストレージの最階層にあるtest.jpgの「画像サイズのみ」を読み込んだ後に、スケールを設定して画像を縮小します。その後にアクションバーに縮小後のサイズを表示して、test.pngファイルを生成します。
2. 画面設計
Buttonを1つだけ設置します。
3. FileProviderの設定
3-1. ストレージ権限とプロバイダを定義する
manifestsフォルダの「AndroidManifest.xml」にストレージ権限(5-6行目)とプロバイダ(23-31行目)を追加します。
[AndroidManifest.xml]
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.petitmonte.android.myapplication"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/my_provider" /> </provider> </application> </manifest>
3-2 プロバイダで使用するpathを定義する
resフォルダの上で右クリックで[新規][ディレクトリ]でxmlフォルダを作成します。
xmlフォルダの上で右クリックで[新規][XMLリソースファイル]を選択して「my_provider.xml」を作成します。
[my_provider.xml]
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="share" path="." /> </paths>
3行目でpath="."として定義しているので、内部ストレージの最階層からサブフォルダも含む全てのファイルが使用できるようになります。
4. コーディング
内部ストレージの最階層にtest.jpgファイルを設置してください。
import android.Manifest; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Environment; import android.support.v4.app.ActivityCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ストレージの権限の確認 if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // ストレージの権限の許可を求めるダイアログを表示する ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1000); } findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 内部ストレージの最階層にあるtest.jpg(各自で用意してください) String openFileName = Environment.getExternalStorageDirectory() + "/"+ "test.jpg"; String saveFileName = Environment.getExternalStorageDirectory() + "/"+ "test.png"; try{ Uri uri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getApplicationContext().getPackageName() + ".provider", new File(openFileName)); // URIからストリームを取得する InputStream src = getContentResolver().openInputStream(uri); // オプションオブジェクトを生成 BitmapFactory.Options options = new BitmapFactory.Options(); // 画像をメモリに展開せずにサイズ情報のみ取得する options.inJustDecodeBounds = true; BitmapFactory.decodeStream(src,null,options); // スケールの計算 int scale = 1; // 1,2,4,8,16のみ 1=1/1,2=1/2,4=1/4... N=1/Nとなる int pixels = options.outWidth * options.outHeight; // 2M以上は1/2 if( pixels >= 2000000 ){ scale = 2; } // 4M以上は1/4 if( pixels >= 4000000 ){ scale = 4; } // 8M以上は1/8 if( pixels >= 8000000 ) { scale = 8; } // 16M以上は1/16 if( pixels >= 16000000 ) { scale = 16; } // 任意の縮尺でビットマップを生成する options.inJustDecodeBounds = false; options.inSampleSize = scale; InputStream src2 = getContentResolver().openInputStream(uri); Bitmap bmp = BitmapFactory.decodeStream(src2,null,options); // 生成した画像サイズをタイトルに表示する setTitle(String.valueOf(bmp.getWidth())+"x" + String.valueOf(bmp.getHeight())); // PNGファイルの生成 FileOutputStream AStream = new FileOutputStream(saveFileName,false); bmp.compress(Bitmap.CompressFormat.PNG, 80, AStream); AStream.close(); // AndroidのOSにファイルを認識させる MediaScannerConnection.scanFile(MainActivity.this, new String[]{saveFileName}, new String[]{"image/png"}, null); }catch(Exception e){ e.printStackTrace(); } } }); } }