¿Cómo puedo pasar un objeto de mapa de bits de una actividad a otra?

145

En mi actividad, creo un Bitmapobjeto y luego necesito lanzar otro Activity. ¿Cómo puedo pasar este Bitmapobjeto desde la sub-actividad (la que se va a lanzar)?

Miguel
fuente

Respuestas:

297

Bitmapimplementa Parcelable, por lo que siempre puede pasarlo con la intención:

Intent intent = new Intent(this, NewActivity.class);
intent.putExtra("BitmapImage", bitmap);

y recuperarlo en el otro extremo:

Intent intent = getIntent(); 
Bitmap bitmap = (Bitmap) intent.getParcelableExtra("BitmapImage");
Erich Douglass
fuente
84
Si el mapa de bits existe como un archivo o un recurso, su es siempre mejor para pasar el URIo ResourceIDdel mapa de bits y no el propio mapa de bits. Pasar todo el mapa de bits requiere mucha memoria. Pasar la URL requiere muy poca memoria y permite que cada actividad cargue y escale el mapa de bits según lo necesite.
slayton
3
No funciona para mí, pero este sí: stackoverflow.com/questions/11010386/…
Houssem
1
@slayton, ¿cómo pasamos las imágenes como URI / ResourceID? ¿ejemplo? ¡Gracias!
WantIt
poner bitmap en extra de esa manera, no es una buena práctica, si el tamaño del objeto de mapa de bits es mayor, entonces obtienes "java.lang.SecurityException: No se puede encontrar la aplicación para la persona que llama android.app.ApplicationThreadProxy ......". la forma recomendada es como dice @slayton, debe guardar el mapa de bits en el almacenamiento externo y pasar solo el URI.
AITAALI_ABDERRAHMANE
1
¿Cuál es el tamaño máximo de mapa de bits que se puede pasar?
AtifSayings
16

Pasar el mapa de bits como parceable en el paquete entre actividades no es una buena idea debido a la limitación de tamaño de Parceable (1mb). Puede almacenar el mapa de bits en un archivo en almacenamiento interno y recuperar el mapa de bits almacenado en varias actividades. Aquí hay un código de muestra.

Para almacenar un mapa de bits en un archivo myImage en almacenamiento interno:

public String createImageFromBitmap(Bitmap bitmap) {
    String fileName = "myImage";//no .png or .jpg needed
    try {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
        FileOutputStream fo = openFileOutput(fileName, Context.MODE_PRIVATE);
        fo.write(bytes.toByteArray());
        // remember close file output
        fo.close();
    } catch (Exception e) {
        e.printStackTrace();
        fileName = null;
    }
    return fileName;
}

Luego, en la siguiente actividad, puede decodificar este archivo myImage en un mapa de bits utilizando el siguiente código:

//here context can be anything like getActivity() for fragment, this or MainActivity.this
Bitmap bitmap = BitmapFactory.decodeStream(context.openFileInput("myImage"));

Nota Se omite mucha comprobación de mapas de bits nulos y de escala.

Argumento ilegal
fuente
Esto no se compilará, no se puede resolver el método openFileOutput.
Hawklike
4

Si la imagen es demasiado grande y no puede guardarla y cargarla en el almacenamiento, debería considerar usar una referencia global estática al mapa de bits (dentro de la actividad de recepción), que se restablecerá a nulo en onDestory, solo si "isChangingConfigurations" devuelve verdadero

desarrollador de Android
fuente
3

Porque la intención tiene límite de tamaño. Utilizo un objeto público estático para pasar el mapa de bits del servicio a la transmisión ...

public class ImageBox {
    public static Queue<Bitmap> mQ = new LinkedBlockingQueue<Bitmap>(); 
}

pasar en mi servicio

private void downloadFile(final String url){
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap b = BitmapFromURL.getBitmapFromURL(url);
                synchronized (this){
                    TaskCount--;
                }
                Intent i = new Intent(ACTION_ON_GET_IMAGE);
                ImageBox.mQ.offer(b);
                sendBroadcast(i);
                if(TaskCount<=0)stopSelf();
            }
        });
    }

