java - animaciones - paint android studio
Cargando imágenes grandes sin OutOfMemoryError (4)
Echa un vistazo a:
http://developer.android.com/reference/android/graphics/BitmapFactory.html
decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts)
o
decodeFile (String pathName, BitmapFactory.Options opts)
De esta manera puede cargar todo su mapa de bits y mostrarlo en pantalla.
Necesitas configurar las opciones correctas.
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
inSampleSize
Lo probé con tu código:
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);
funciona para mi.
Tengo una imagen de 5000 x 4000 px que quiero dibujar en un lienzo.
Primero intenté cargarlo desde recursos. Lo puse en /res/drawable
.
Utilicé el siguiente método:
InputStream input = getResources().openRawResource(R.drawable.huge_image);
Drawable d = Drawable.createFromStream(input, "image");
d.setBounds(...);
d.draw(canvas);
Funcionó a las mil maravillas.
En este caso, el InputStream
es un AssetManager.AssetInputStream
.
Así que ahora quiero cargarlo desde el sdcard.
Esto es lo que traté de hacer:
File f = new File(path);
Uri uri = Uri.fromFile(f);
InputStream input = mContext.getContentResolver().openInputStream(uri);
Drawable d = Drawable.createFromStream(input, "image");
En este caso, el InputStream
es un FileInputStream
y obtuve un OutOfMemoryError
al crear el Drawable
.
Así que me pregunto:
¿Hay una manera de cargar la imagen sin obtener ese error? ¿O hay una manera de convertir un FileInputStream
a un AssetInputStream
?
Nota:
No quiero cambiar el tamaño de la imagen porque estoy implementando la funcionalidad zoom / pan. Por favor, no me digas que lea Loading Large Bitmaps Efficiently .
Puedes ver la clase completa here . El error ocurre cuando se usa setImageUri()
.
Aquí está mi registro de errores:
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)
EDITAR:
Estaba probando mi código en un HTC Desire A8181. Después de que me dijeron que el primer fragmento de código no funcionaba en otros dispositivos, lo probé en un Samsung Galaxy S2 y en el Emulador.
Resultados: al cargar desde recursos, el emulador dio un OutOfMemoryError
, el Galaxy S2 no lanzó una excepción, pero el Drawable
devuelto era nulo.
Así que supongo que por el momento la única solución es reducir la muestra de la imagen.
En mi opinión, la mejor opción es dividir la imagen en partes más pequeñas. Esto evitará que la aplicación dé una excepción OutOfMemoryException. Aquí está mi código de ejemplo
Primero obtenga la imagen de la tarjeta SD y póngala en un mapa de bits
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();
}
Divide el mapa de bits (b) en partes más pequeñas. En este caso, SPLIT_HEIGHT es 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);
Ahora tienes una colección de imágenes más pequeñas. Si quieres hacerlo perfectamente, puedes reciclar este mapa de bits:
// Not needed anymore, free up some memory
if (b != null)
b.recycle();
Desde este punto puedes poner las imágenes más pequeñas en el lienzo. En mi siguiente código coloco las imágenes en un ScrollView. El código para colocar las imágenes en un lienzo no es muy diferente.
// 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();
}
}
Espero que esto ayude.
NOTA: La resolución máxima para las imágenes es 2048 * 2048 (restricciones OpenGL o algo así), por lo que siempre tendrá que dividir las imágenes en partes más pequeñas.
No estoy seguro de si es la forma correcta de hacer esto pero aún así esto puede resolver su problema.
Intenta abrir tu imagen en una vista web. Esto le proporcionará una función integrada de zoom in / out y paneo y no tendrá que preocuparse por abrir ningún flujo de entrada o cualquier otra cosa.
Para abrir una imagen en webview desde activos:
WebView wv = (WebView) findViewById(R.id.webView1);
wv.loadUrl("file:///android_asset/abc.png");
Puede realizar WebSettings para permitir controles de zoom.
¡¡Espero que esto te ayudará!!
Si funcionó cuando cargó desde recursos y ahora no funciona cuando lo carga manualmente, entonces sospecho que tiene algún problema con la administración de recursos, probablemente una pérdida de memoria.
¿ OutOfMemoryError
siempre OutOfMemoryError
al inicio de la aplicación o sucede más tarde? ¿Está en cambio de configuración? ¿Usa campos estáticos (si se usa incorrectamente, esto a menudo conduce a pérdidas de memoria en la aplicación de Android, ejemplo con una explicación que se ajusta a su caso)?
Intente usar algunas herramientas de creación de perfiles ( google puede ayudar a encontrar algo útil).