Android ImageView.setMatrix () y .invalidate () – el repintado toma demasiado tiempo

Tarea: Quiero cambiar el tamaño y mover una imagen a través de la pantalla. Quiero hacer esto sin problemas, no importa cuán grande sea la imagen. El código debe estar soportado por el API nivel 8.

Problema: Traté de usar ImageView con scaleType="matrix" . Llamar ImageView.setMatrix() y luego ImageView.invalidate() funciona muy bien con imágenes pequeñas pero horrible con las grandes. No importa cuán grande sea el ImageView .

¿Puedo de alguna manera acelerar el repintado de ImageView para que no vuelva a calcular la imagen completa? Tal vez hay una manera de lograr la tarea utilizando diferentes componentes?


EDIT: Más información sobre lo que estoy tratando de lograr.

  • Pw, ph – anchura y altura de la imagen (en píxeles)
  • Dw, dh – anchura y altura de la pantalla del dispositivo (en píxeles)
  • Fw, fh – anchura y altura del marco visible (en píxeles)
  • X, y – posición de la esquina superior izquierda del marco (en píxeles) Visualización del problema.

Quiero mostrar una parte de la imagen en la pantalla. Las propiedades x , y , fw y fh están cambiando constantemente. Estoy buscando una parte de código (idea), o componentes que para estas 8 variables especificadas rápidamente generar y mostrar la parte de la imagen.


EDIT 2: Información sobre pw y ph

Supongo que pw y ph pueden contener valores de 1 a infinito. Si este enfoque causa muchos problemas, podemos suponer que la imagen no es mayor que la foto tomada con la cámara del dispositivo.

Con su ayuda (comunidad) me di cuenta de la solución. Estoy seguro de que hay otras mejores maneras de hacerlo, pero mi solución no es muy complicado y debería funcionar con cualquier imagen, cualquier Android desde API de nivel 8.

La solución es utilizar dos objetos ImageView lugar de uno.

El primer ImageView funcionará como antes, pero la imagen cargada se reducirá de manera que su ancho será menor que el ancho del ImageView y su altura será menor que la altura del ImageView .

El segundo ImageView estará en blanco al principio. Cada vez que las propiedades x , y , fw y fh están cambiando, el AsyncTask se ejecutará para cargar sólo parte visible de la imagen. Cuando las propiedades cambian rápidamente, el AsyncTask no podrá terminar a tiempo. Tendrá que ser cancelado y se iniciará uno nuevo. Cuando termine el resultado Bitmap se cargará en el segundo ImageView para que sea visible para el usuario. Cuando las propiedades cambian de nuevo Cargado Bitmap se eliminará, por lo que no cubrirá el movimiento Bitmap cargado en el primer ImageView . Nota: BitmapRegionDecoder que BitmapRegionDecoder para cargar BitmapRegionDecoder está disponible desde Android API de nivel 10, por lo que los usuarios de API 8 y API 9 sólo verán imagen reducida. Decidí que estaba bien.


Código necesario:

  • Configuración de la primera (inferior) ImageView scaleType="matrix" (mejor en XML)
  • Configuración de la segunda (arriba) ImageView scaleType="fitXY" (mejor en XML)
  • Funciones de la documentación de Android (aquí) – gracias al usuario Vishavjeet Singh .