My BroadcastReceiver

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            LOG.d(TAG, "BroadcastReceiver get broadcast");

            String action = intent.getAction();
            if (DownLoadImageService.ACTION_ON_GET_IMAGE.equals(action)) {
                Bitmap b = ImageBox.mQ.poll();
                if(b==null)return;
                if(mListener!=null)mListener.OnGetImage(b);
            }
        }
    };
Deyu 瑜
fuente
2

Comprimir y enviar Bitmap

La respuesta aceptada se bloqueará cuando Bitmapsea ​​demasiado grande. Creo que es un límite de 1 MB . Se Bitmapdebe comprimir en un formato de archivo diferente, como un JPG representado por un ByteArray, luego se puede pasar de forma segura a través de unIntent .

Implementación

La función está contenida en un hilo separado usando Kotlin Coroutines porque la Bitmapcompresión se encadena después de que Bitmapse crea desde una url String. La Bitmapcreación requiere un subproceso separado para evitar errores de aplicación que no responde (ANR) .

Conceptos usados

  • Kotlin corrutinas notas .
  • El patrón de carga, contenido, error (LCE) se utiliza a continuación. Si está interesado, puede obtener más información al respecto en esta charla y video .
  • LiveData se utiliza para devolver los datos. He compilado mi recurso LiveData favorito en estas notas .
  • En el Paso 3 , toBitmap()hay una función de extensión de Kotlin que requiere que la biblioteca se agregue a las dependencias de la aplicación.

Código

1. Comprima Bitmapa JPG ByteArray después de que se haya creado.

Repository.kt

suspend fun bitmapToByteArray(url: String) = withContext(Dispatchers.IO) {
    MutableLiveData<Lce<ContentResult.ContentBitmap>>().apply {
        postValue(Lce.Loading())
        postValue(Lce.Content(ContentResult.ContentBitmap(
            ByteArrayOutputStream().apply {
                try {                     
                    BitmapFactory.decodeStream(URL(url).openConnection().apply {
                        doInput = true
                        connect()
                    }.getInputStream())
                } catch (e: IOException) {
                   postValue(Lce.Error(ContentResult.ContentBitmap(ByteArray(0), "bitmapToByteArray error or null - ${e.localizedMessage}")))
                   null
                }?.compress(CompressFormat.JPEG, BITMAP_COMPRESSION_QUALITY, this)
           }.toByteArray(), "")))
        }
    }

ViewModel.kt

//Calls bitmapToByteArray from the Repository
private fun bitmapToByteArray(url: String) = liveData {
    emitSource(switchMap(repository.bitmapToByteArray(url)) { lce ->
        when (lce) {
            is Lce.Loading -> liveData {}
            is Lce.Content -> liveData {
                emit(Event(ContentResult.ContentBitmap(lce.packet.image, lce.packet.errorMessage)))
            }
            is Lce.Error -> liveData {
                Crashlytics.log(Log.WARN, LOG_TAG,
                        "bitmapToByteArray error or null - ${lce.packet.errorMessage}")
            }
        }
    })
}

2. Pase la imagen como a ByteArraytravés de un Intent.

En esta muestra se pasa de un Fragmento a un Servicio . Es el mismo concepto si se comparte entre dos actividades .

Fragment.kt

ContextCompat.startForegroundService(
    context!!,
    Intent(context, AudioService::class.java).apply {
        action = CONTENT_SELECTED_ACTION
        putExtra(CONTENT_SELECTED_BITMAP_KEY, contentPlayer.image)
    })

3. Convertir de ByteArraynuevo a Bitmap.

Utils.kt

fun ByteArray.byteArrayToBitmap(context: Context) =
    run {
        BitmapFactory.decodeByteArray(this, BITMAP_OFFSET, size).run {
            if (this != null) this
            // In case the Bitmap loaded was empty or there is an error I have a default Bitmap to return.
            else AppCompatResources.getDrawable(context, ic_coinverse_48dp)?.toBitmap()
        }
    }
Adam Hurwitz
fuente
1

