Comment puis-je passer un objet Bitmap d'une activité à une autre


146

Dans mon activité, je crée un Bitmap objet puis je dois en lancer un autre Activity, comment passer cet Bitmapobjet depuis la sous-activité (celle qui va être lancée)?

Réponses:


297

Bitmapimplémente Parcelable, de sorte que vous puissiez toujours le transmettre avec l'intention:

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

et récupérez-le à l'autre bout:

Intent intent = getIntent(); 
Bitmap bitmap = (Bitmap) intent.getParcelableExtra("BitmapImage");

85
Si le bitmap existe en tant que fichier ou ressource, il est toujours préférable de passer le URIou ResourceIDdu bitmap et non le bitmap lui-même. Passer le bitmap entier nécessite beaucoup de mémoire. La transmission de l'URL nécessite très peu de mémoire et permet à chaque activité de charger et de mettre à l'échelle le bitmap selon ses besoins.
slayton

3
Cela ne fonctionne pas pour moi, mais celui-ci fonctionne: stackoverflow.com/questions/11010386/…
Houssem

1
@slayton comment passons-nous des images en tant qu'URI / ResourceID? exemple? Merci!
WantIt

mettre un bitmap en plus comme ça n'est pas une bonne pratique, si la taille de l'objet bitmap est plus grande, alors vous obtenez "java.lang.SecurityException: Impossible de trouver l'application pour l'appelant android.app.ApplicationThreadProxy ......". la méthode recommandée est comme @slayton le dit, vous devez enregistrer le bitmap sur le stockage externe et ne transmettre que l'URI.
AITAALI_ABDERRAHMANE

1
quelle est la taille maximale du bitmap qui peut être transmise?
AtifSayings


17

Passer le bitmap comme parceable dans le bundle entre les activités n'est pas une bonne idée en raison de la limitation de taille de Parceable (1mb). Vous pouvez stocker le bitmap dans un fichier dans la mémoire interne et récupérer le bitmap stocké dans plusieurs activités. Voici un exemple de code.

Pour stocker le bitmap dans un fichier myImage dans la mémoire interne:

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;
}

Ensuite, dans l'activité suivante, vous pouvez décoder ce fichier myImage en un bitmap en utilisant le code suivant:

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

Remarque Une grande partie de la vérification des bitmap nuls et de mise à l'échelle est omise.


Cela ne compilera pas - ne peut pas résoudre la méthode openFileOutput.
Hawklike

4

Si l'image est trop grande et que vous ne pouvez pas l'enregistrer et la charger dans le stockage, vous devriez envisager d'utiliser simplement une référence statique globale au bitmap (à l'intérieur de l'activité de réception), qui sera réinitialisée à null sur onDestory, uniquement si "isChangingConfigurations" renvoie vrai.


3

Parce que l'intention a une limite de taille. J'utilise un objet statique public pour passer le bitmap d'un service à l'autre ...

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

passer à mon service

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();
            }
        });
    }

Mon récepteur de diffusion

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);
            }
        }
    };

2

Compresser et envoyer Bitmap

La réponse acceptée plantera lorsque le Bitmapest trop grand. Je pense que c'est une limite de 1 Mo. Le Bitmapdoit être compressé dans un format de fichier différent tel qu'un JPG représenté par un ByteArray, puis il peut être transmis en toute sécurité via un fichier Intent.

la mise en oeuvre

La fonction est contenue dans un thread séparé à l'aide de Kotlin Coroutines car la Bitmapcompression est chaînée après la Bitmapcréation de l'URL String. La Bitmapcréation nécessite un thread distinct afin d'éviter les erreurs ANR (Application Not Responding) .

Concepts utilisés

  • Kotlin Coroutines notes .
  • Le modèle Chargement, Contenu, Erreur (LCE) est utilisé ci-dessous. Si vous êtes intéressé, vous pouvez en apprendre plus à ce sujet dans cette conférence et cette vidéo .
  • LiveData est utilisé pour renvoyer les données. J'ai compilé ma ressource LiveData préférée dans ces notes .
  • À l' étape 3 , toBitmap()une fonction d'extension Kotlin nécessite que cette bibliothèque soit ajoutée aux dépendances de l'application.

Code

1. Compresser Bitmapen JPG ByteArray après sa création.

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. Transmettez l'image comme ByteArrayvia unIntent .

Dans cet exemple, il est passé d'un fragment à un service . C'est le même concept s'il est partagé entre deux activités .

Fragment.kt

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

3. Reconvertissez ByteArrayen 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()
        }
    }

1

Il peut être tard mais peut aider. Sur le premier fragment ou activité déclarez une classe ... par exemple

   @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;
}

Ensuite, sur la deuxième classe / fragment, faites ceci.

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

J'espère que ça aide.


1

Toutes les solutions ci-dessus ne fonctionnent pas pour moi, l'envoi de bitmap parceableByteArraygénère également une erreurandroid.os.TransactionTooLargeException: data parcel size .

Solution

  1. Enregistré le bitmap dans la mémoire interne comme:
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. et envoyer putExtra(String)comme
Intent intent = new Intent(ActivitySketcher.this,ActivityEditor.class);
intent.putExtra("KEY", saveBitmap(bmp));
startActivity(intent);
  1. et recevez-le dans une autre activité comme:
if(getIntent() != null){
  try {
           src = BitmapFactory.decodeStream(openFileInput("myImage"));
       } catch (FileNotFoundException e) {
            e.printStackTrace();
      }

 }


0

Vous pouvez créer un transfert bitmap. essaye ça....

Dans la première classe:

1) Créez:

private static Bitmap bitmap_transfer;

2) Créer un getter et un setter

public static Bitmap getBitmap_transfer() {
    return bitmap_transfer;
}

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

3) Réglez l'image:

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

Puis, en deuxième classe:

ImageView image2 = (ImageView) view.findViewById(R.id.img2);
imagem2.setImageDrawable(new BitmapDrawable(getResources(), classe1.getBitmap_transfer()));

-2

Dans mon cas, la manière mentionnée ci-dessus n'a pas fonctionné pour moi. Chaque fois que je mets le bitmap dans l'intention, la deuxième activité ne démarre pas. La même chose s'est produite lorsque j'ai passé le bitmap en tant que byte [].

J'ai suivi ce lien et cela a fonctionné comme un charme et très vite:

package your.packagename

import android.graphics.Bitmap;

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

dans ma 1ère activité:

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

et voici le onCreate () de ma 2ème activité:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Bitmap photo = Constants.photoFinishBitmap;
    if (photo != null) {
        mViewHolder.imageViewerImage.setImageDrawable(new BitmapDrawable(getResources(), photo));
    }
}

J'ai essayé cela, je n'ai pas fonctionné. J'ai suivi le lien, et il semble que vous auriez dû utiliser à la CommonResources.photoFinishBitmapplace de Constants.photoFinishBitmap.
Nathan Hutton

Mauvaise pratique. Que se passera-t-il avec le champ statique de la classe Activity lors de la recréation de l'ensemble du processus (par exemple, en raison de la modification des autorisations de l'application au moment de l'exécution)? La réponse est NPE.
Alexander
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.