大きな画像をエラーなしで縮小する [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();
                }
            }
        });
    }
}
関連記事
プチモンテ ※この記事を書いた人
|  | |
|  | 💻 ITスキル・経験 サーバー構築からWebアプリケーション開発。IoTをはじめとする電子工作、ロボット、人工知能やスマホ/OSアプリまで分野問わず経験。 画像処理/音声処理/アニメーション、3Dゲーム、会計ソフト、PDF作成/編集、逆アセンブラ、EXE/DLLファイルの書き換えなどのアプリを公開。詳しくは自己紹介へ | 
| 🎵 音楽制作 BGMは楽器(音源)さえあれば、何でも制作可能。歌モノは主にロック、バラード、ポップスを制作。歌詞は抒情詩、抒情的な楽曲が多い。楽曲制作は🔰2023年12月中旬 ~ | |

 
        



 
  
 


