how do you pass images (bitmaps) between android activities using bundles?
Suppose I have an activity to select an image from the gallery, and retrieve it as a BitMap, just like the example: here
Now, I want to pass this BitMap to be used in an ImageView for another activity. I am aware bundles can be passed between activities, but how would I开发者_高级运维 store this BitMap into the bundle?
or is there another approach I should take?
I would highly recommend a different approach.
It's possible if you REALLY want to do it, but it costs a lot of memory and is also slow. It might not work if you have an older phone and a big bitmap. You could just pass it as an extra, for example intent.putExtra("data", bitmap)
. A Bitmap implements Parcelable, so you can put it in an extra. Likewise, a bundle has putParcelable
.
If you want to pass it inbetween activities, I would store it in a file. That's more efficient, and less work for you. You can create private files in your data folder using MODE_PRIVATE that are not accessible to any other app.
If you pass it as a Parcelable, you're bound to get a JAVA BINDER FAILURE error. So, the solution is this: If the bitmap is small, like, say, a thumbnail, pass it as a byte array and build the bitmap for display in the next activity. For instance:
in your calling activity...
Intent i = new Intent(this, NextActivity.class);
Bitmap b; // your bitmap
ByteArrayOutputStream bs = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 50, bs);
i.putExtra("byteArray", bs.toByteArray());
startActivity(i);
...and in your receiving activity
if(getIntent().hasExtra("byteArray")) {
ImageView previewThumbnail = new ImageView(this);
Bitmap b = BitmapFactory.decodeByteArray(
getIntent().getByteArrayExtra("byteArray"),0,getIntent().getByteArrayExtra("byteArray").length);
previewThumbnail.setImageBitmap(b);
}
As suggested by @EboMike I saved the bitmap in a file named myImage in the internal storage of my application not accessible my other apps. Here's the code of that part:
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;
}
Then in the next activity you can decode this file myImage to a bitmap using following code:
Bitmap bitmap = BitmapFactory.decodeStream(context
.openFileInput("myImage"));//here context can be anything like getActivity() for fragment, this or MainActivity.this
Note A lot of checking for null and scaling bitmap's is ommited.
Activity
To pass a bitmap between Activites
Intent intent = new Intent(this, Activity.class);
intent.putExtra("bitmap", bitmap);
And in the Activity class
Bitmap bitmap = getIntent().getParcelableExtra("bitmap");
Fragment
To pass a bitmap between Fragments
SecondFragment fragment = new SecondFragment();
Bundle bundle = new Bundle();
bundle.putParcelable("bitmap", bitmap);
fragment.setArguments(bundle);
To receive inside the SecondFragment
Bitmap bitmap = getArguments().getParcelable("bitmap");
Transferring large bitmap (Compress bitmap)
If you are getting failed binder transaction, this means you are exceeding the binder transaction buffer by transferring large element from one activity to another activity.
So in that case you have to compress the bitmap as an byte's array and then uncompress it in another activity, like this
In the FirstActivity
Intent intent = new Intent(this, SecondActivity.class);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPG, 100, stream);
byte[] bytes = stream.toByteArray();
intent.putExtra("bitmapbytes",bytes);
And in the SecondActivity
byte[] bytes = getIntent().getByteArrayExtra("bitmapbytes");
Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Bitmap is Parcelable so you can add using [putExtra(String,Parcelable)][2] method, But not sure it is a best practice, If it is large size data it is better to store in a single place and use from both activities.
[2]: http://developer.android.com/reference/android/content/Intent.html#putExtra(java.lang.String, android.os.Parcelable)
in first.java
Intent i = new Intent(this, second.class);
i.putExtra("uri",uri);
startActivity(i);
in second.java
Bundle bd = getIntent().getExtras();
Uri uri = bd.getParcelable("uri");
Log.e("URI", uri.toString());
try {
Bitmap bitmap = Media.getBitmap(this.getContentResolver(), uri);
imageView.setImageBitmap(bitmap);
}
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
I had to rescale the bitmap a bit to not exceed the 1mb limit of the transaction binder. You can adapt the 400 the your screen or make it dinamic it's just meant to be an example. It works fine and the quality is nice. Its also a lot faster then saving the image and loading it after but you have the size limitation.
public void loadNextActivity(){
Intent confirmBMP = new Intent(this,ConfirmBMPActivity.class);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Bitmap bmp = returnScaledBMP();
bmp.compress(Bitmap.CompressFormat.JPEG, 100, stream);
confirmBMP.putExtra("Bitmap",bmp);
startActivity(confirmBMP);
finish();
}
public Bitmap returnScaledBMP(){
Bitmap bmp=null;
bmp = tempBitmap;
bmp = createScaledBitmapKeepingAspectRatio(bmp,400);
return bmp;
}
After you recover the bmp in your nextActivity with the following code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_confirmBMP);
Intent intent = getIntent();
Bitmap bitmap = (Bitmap) intent.getParcelableExtra("Bitmap");
}
I hope my answer was somehow helpfull. Greetings
Write this code from where you want to Intent into next activity.
yourimageView.setDrawingCacheEnabled(true);
Drawable drawable = ((ImageView)view).getDrawable();
Bitmap bitmap = imageView.getDrawingCache();
Intent intent = new Intent(getBaseContext(), NextActivity.class);
intent.putExtra("Image", imageBitmap);
In onCreate Function of NextActivity.class
Bitmap hotel_image;
Intent intent = getIntent();
hotel_image= intent.getParcelableExtra("Image");
You can pass image in short without using bundle like this This is the code of sender .class file
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher;
Intent intent = new Intent();
Intent.setClass(<Sender_Activity>.this, <Receiver_Activity.class);
Intent.putExtra("Bitmap", bitmap);
startActivity(intent);
and this is receiver class file code.
Bitmap bitmap = (Bitmap)this.getIntent().getParcelableExtra("Bitmap");
ImageView viewBitmap = (ImageView)findViewById(R.id.bitmapview);
viewBitmap.setImageBitmap(bitmap);
No need to compress. that's it
It's better to save the file in temp/cache folder and pass the file path through intent data as I did it. here is sample code:
from on Button Click (Here bitmapFullScreen is a bitmap data which I have collected from Live Server)
Intent intent = new Intent(context, FullscreenActivity.class);
String fPath = CreateTempFile(bitmapFullScreen);
if(!StringUtils.isEmpty(fPath)) {
intent.putExtra("image", fPath);
startActivity(intent);
}
Function to create a File on Temp/Cache folder
public String CreateTempFile(Bitmap mBitmap) {
File f3 = new File(Environment.getExternalStorageDirectory() + "/inpaint/");
if (!f3.exists())
f3.mkdirs();
OutputStream outStream = null;
SimpleDateFormat dateFormatter;
dateFormatter = new SimpleDateFormat("MMddyyyyhhmmss", Locale.US);
String FN = Environment.getExternalStorageDirectory() + "/inpaint/" + dateFormatter.format(Calendar.getInstance().getTime()) + ".png";
File file = new File(FN);
try {
outStream = new FileOutputStream(file);
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.close();
return file.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
2nd File where we need the file
Global var
String FileName = "";
Receive the Intent String data as FilePath to View Image on ImageView this code will be onCreate
ImageView imgUpload = findViewById(R.id.imgUpload);
try {
if (getIntent().hasExtra("image")) {
FileName = getIntent().getStringExtra("image");
Bitmap b = BitmapFactory.decodeFile(FileName);
imgUpload.setImageBitmap(b);
}
} catch (Exception ex) {
Toast.makeText(context, "Error: " + ex.getMessage(), Toast.LENGTH_LONG).show();
}
After display the Image we need to delete temp file
@Override
public void onBackPressed() {
super.onBackPressed();
try {
File file = new File(FileName);
file.delete();
} catch (Exception ex) {
Toast.makeText(context, "Error: " + ex.getMessage(), Toast.LENGTH_LONG).show();
}
}
精彩评论