読者です 読者をやめる 読者になる 読者になる

2hours

1日2時間でなにができるかな

標準トリミング機能の呼び出し

Android

大きい画像だとうまく動かない


トリミング機能をintentで呼び出そうと思い「android トリミング intent」でググったら大体以下のようなサンプルが出てくる。

Intent intent = new Intent("com.android.camera.action.CROP");
intent.setData(uri);              // トリミングに渡す画像パス
intent.putExtra("outputX", 200);        // トリミング後の画像の幅
intent.putExtra("outputY", 200);        // トリミング後の画像の高さ
intent.putExtra("aspectX", 1);         // トリミング後の画像のアスペクト比(X)
intent.putExtra("aspectY", 1);         // トリミング後の画像のアスペクト比(Y)
intent.putExtra("scale", true);         // トリミング中の枠を拡大縮小させるか
intent.putExtra("return-data", true);      // トリミングしたデータを返すよ
startActivityForResult(intent, REQUEST_CROP_PICK);

これでトリミング機能を呼び出すことが出来て、以下のように返却されたデータを受け取れる。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case REQUEST_GET_CONTENT:
        break;
    case REQUEST_CROP_PICK:
        Bitmap bitmap = data.getExtras().getParcelable("data");
    }
}


これで動くには動く。
ただ問題があって大きい画像を扱うと何も返ってこなくなる・・・。
例えば壁紙用にoutpuX、outputYに480、800とか指定すると動かない。



調べてみた

でも壁紙設定アプリでは出来てるわけだから何か方法があるはず。
ってことで調べてみた。
壁紙設定アプリではこのパッケージを呼び出してたので、そのソースを見れば解決する(はず)。

com.cooliris.media.CropImage

調べた結果

カメラ扱う時も同じ(だったと思う)だけど、intentに大きい画像渡すと動かない。
ので、トリミング機能に「データを返せ」ではなく「データを保存してくれ」と指示して解決。

トリミングはOSのバージョンによって呼ぶものが変わるっぽい。
その部分のソースはどこかからコピペしたんですが、引用元がどこだか分からなくなった・・・。
ただGalaxyNexusでは動かなかったので、パッケージを追加してあります。

サンプル

下にギャラリーで画像を選択して、トリミングした画像をSDカードに保存した後に画面に表示するサンプルを(エラーハンドリングとか適当)。

package goodspeed.jp.ne.hatena.d.cropimage;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.widget.ImageView;

public class ImageCropSampleActivity extends Activity {
    
    private static final int PICK_PICUTER = 0;
    private static final int CROP_IMAGE = 1;
    
    private File mFile;
    private ImageView mImageView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mImageView = (ImageView)findViewById(R.id.image);
        
        // ギャラリー呼び出し
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_PICK);
        startActivityForResult(intent, PICK_PICUTER);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        
        if (requestCode == PICK_PICUTER && resultCode == RESULT_OK) {
                
            // ギャラリーで選択されたファイルパス
            Uri uri = data.getData();

            // トリミングがいくつかあるようなので、使えるものを使う
            PackageManager pm = this.getPackageManager();
            List<ApplicationInfo> list = pm.getInstalledApplications(0);
            String[] apps = {"com.android.gallery", "com.cooliris.media", "com.google.android.gallery3d"};
            String[] clss = {"com.android.camera.CropImage", "com.cooliris.media.CropImage", "com.android.gallery3d.app.CropImage"};
            
            int classtype = -1;
            for (ApplicationInfo ai : list) {
                String s1 = ai.packageName;
                if (apps[0].equals(s1)) {
                    classtype = 0;
                }
                if (apps[1].equals(s1)) {
                    classtype = 1;
                }
                if (apps[2].equals(s1)) {
                    classtype = 2;
                }
            }
            
            // トリミング呼び出し
            Intent intent = new Intent();
            if (classtype >= 0) {
                // 適当に保存するところ作る
                Date date = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                String filename = sdf.format(date)+".png";
                String dirPath = Environment.getExternalStorageDirectory().getPath()+"/"+getString(R.string.app_name);
                
                File dir = new File(dirPath);
                if(!dir.exists()){
                    if(!dir.mkdirs()){
                        // ディレクトリ作れなかった
                    }
                }

                // 先にファイルを用意しておいて、トリミングさんにそこに保存してくれるように頼む
                mFile = new File(dirPath+"/"+filename);
                intent.setClassName(apps[classtype], clss[classtype]);
                intent.setData(uri);
                intent.putExtra("outputX", 800);
                intent.putExtra("outputY", 800);
                intent.putExtra("aspectX", 1);
                intent.putExtra("aspectY", 1);
                intent.putExtra("scale", true);
                intent.putExtra("noFaceDetection", true);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mFile));
                intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.name());
                
                startActivityForResult(intent, CROP_IMAGE);
            } else {
                // トリミング機能呼び出し失敗
            }
        }
        
        if (requestCode == CROP_IMAGE && resultCode == RESULT_OK){
            // トリミングした画像を表示
            InputStream inputStream;
            try {
                inputStream = new FileInputStream(mFile);
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                mImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                // ファイルがなかった
            }
            
        }
    }
}