NOTA: Observe el || Operador en lugar de && mientras calcula inSampleSize . Queremos que la imagen cargada sea más pequeña que ImageView para que tengamos suficiente RAM para cargarla. (Supongo que el tamaño de ImageView no es mayor que el tamaño de la pantalla del dispositivo.También supongo que el dispositivo tiene suficiente memoria para cargar al menos 2 Bitmaps del tamaño de la pantalla del dispositivo.Por favor, dígame si estoy cometiendo un error aquí. )
NOTA 2: Estoy cargando imágenes usando InputStream . Para cargar un archivo de manera diferente tendrá que cambiar el código en try{...} catch(...){...} bloques.

 public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight || (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } public Bitmap decodeSampledBitmapFromResource(Uri fileUri, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; try { InputStream is = this.getContentResolver().openInputStream(fileUri); BitmapFactory.decodeStream(is, null, options); } catch (Exception e) { e.printStackTrace(); return null; } // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; try { InputStream is = this.getContentResolver().openInputStream(fileUri); return BitmapFactory.decodeStream(is, null, options); } catch (Exception e) { e.printStackTrace(); return null; } } 
  • Función que devuelve una subimagen de una imagen.

NOTA: El tamaño del rectángulo que se cortará de la imagen de origen es relativo a la imagen. Los valores que lo especifican son de 0 a 1 porque el tamaño de ImageView y de Bitmap cargado s difiere del tamaño de la imagen original.

 public Bitmap getCroppedBitmap (Uri fileUri, int outWidth, int outHeight, double rl, double rt, double rr, double rb) { // rl, rt, rr, rb are relative (values from 0 to 1) to the size of the image. // That is because image moving will be smaller than the original. if (Build.VERSION.SDK_INT >= 10) { // Ensure that device supports at least API level 10 // so we can use BitmapRegionDecoder BitmapRegionDecoder brd; try { // Again loading from URI. Change the code so it suits yours. InputStream is = this.getContentResolver().openInputStream(fileUri); brd = BitmapRegionDecoder.newInstance(is, true); BitmapFactory.Options options = new BitmapFactory.Options(); options.outWidth = (int)((rr - rl) * brd.getWidth()); options.outHeight = (int)((rb - rt) * brd.getHeight()); options.inSampleSize = calculateInSampleSize(options, outWidth, outHeight); return brd.decodeRegion(new Rect( (int) (rl * brd.getWidth()), (int) (rt * brd.getHeight()), (int) (rr * brd.getWidth()), (int) (rb * brd.getHeight()) ), options); } catch (Exception e) { e.printStackTrace(); return null; } } else return null; } 
  • AsyncTask cargando la AsyncTask Bitmap .

NOTA: aviso declarando una variable del tipo de esta clase. Se utilizará más tarde.

 private LoadHiResImageTask loadHiResImageTask = new LoadHiResImageTask(); private class LoadHiResImageTask extends AsyncTask<Double, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(Double... numbers) { return getCroppedBitmap( // You will have to change first parameter here! Uri.parse(imagesToCrop[0]), numbers[0].intValue(), numbers[1].intValue(), numbers[2], numbers[3], numbers[4], numbers[5]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { ImageView hiresImage = (ImageView) findViewById(R.id.hiresImage); hiresImage.setImageBitmap(result); hiresImage.postInvalidate(); } } 
  • Función que hará que todos trabajen juntos.

Esta función se llamará cada vez que cambie la propiedad x , y , fw o fh .
NOTA: hiresImage en mi código es el id del segundo (superior) ImageView

 private void updateImageView () { // ... your code to update ImageView matrix ... // // imageToCrop.setImageMatrix(m); // imageToCrop.postInvalidateDelayed(10); if (Build.VERSION.SDK_INT >= 10) { ImageView hiresImage = (ImageView) findViewById(R.id.hiresImage); hiresImage.setImageDrawable(null); hiresImage.invalidate(); if (loadHiResImageTask.getStatus() != AsyncTask.Status.FINISHED) { loadHiResImageTask.cancel(true); } loadHiResImageTask = null; loadHiResImageTask = new LoadHiResImageTask(); loadHiResImageTask.execute( (double) hiresImage.getWidth(), (double) hiresImage.getHeight(), // x, y, fw, fh are properties from the question (double) x / d.getIntrinsicWidth(), (double) y / d.getIntrinsicHeight(), (double) x / d.getIntrinsicWidth() + fw / d.getIntrinsicWidth(), (double) y / d.getIntrinsicHeight() + fh / d.getIntrinsicHeight()); } } 

Intente cargar mapa de bits de origen por Opciones de BitmapFactory en solo límites de decodificación

 public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } 

Después de escribir estos 2 métodos

 mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)); 

Set bitmap en imageview como éste

Después intente escalar la imagen que hará para un mapa de bits grande eficientemente

Puedes leer aquí más

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Como ya dijiste, podrías recortar tu imagen grande y colocarla una por una en la nueva imagen.

Pero desde el otro lado podría sugerir que cortar su gran imagen en varios pequeños y en este caso, probablemente ahorrar memoria y velocidad, porque no se carga toda la imagen en su memoria.

Usted ganará similares que por ejemplo google maps tienen – tienen muchos pequeños azulejos que se cargan por la demanda. No cargan el mapa del mundo entero sino las partes pequeñas de él.

En este caso, se construirá algo como ListView cuando cada nuevo elemento será una vista de imagen y contiene alguna pequeña imagen que forma parte de una imagen grande. Por cierto, con tal enfoque que podría obtener aún repetidos azulejos de fondo y cambiarlos en tiempo de ejecución.

  • No entiendo cómo utilizar GridLayout.spec ()
  • Android RandomAccessFile uso del recurso
  • Cómo acceder a setPreferredNetworkType en la fuente de Android
  • ¿Cómo almacenar las contraseñas de forma segura en Android?
  • ¿Cómo agregar icono de búsqueda con texto como sugerencia en el campo de texto?
  • Conversión de mapa de bits: Creación de mapa de bits que excluye los lados transparentes del mapa de bits transparente
  • Cómo pasar los datos con formato JSON desde un WebView a una página HTML
  • Android ¿por qué Fragmentos no deben comunicarse directamente entre sí?
  • Problema de fuga de base de datos de proveedor de contenido de Android
  • IntelliJ Build Error Context Mismatch
  • ¿Usa la unidad Xamarin?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.