Puede ser tarde pero puede ayudar. En el primer fragmento o actividad, declare una clase ... por ejemplo

   @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        description des = new description();

        if (requestCode == PICK_IMAGE_REQUEST && data != null && data.getData() != null) {
            filePath = data.getData();
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), filePath);
                imageView.setImageBitmap(bitmap);
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
                constan.photoMap = bitmap;
            } catch (IOException e) {
                e.printStackTrace();
            }
       }
    }

public static class constan {
    public static Bitmap photoMap = null;
    public static String namePass = null;
}

Luego, en la segunda clase / fragmento, haga esto ...

Bitmap bm = postFragment.constan.photoMap;
final String itemName = postFragment.constan.namePass;

Espero eso ayude.

Mwangi Njuguna
fuente
1

Todas las soluciones anteriores no me funcionan, el envío de mapas de bits parceableByteArraytambién genera errores android.os.TransactionTooLargeException: data parcel size.

Solución

  1. Guardado el mapa de bits en el almacenamiento interno como:
public String saveBitmap(Bitmap bitmap) {
        String fileName = "ImageName";//no .png or .jpg needed
        try {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
            FileOutputStream fo = openFileOutput(fileName, Context.MODE_PRIVATE);
            fo.write(bytes.toByteArray());
            // remember close file output
            fo.close();
        } catch (Exception e) {
            e.printStackTrace();
            fileName = null;
        }
        return fileName;
    }
  1. y enviar putExtra(String)como
Intent intent = new Intent(ActivitySketcher.this,ActivityEditor.class);
intent.putExtra("KEY", saveBitmap(bmp));
startActivity(intent);
  1. y recibirlo en otra actividad como:
if(getIntent() != null){
  try {
           src = BitmapFactory.decodeStream(openFileInput("myImage"));
       } catch (FileNotFoundException e) {
            e.printStackTrace();
      }

 }

Ali Tamoor
fuente
0

Puede crear una transferencia de mapa de bits. prueba esto....

En la primera clase:

1) Crear:

private static Bitmap bitmap_transfer;

2) Crear getter y setter

public static Bitmap getBitmap_transfer() {
    return bitmap_transfer;
}

public static void setBitmap_transfer(Bitmap bitmap_transfer_param) {
    bitmap_transfer = bitmap_transfer_param;
}

3) Establecer la imagen:

ImageView image = (ImageView) view.findViewById(R.id.image);
image.buildDrawingCache();
setBitmap_transfer(image.getDrawingCache());

Luego, en la segunda clase:

ImageView image2 = (ImageView) view.findViewById(R.id.img2);
imagem2.setImageDrawable(new BitmapDrawable(getResources(), classe1.getBitmap_transfer()));
Rômulo ZC Cunha
fuente
-2

En mi caso, la forma mencionada anteriormente no funcionó para mí. Cada vez que pongo el mapa de bits en la intención, la segunda actividad no comenzó. Lo mismo sucedió cuando pasé el mapa de bits como byte [].

Seguí este enlace y funcionó como un encanto y muy rápido:

package your.packagename

import android.graphics.Bitmap;

public class CommonResources { 
      public static Bitmap photoFinishBitmap = null;
}

en mi primera actividad:

Constants.photoFinishBitmap = photoFinishBitmap;
Intent intent = new Intent(mContext, ImageViewerActivity.class);
startActivity(intent);

y aquí está el onCreate () de mi segunda actividad:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Bitmap photo = Constants.photoFinishBitmap;
    if (photo != null) {
        mViewHolder.imageViewerImage.setImageDrawable(new BitmapDrawable(getResources(), photo));
    }
}
Camino2007
fuente
Intenté esto, no funcionó. Seguí el enlace, y parece que deberías haber usado en CommonResources.photoFinishBitmaplugar de Constants.photoFinishBitmap.
Nathan Hutton
Mala práctica. ¿Qué pasará con el campo estático en la clase Actividad durante la recreación de todo el proceso (por ejemplo, debido a los permisos cambiantes para la aplicación en tiempo de ejecución)? La respuesta es NPE.
Alexander