我有一个5000 x 4000像素的图像,我想画在画布上。
首先我尝试从资源中加载它。
我把它放在/res/drawable
。
我使用了以下方法:
InputStream input = getResources().openRawResource(R.drawable.huge_image);
Drawable d = Drawable.createFromStream(input, "image");
d.setBounds(...);
d.draw(canvas);
它就像一个魅力。
在这种情况下,InputStream
是AssetManager.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
,我在创建OutOfMemoryError
时获得Drawable
。
所以我想知道:
有没有办法加载图像而不会出现错误?或者有没有办法将FileInputStream
转换为AssetInputStream
?
注意:
我不想调整图像大小,因为我正在实现缩放/平移功能。 请不要告诉我阅读Loading Large Bitmaps Efficiently。
您可以查看完整的课程here。使用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上测试我的代码。 在被告知第一个代码片段无法在其他设备上运行之后,我在三星Galaxy S2和仿真器上进行了测试。
结果:
从资源加载时,模拟器提供了OutOfMemoryError
,Galaxy S2没有抛出异常,但返回的Drawable
为空。
所以我想暂时唯一的解决办法是对图像进行缩减采样。
答案 0 :(得分: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);
适合我。
答案 1 :(得分: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限制或其他),因此您必须始终将图像拆分为较小的部分。
答案 2 :(得分:0)
我不确定这样做是否正确,但这仍然可以解决您的问题。
尝试在网页浏览中打开图片。这将为您提供内置的放大/缩小和平移功能,您不必担心打开任何输入流或其他任何内容。
从资产中打开webview中的图像:
WebView wv = (WebView) findViewById(R.id.webView1);
wv.loadUrl("file:///android_asset/abc.png");
您可以WebSettings允许缩放控件。
希望这会对你有所帮助!!
答案 3 :(得分:0)
如果从资源加载时它起作用,现在当你手动加载时它不起作用,那么我怀疑你在资源管理方面有一些问题,可能是内存泄漏。
OutOfMemoryError
是否始终在应用程序启动时发生,或者在以后发生?它是否在配置更改?您是否使用静态字段(如果使用不当,这通常会导致Android应用程序中的内存泄漏,example with explanation这适合您的情况)?
尝试使用一些分析工具(google可以帮助找到有用的东西)。