16

キャンバスに描画したい 5000 x 4000 px の画像があります。

まず、リソースからロードしようとしました。入れました/res/drawable

次の方法を使用しました。

InputStream input = getResources().openRawResource(R.drawable.huge_image);
Drawable d = Drawable.createFromStream(input, "image");
d.setBounds(...);
d.draw(canvas);

それは魅力のように機能しました。

この場合、InputStreamAssetManager.AssetInputStreamです。

だから今、私はSDカードからそれをロードしたい.

これが私がやろうとしたことです:

File f = new File(path);
Uri uri = Uri.fromFile(f);
InputStream input = mContext.getContentResolver().openInputStream(uri);
Drawable d = Drawable.createFromStream(input, "image");

この場合、InputStreamは であり、 の作成時FileInputStreamに を取得しました。OutOfMemoryErrorDrawable

だから私は疑問に思っています:

そのエラーを発生させずに画像をロードする方法はありますか? または、 を に変換する方法はありFileInputStreamますAssetInputStreamか?

ノート:

ズーム/パン機能を実装しているため、画像のサイズを変更したくありません。Loading Large Bitmaps Efficientlyを読むように言わないでください。

ここでクラス全体を確認できます。を使用するとエラーが発生しsetImageUri()ます。

ここに私のエラーログがあります:

08-13 11:57:54.180: E/AndroidRuntime(23763): FATAL EXCEPTION: main
08-13 11:57:54.180: E/AndroidRuntime(23763): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:468)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:332)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.benitobertoli.largeimagezoom.ZoomImageView.setDrawablefromUri(ZoomImageView.java:187)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.benitobertoli.largeimagezoom.ZoomImageView.setImageUri(ZoomImageView.java:588)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.benitobertoli.largeimagezoom.TestActivity.onKeyDown(TestActivity.java:30)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.KeyEvent.dispatch(KeyEvent.java:1257)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.app.Activity.dispatchKeyEvent(Activity.java:2075)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1673)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.ViewRoot.deliverKeyEventToViewHierarchy(ViewRoot.java:2493)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2463)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1752)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.os.Handler.dispatchMessage(Handler.java:99)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.os.Looper.loop(Looper.java:144)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at android.app.ActivityThread.main(ActivityThread.java:4937)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at java.lang.reflect.Method.invokeNative(Native Method)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at java.lang.reflect.Method.invoke(Method.java:521)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
08-13 11:57:54.180: E/AndroidRuntime(23763):    at dalvik.system.NativeStart.main(Native Method)

編集:

HTC Desire A8181 でコードをテストしていました。最初のコード スニペットが他のデバイスで機能しないと言われた後、Samsung Galaxy S2 とエミュレーターでテストしました。

結果: リソースからロードするとき、エミュレーターは を返しましOutOfMemoryErrorた。Galaxy S2 は例外をスローしませんでしたが、返さDrawableれたのは null でした。

したがって、当面の唯一の解決策は、画像をダウンサンプリングすることだと思います。

4

4 に答える 4

13

を見てみましょう:

http://developer.android.com/reference/android/graphics/BitmapFactory.html

decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts)

また

decodeFile (String pathName, BitmapFactory.Options opts)

このようにして、ビットマップ全体をロードして画面に表示できます。

正しいオプションを設定する必要があります。

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

inSampleSize

私はあなたのコードでそれを試しました:

 BitmapFactory.Options options = new BitmapFactory.Options();

 options.inSampleSize = 4;

 Bitmap myBitmap = BitmapFactory.decodeFile(mUri.getPath(),options);
 Drawable d = new BitmapDrawable(Resources.getSystem(),myBitmap);
 updateDrawable(d);

私のために働きます。

于 2012-08-14T09:46:01.237 に答える
3

私の意見では、画像を小さな断片に分割するのが最善の方法です。これにより、アプリケーションで OutOfMemoryException が発生するのを防ぐことができます。ここに私のコード例があります

最初にSDカードから画像を取得し、ビットマップに入れます

Bitmap b = null;
        try {
            File sd = new File(Environment.getExternalStorageDirectory() + link_to_image.jpg);
            FileInputStream fis;

            fis = new FileInputStream(sd);

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPurgeable = true;
            options.inScaled = false;

            b = BitmapFactory.decodeStream(fis, null, options);
            fis.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

ビットマップ (b) を小さな断片に分割します。この場合、SPLIT_HEIGHT は 400 です。

double rest = (image_height + SPLIT_HEIGHT);
        int i = 0;
        Bitmap[] imgs = new Bitmap[50];

        do {
            rest -= SPLIT_HEIGHT;
            if (rest < 0) {
                // Do nothing
            } else if (rest < SPLIT_HEIGHT) {
                imgs[i] = Bitmap.createBitmap(b, 0, (i * SPLIT_HEIGHT), image_width, (int) rest);
            } else {
                imgs[i] = Bitmap.createBitmap(b, 0, i * SPLIT_HEIGHT, image_width, SPLIT_HEIGHT);
            }

            i++;

        } while (rest > SPLIT_HEIGHT);

これで、小さな画像のコレクションができました。完全にやりたい場合は、このビットマップをリサイクルできます。

// Not needed anymore, free up some memory
        if (b != null)
            b.recycle();

この時点から、小さな画像をキャンバスに配置できます。次のコードでは、画像を ScrollView に入れます。Canvas に画像を配置するためのコードは、あまり変わらないと思います。

// Add images to scrollview
        for (int j = 0; j < imgs.length; j++) {
            Bitmap tmp = imgs[j];
            if (tmp != null) {
                // Add to scrollview
                ImageView iv = new ImageView(activity);

                String id = j + "" + articlenumber;
                iv.setId(Integer.parseInt(id));
                iv.setTag(i);

                iv.setImageBitmap(tmp);

                RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(globalwidth, tmp.getHeight());
                lp.addRule(RelativeLayout.BELOW, previousLongPageImageId);
                lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);

                container.addView(iv, lp);
                previousLongPageImageId = iv.getId();
            }
        }

これがお役に立てば幸いです。

注: 画像の最大解像度は 2048*2048 (OpenGL の制限など) であるため、常に画像を小さな断片に分割する必要があります。

于 2012-08-14T07:22:59.350 に答える
0

これが正しい方法かどうかはわかりませんが、それでも問題を解決できます。

画像を Web ビューで開いてみてください。これにより、組み込みのズームイン/アウトおよびパン機能が提供され、入力ストリームやその他のものを開くことを心配する必要がなくなります。

アセットから webview で画像を開くには:

    WebView  wv = (WebView) findViewById(R.id.webView1);  
    wv.loadUrl("file:///android_asset/abc.png");  

ズーム コントロールを許可するためのWebSettingsを使用できます。

これがあなたを助けることを願っています!!

于 2012-08-13T09:52:21.317 に答える
0

リソースからロードしたときは機能していたのに、手動でロードしたときに機能しない場合は、リソース管理に問題があり、おそらくメモリ リークが発生していると思われます。

アプリケーションの開始時に常に発生しますかOutOfMemoryError、それとも後で発生しますか? 構成変更中ですか?静的フィールドを使用していますか (誤って使用すると、Android アプリケーションでメモリ リークが発生することがよくあります。説明付きの例は、あなたのケースに当てはまります)?
いくつかのプロファイリング ツールを使用してみてください ( Googleは何か役立つものを見つけるのに役立ちます)。

于 2012-08-14T07:41:05.540 に答える