Wie kann ich ein Bitmap-Objekt von einer Aktivität an eine andere übergeben?

145

In meiner Aktivität erstelle ich ein BitmapObjekt und muss dann ein anderes starten Activity. Wie kann ich dieses BitmapObjekt aus der Unteraktivität (der zu startenden) übergeben?

Michael
quelle

Antworten:

297

Bitmapimplementiert Parcelable, so dass Sie es immer mit der Absicht übergeben können:

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

und am anderen Ende abrufen:

Intent intent = getIntent(); 
Bitmap bitmap = (Bitmap) intent.getParcelableExtra("BitmapImage");
Erich Douglass
quelle
84
Wenn die Bitmap als Datei oder Ressource vorhanden ist, ist es immer besser, das URIoder ResourceIDder Bitmap und nicht die Bitmap selbst zu übergeben. Das Übergeben der gesamten Bitmap erfordert viel Speicher. Das Übergeben der URL erfordert sehr wenig Speicher und ermöglicht es jeder Aktivität, die Bitmap nach Bedarf zu laden und zu skalieren.
Slayton
3
Funktioniert nicht für mich, aber dieser funktioniert: stackoverflow.com/questions/11010386/…
Houssem
1
@slayton Wie übergeben wir Bilder als URI / ResourceIDs? Beispiel? Vielen Dank!
WantIt
Wenn Sie die Bitmap so extra hinzufügen, ist dies keine bewährte Methode. Wenn die Bitmap-Objektgröße größer ist, erhalten Sie "java.lang.SecurityException: App für Aufrufer android.app.ApplicationThreadProxy ...... kann nicht gefunden werden." Der empfohlene Weg ist, wie @slayton sagt, Sie müssen Bitmap auf externem Speicher speichern und nur den URI übergeben.
AITAALI_ABDERRAHMANE
1
Was ist die maximale Größe der Bitmap, die übergeben werden kann?
AtifSayings
16

Das Übergeben einer Bitmap als parcelbar im Paket zwischen Aktivitäten ist aufgrund der Größenbeschränkung von parceable (1 MB) keine gute Idee. Sie können die Bitmap in einer Datei im internen Speicher speichern und die gespeicherte Bitmap in mehreren Aktivitäten abrufen. Hier ist ein Beispielcode.

So speichern Sie Bitmap in einer Datei myImage im internen Speicher:

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

In der nächsten Aktivität können Sie diese Datei myImage mit folgendem Code in eine Bitmap dekodieren:

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

Hinweis Viele Überprüfungen auf Null- und Skalierungs-Bitmaps werden nicht durchgeführt.

Illegales Argument
quelle
Dies wird nicht kompiliert - Methode kann nicht aufgelöst werden openFileOutput.
Hawklike
4

Wenn das Bild zu groß ist und Sie es nicht speichern und in den Speicher laden können, sollten Sie nur einen globalen statischen Verweis auf die Bitmap (innerhalb der empfangenden Aktivität) verwenden, der bei onDestory auf null zurückgesetzt wird, nur wenn "isChangingConfigurations" gibt true zurück.

Android-Entwickler
quelle
3

Weil Intent eine Größenbeschränkung hat. Ich verwende ein öffentliches statisches Objekt, um die Bitmap vom Dienst an die Übertragung zu übergeben.

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

in meinem Dienst übergeben

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

Mein 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 瑜
quelle
2

Komprimieren und senden Bitmap

Die akzeptierte Antwort stürzt ab, wenn die Bitmapzu groß ist. Ich glaube, es ist ein Limit von 1 MB . Das Bitmapmuss in ein anderes Dateiformat wie ein durch a dargestelltes JPG komprimiert werden ByteArray, dann kann es sicher über ein übergeben werdenIntent .

Implementierung

Die Funktion ist in einem separaten Thread mit Kotlin Coroutines enthalten, da die BitmapKomprimierung verkettet wird, nachdem die Bitmapaus einer URL erstellt wurde String. Für die BitmapErstellung ist ein separater Thread erforderlich, um ANR- Fehler (Application Not Responding) zu vermeiden.

Verwendete Konzepte

Code

1. Nach der Erstellung Bitmapin JPG komprimieren ByteArray.

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. Bild wie ByteArrayüber übergeben Intent.

In diesem Beispiel wird es von einem Fragment an einen Dienst übergeben . Es ist das gleiche Konzept, wenn es zwischen zwei Aktivitäten geteilt wird .

Fragment.kt

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

3. Konvertieren Sie ByteArrayzurück zu 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
quelle
1

Es könnte spät sein, kann aber helfen. Deklarieren Sie auf dem ersten Fragment oder der ersten Aktivität eine Klasse ... zum Beispiel

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

Dann machen Sie dies auf der zweiten Klasse / Fragment.

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

Ich hoffe es hilft.

Mwangi Njuguna
quelle
1

Alle oben genannten Lösungen funktionieren bei mir nicht. Das Senden einer Bitmap führt parceableByteArrayebenfalls zu Fehlern android.os.TransactionTooLargeException: data parcel size.

Lösung

  1. Die Bitmap wurde im internen Speicher gespeichert als:
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. und senden putExtra(String)als
Intent intent = new Intent(ActivitySketcher.this,ActivityEditor.class);
intent.putExtra("KEY", saveBitmap(bmp));
startActivity(intent);
  1. und Erhalte es in einer anderen Aktivität als:
if(getIntent() != null){
  try {
           src = BitmapFactory.decodeStream(openFileInput("myImage"));
       } catch (FileNotFoundException e) {
            e.printStackTrace();
      }

 }

Ali Tamoor
quelle
0

Sie können eine Bitmap-Übertragung erstellen. Versuche dies....

In der ersten Klasse:

1) Erstellen:

private static Bitmap bitmap_transfer;

2) Erstellen Sie Getter und Setter

public static Bitmap getBitmap_transfer() {
    return bitmap_transfer;
}

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

3) Stellen Sie das Bild ein:

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

Dann in der zweiten Klasse:

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

In meinem Fall hat der oben erwähnte Weg bei mir nicht funktioniert. Jedes Mal, wenn ich die Bitmap in die Absicht einfügte, wurde die zweite Aktivität nicht gestartet. Das gleiche passierte, als ich die Bitmap als Byte [] übergab.

Ich folgte diesem Link und es funktionierte wie ein Charme und sehr schnell:

package your.packagename

import android.graphics.Bitmap;

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

in meiner 1. Tätigkeit:

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

und hier ist das onCreate () meiner 2. Aktivität:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Bitmap photo = Constants.photoFinishBitmap;
    if (photo != null) {
        mViewHolder.imageViewerImage.setImageDrawable(new BitmapDrawable(getResources(), photo));
    }
}
Camino2007
quelle
Ich habe es versucht, hat nicht funktioniert. Ich bin dem Link gefolgt, und es scheint, dass Sie CommonResources.photoFinishBitmapanstelle von verwendet haben sollten Constants.photoFinishBitmap.
Nathan Hutton
Schlechte Praxis. Was passiert mit dem statischen Feld in der Aktivitätsklasse während der Neuerstellung des gesamten Prozesses (z. B. aufgrund geänderter Berechtigungen für die App zur Laufzeit)? Die Antwort lautet NPE.
Alexander