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();
}
}
Related
Please help me on how to save the image to the device then get it in the next activity.
I was told that if I want to use high quality images, don't pass it in the Intent.
I have to save images to the device that way it doesn't need to be passed in intents but I'm not sure how
This is my onPostExecute code
#Override
protected void onPostExecute(Face[] faces) {
pd.dismiss();
Intent intent = new Intent(getApplicationContext(), ResultActivity.class);
Gson gson = new Gson();
String data = gson.toJson(faces);
if (faces == null || faces.length == 0) {
makeToast("No faces detected. You may not have added the API Key or try retaking the picture.");
} else {
intent.putExtra("list_faces", data);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
intent.putExtra("image", byteArray);
startActivity(intent);
}
}
};
detectTask.execute(inputStream);
}
in the first activity save bitmap on the internal memory
fun saveFile(bitmap: Bitmap, context: Context):String?{
var fileName:String?= "myImage"
try {
val fo = context.openFileOutput(fileName, Context.MODE_PRIVATE)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fo)
fo.close()
} catch (e:Exception) {
e.printStackTrace()
fileName = null
}
return fileName;
}
put fileName in extra and send it for second activity:
val filename=saveFile(bitmap,requireContext())
val intent = Intent(getApplicationContext(), ResultActivity.class)
intent.putExtra("fileName", filename)
startActivity(intent)
and in second activity you can have the bitmap :
val fileName = intent.getStringExtra("fileName")
val bitmap = BitmapFactory.decodeStream(context.openFileInput(fileName))
I'm a beginner in Android, my problem is I can't pass the captured image to another activity for it to be manipulated. My plan is to save the captured photo in byte since i only need it temporarily then pass it to another activity or for the sake of manipulating the image. Is their a way for me to get its "jpg url" without saving to the user's gallery? Thanks in advance.
EDIT: Here are some code from CameraUtility class added with
bitmapPicture = BitmapFactory.decodeByteArray(arg0 , 0, arg0.length);...
try{
//Write File
String filename="bitmap.png";
FileOutputStream stream = getApplicationContext().openFileOutput(filename, Context.MODE_PRIVATE);
// bitmapPicture.compress(Bitmap.CompressFormat.JPEG, 50, stream);
bitmapPicture = BitmapFactory.decodeByteArray(arg0 , 0, arg0.length);
//Cleanup
stream.close();
Toast.makeText(getApplicationContext(), "Saved", Toast.LENGTH_LONG).show();
bitmapPicture.recycle();
//Pop intent
Intent in1 = new Intent(CameraUtility.this, Receiver.class);
in1.putExtra("image", filename);
startActivity(in1);
}catch (Exception e){
e.printStackTrace();
}
Receiver class...
public class Receiver extends AppCompatActivity{
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bitmap bitmapPicture = null;
String filename = getIntent().getStringExtra("image");
try{
FileInputStream is = this.openFileInput(filename);
bitmapPicture = BitmapFactory.decodeStream(is);
is.close();
}catch (Exception e){
e.printStackTrace();
}
setContentView(R.layout.receive_bitmap);
ImageView viewBtimap = findViewById(R.id.bitmapview);
viewBtimap.setImageBitmap(bitmapPicture);
}
EDIT: But now, it doesn't have an error. But the image won't pass on to another activity.
To fix the particular crash, it's enough to add one line to your pictureTaken function:
bitmapPicture = BitmapFactory.decodeByteArray(arg0 , 0, arg0.length);
But this is not the best way to deal with your task.
Instead of converting the picture to PNG, you can save the arg0 byte array as is. It will be a legitimate Jpeg file, and any consumer, including your Receiver activity, can use it with no changes.
Furthermore, you don't need to store the picture as file. Keep the byte array in memory and convert it to bitmap (don't forget to scale it down to match the ImageView dimensions) with BitmapFactory.decodeByteArray().
I'm trying to tap an image view, and open that image inside the default photo viewer:
void handleOpenImage(){
try {
File temp = File.createTempFile("myImage", ".png");
BitmapDrawable bitmapDrawable = (BitmapDrawable) attachedImageView.getDrawable();
Bitmap bitmap = bitmapDrawable.getBitmap();
FileOutputStream stream = new FileOutputStream(temp);
boolean success = bitmap.compress(Bitmap.CompressFormat.PNG, 0, stream);
if(!success){
throw new Exception();
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(temp), "image/*");
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
However, when I call this function, a gallery activity starts, but doesn't display my image. The image file is created successfully at the temp path, double checked that. Why isn't the intent working?
As I asked in the comments, the absolute path of the temp file was /data/data/MY_APPS_IDENTIFIER/cache/ulouder-1004534880.png.
This path ist in the private space each app has and cannot be accessed by other apps for security reasons.
By saving the temp file to another location, the gallery app can access it and displays the image correctly.
I use below code to take screen shot from my layout and share it via android intent but the captured screen shot in the selected app is not showing any thing.
#Override
public void onClick(View view) {
shareBitmap(this,takeScreenshot());
}
public Bitmap takeScreenshot() {
try{
View rootView = getWindow().getDecorView().findViewById(R.id.lyt_main_report_activity);
rootView.setDrawingCacheEnabled(true);
return rootView.getDrawingCache();
}catch (Exception ex){
ex.printStackTrace();
}
return null;
}
public static void shareBitmap(Context context, Bitmap bitmap){
//save to sd card
try {
File cachePath = new File(context.getCacheDir(), "images");
cachePath.mkdirs(); // don't forget to make the directory
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png"); // overwrites this image every time
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try{
//start share activity
File imagePath = new File(context.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = Uri.fromFile(newFile); //FileProvider.getUriForFile(context, "com.persianswitch.apmb.app.fileprovider", newFile);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
//shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
// shareIntent.setDataAndType(contentUri, context. getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.setType("image/*");
context.startActivity(Intent.createChooser(shareIntent, context.getResources().getString(R.string.share_using)));
}
}catch (Exception ex){
ex.printStackTrace();
}
}
Please use this code this is tested code :
public static void takeScreenshot(Context context, View view) {
String path = Environment.getExternalStorageDirectory().toString() +
"/" + "test.png";
View v = view.findViewById(android.R.id.content).getRootView();
v.setDrawingCacheEnabled(true);
v.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v.getDrawingCache());
v.setDrawingCacheEnabled(false);
OutputStream out = null;
File imageFile = new File(path);
try {
out = new FileOutputStream(imageFile);
// choose JPEG format
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
} catch (FileNotFoundException e) {
// manage exception
} catch (IOException e) {
// manage exception
} finally {
try {
if (out != null) {
out.close();
}
} catch (Exception exc) {}
}
// onPauseVideo();
Intent share = new Intent(Intent.ACTION_SEND);
share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
share.setType("image/png");
((Activity) context).startActivityForResult(
Intent.createChooser(share, "Share Drawing"), 111);
}
Since DrawingCache() deprecated above 28 so using Canvas will sort the issues for many. below answer can be used for share Screenshot including text without requesting for permissions.
To take the Screenshot
private Bitmap takeScreenShot(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
To share the Screenshot
private void shareContent(Bitmap bitmap) {
String bitmapPath = MediaStore.Images.Media.insertImage(
binding.getRoot().getContext().getContentResolver(), bitmap, "title", "");
Uri uri = Uri.parse(bitmapPath);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "App");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Currently a new version of KiKi app is available.");
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
binding.getRoot().getContext().startActivity(Intent.createChooser(shareIntent, "Share"));
}
if nothing happens it means your Bitmap is null kindly check that. or you could also fall on View.buildDrawingCache(); which will draw the View to Bitmap and then call your View.getDrawingCache();
also When hardware acceleration is turned on, enabling the drawing cache has no effect on rendering because the system uses a different mechanism for acceleration which ignores the flag. If you want to use a Bitmap for the view, even when hardware acceleration is enabled, see setLayerType(int, android.graphics.Paint) for information on how to enable software and hardware layers.
the quote was taken from the documented page
hope it helps
Tipp:The checked answer works when your device has external storage. On the Samsung 6 for example, it doesnt. Therefore you need to work with fileprovider.
Just incase somebody fell into this trap like me. The problem is that you are putting the name of the image twice.
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png");
and than again.
File newFile = new File(imagePath, "image.png");
Change The seconed one to
File newFile = new File(imagePath);
Otherwise the contentUri give you the bitmap of your screenshot. I hope this helps someone. Took me 3 hours to figure it out :)
I hava a Bitmap variable named bmp in Activity1 , and I want to send the bitmap to Activity2
Following is the code I use to pass it with the intent.
Intent in1 = new Intent(this, Activity2.class);
in1.putExtra("image",bmp);
startActivity(in1);
And in Activity2 I try to access the bitmap using the following code
Bundle ex = getIntent().getExtras();
Bitmap bmp2 = ex.getParceable("image");
ImageView result = (ImageView)findViewById(R.Id.imageView1);
result.setImageBitmap(bmp);
The application runs without an exception but it does not give the expected result
Convert it to a Byte array before you add it to the intent, send it out, and decode.
//Convert to byte array
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
Intent in1 = new Intent(this, Activity2.class);
in1.putExtra("image",byteArray);
Then in Activity 2:
byte[] byteArray = getIntent().getByteArrayExtra("image");
Bitmap bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
edit
Thought I should update this with best practice:
In your first activity, you should save the Bitmap to disk then load it up in the next activity. Make sure to recycle your bitmap in the first activity to prime it for garbage collection:
Activity 1:
try {
//Write file
String filename = "bitmap.png";
FileOutputStream stream = this.openFileOutput(filename, Context.MODE_PRIVATE);
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
//Cleanup
stream.close();
bmp.recycle();
//Pop intent
Intent in1 = new Intent(this, Activity2.class);
in1.putExtra("image", filename);
startActivity(in1);
} catch (Exception e) {
e.printStackTrace();
}
In Activity 2, load up the bitmap:
Bitmap bmp = null;
String filename = getIntent().getStringExtra("image");
try {
FileInputStream is = this.openFileInput(filename);
bmp = BitmapFactory.decodeStream(is);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
Sometimes, the bitmap might be too large for encode&decode or pass as a byte array in the intent. This can cause either OOM or a bad UI experience.
I suggest to consider putting the bitmap into a static variable of the new activity (the one that uses it) which will carefully be null when you no longer need it (meaning in onDestroy but only if "isChangingConfigurations" returns false).
Simply we can pass only Uri of the Bitmap instead of passing Bitmap object. If Bitmap object is Big, that will cause memory issue.
FirstActivity.
intent.putExtra("uri", Uri);
From SecondActivity we get back bitmap.
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),Uri.parse(uri));
Kotlin Code for send Bitmap to another activity by intent:
1- in First Activity :
val i = Intent(this#Act1, Act2::class.java)
var bStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 50, bStream)
val byteArray = bStream.toByteArray()
i.putExtra("image", byteArray )
startActivity(i)
2- In Activity two (Read the bitmap image) :
var bitmap : Bitmap? =null
if (intent.hasExtra("image")){
//convert to bitmap
val byteArray = intent.getByteArrayExtra("image")
bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
}
3- if you need to set it as background for a view, like ConstraintLayout or... :
if (bitmap != null) {
//Convert bitmap to BitmapDrawable
var bitmapDrawable = BitmapDrawable( resources , bitmap)
root_constraintLayout.backgroundDrawable = bitmapDrawable
}
create a singleton class for storing bitmap data
public final class BitmapData {
private static final BitmapData bitmapData = new BitmapData();
private Bitmap bitmap;
private BitmapData() {
}
public static BitmapData getInstance() {
return bitmapData;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
in first activity you can set like this.
BitmapData.getInstance().setBitmap(bitmap);
and in second activity
Bitmap bitmap = BitmapData.getInstance().getBitmap();
Note: check null value
We can also solve this without passing data through intent. Just store the image in the memory and in the next Activity just load the image from that location, which can also avoid app crash from passing large bitmap data.
Note: You need not even pass the location path to the intent, remember the path and use it.
I would like to also post a best practices answer for those looking to do this (but may be asking the wrong question).
Instead of passing a bitmap around (which I presume you downloaded from the network, otherwise, you would already have a file reference to it), I recommend using an image loader such as Universal Image Loader to download an image into an ImageView. You can configure it to then cache the image to disk:
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.considerExifParams(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultOptions)
.build();
ImageLoader.getInstance().init(config);
Now, just pass the image URL in your intent and use the UIL to load the image. In your newly created activity for example, the image will load instantly because it is loading from the cache - even if your image URL has expired since the download.
Bundle b = new Bundle();
b.putSerializable("Image", Bitmap);
mIntent.putExtras(b);
startActivity(mIntent);