Lots been said, written, vented out ;) on this already, I know. But my question is a bit different. I realize that there is issue with using large bitmap objects in Android. In my Android App, I need to use large bitmaps in various places. cannot help it. I tried various things listed in stackoverflow. One that I want to try but not able to is to avoid using ARBG_8888. The App crashes exactly at that line many times. I have an ImageView where I load an initial large bitmap. Then the user can make some markings in the bitmap and then I need to save the whole image (i.e. the original bitmap + the user's markings). Am trying this with this code I got from stackoverflow:
public static Bitmap loadBitmapFromView(Context context, View view) {
Log.d(logtag, "loadBitmapFromView");
Bitmap bitmap;
BitmapFactory.Options options = new BitmapFactory.Options();
// These help in saving/recycling the bitmap memory
options.inPurgeable = true;
options.inInputShareable = true;
Bitmap dummy = null;
try {
dummy = BitmapFactory.decodeStream(context.getAssets().open("icon_add.png"), new Rect(-1,-1,-1,-1), options);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bitmap = Bitmap.createBitmap(view.getLayoutParams().width,
view.getLayoutParams().height, Bitmap.Config.ARGB_8888);
// bitmap = Bitmap.createScaledBitmap( dummy, view.getLayoutParams().width,
// view.getLayoutParams().height, false);
Canvas c = new Canvas(bitmap);
view.layout(0, 0, view.getLayoutParams().width, view.getLayoutParams().height);
view.draw(c);
Log.d(logtag, "loadBitmapFromView: width:" + view.getLayoutParams().width);
c = null;
return bitmap;
}
How do I avoid using ARGB_8888 in the above code? Can someone please help?
I removed it from many other places in the code, but, in the above snippet, am left in vain.
I tried using an initial dummy object (you can see the object 'dummy' in the above code) to create a scaled bitmap object ( with createScaledBitmap() which necessarily asks for a source bitmap ) and then tried to load the canvas, but the generated image is having only the dummy object (icon_add.png) and not the one from the imageview.
you can show image in device specific height width....then you don't get out of memory for imagesize.
this is code:
#SuppressLint("NewApi")
public static int getDeviceWidth(Activity activity) {
int deviceWidth = 0;
Point size = new Point();
WindowManager windowManager = activity.getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
windowManager.getDefaultDisplay().getSize(size);
deviceWidth = size.x;
} else {
Display display = windowManager.getDefaultDisplay();
deviceWidth = display.getWidth();
}
return deviceWidth;
}
#SuppressLint("NewApi")
public static int getDeviceHeight(Activity activity) {
int deviceHeight = 0;
Point size = new Point();
WindowManager windowManager = activity.getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
windowManager.getDefaultDisplay().getSize(size);
deviceHeight = size.y;
} else {
Display display = windowManager.getDefaultDisplay();
deviceHeight = display.getHeight();
}
return deviceHeight;
}
you can use device height and width..and set this image in imageview..
ImageView image = new ImageView(viewHomeScreen);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.FILL_PARENT,
FrameLayout.LayoutParams.FILL_PARENT);
params.height = getDeviceHeight(activity);
params.width = getDevicewidth(activity);
FrameLayout framelayout = new FrameLayout(viewHomeScreen);
framelayout.addView(image, 0, params);
i hope if you use device specific height and width then your image memory issue will be solved.
Related
I am having a dynamic layout where there is a imagebutton named ButtonQ.
I would like to set the dimension of the ButtonQ and then to see if it is correct, I have added a toast.
#Override
protected void onStart()
{
super.onStart();
measure_dimension();
}
private static Point getDisplaySize(final Display display)
{
final Point point = new Point();
try {
display.getSize(point);
} catch (java.lang.NoSuchMethodError ignore) { // Older device
point.x = display.getWidth();
point.y = display.getHeight();
}
return point;
}
public void measure_dimension()
{
Display display = getWindowManager().getDefaultDisplay();
Point size = getDisplaySize(display);
int screen_width = size.x;
int screen_height = size.y;
int target_height = Math.min(screen_height, 600);
int target_width = Math.min(screen_width, 600);
int target_dimension = Math.min(target_height, target_width);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(target_dimension, target_dimension); // width, height
buttonQ.setLayoutParams(layoutParams);
image_height = target_height;
image_width = target_width;
Toast.makeText(Game_middle.this, "buttonQ height:"+ buttonQ.getHeight() +"\nbuttonQ width=" + buttonQ.getWidth(), Toast.LENGTH_LONG).show();
}
Question:
The toast reports the height and the width of ButtonQ are both 0.
Why?
I'd use the analogue way you're setting your height and width: via getLayoutParams(). If you want to set the params dynamically, I wouldn't use pixels directly but the dp unit, using TypedValue.COMPLEX_UNIT_DIP.
Regarding to synchronization, in that example I'd definitely use asynchronous mechanisms, such as CountDownLatch or Semaphore mechanisms. It's quite more secure this way than using temporary timers. You may want to check the CountDownLatch(1) example in the reference page for that purpose.
Try this to get the button dimensions where popLayout is the button name in your case
popupLayout.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
width = popupLayout.getMeasuredWidth();
height = popupLayout.getMeasuredHeight();
Hey I need to get the width of the screen in my application. The application will run on 2.1 and upwards. I have set it up like the one below. The method is deprecated and i should proabably use getSize or a other way. But the question is: Will this work on android versions like 3.0+ and 4.0+, or will it make the app crash. I have used a deprecated method in a thread before and it made the app crash on ice cream devices. Will the method below work ?
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
EDIT:
I have tried the getSize but i dont get it to work:
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
I am not sure but this may work:
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
} else {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
}
I don't know whether these deprecated methods will work on Android 3 and 4. The best way to tell is to test on an emulator.
But, the safest method here for max compatibility will be to try one method using reflection, and fall back to the other. Essentially, you could make your own version of getSize() that can't fail. I can't test this atm, but it might look like this:
void overrideGetSize(Display display, Point outSize) {
try {
// test for new method to trigger exception
Class pointClass = Class.forName("android.graphics.Point");
Method newGetSize = Display.class.getMethod("getSize", new Class[]{ pointClass });
// no exception, so new method is available, just use it
newGetSize.invoke(display, outSize);
} catch(NoSuchMethodException ex) {
// new method is not available, use the old ones
outSize.x = display.getWidth();
outSize.y = display.getHeight();
}
}
Then of course just call it with something like
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
overrideGetSize(display, size);
I've extended Steve's helpful code so that Eclipse doesn't give any warnings or errors, and I've also restructured it slightly. Since the Point class has been present since API level 1 I didn't see much benefit in creating it through reflection.
final static String mTAG = "MYTAG";
// Cope with deprecated getWidth() and getHeight() methods
Point getSize(Display xiDisplay)
{
Point outSize = new Point();
boolean sizeFound = false;
try
{
// Test if the new getSize() method is available
Method newGetSize =
Display.class.getMethod("getSize", new Class[] { Point.class });
// No exception, so the new method is available
Log.d(mTAG, "Use getSize to find screen size");
newGetSize.invoke(xiDisplay, outSize);
sizeFound = true;
Log.d(mTAG, "Screen size is " + outSize.x + " x " + outSize.y);
}
catch (NoSuchMethodException ex)
{
// This is the failure I expect when the deprecated APIs are not available
Log.d(mTAG, "getSize not available - NoSuchMethodException");
}
catch (InvocationTargetException e)
{
Log.w(mTAG, "getSize not available - InvocationTargetException");
}
catch (IllegalArgumentException e)
{
Log.w(mTAG, "getSize not available - IllegalArgumentException");
}
catch (IllegalAccessException e)
{
Log.w(mTAG, "getSize not available - IllegalAccessException");
}
if (!sizeFound)
{
Log.i(mTAG, "Used deprecated methods as getSize not available");
outSize = new Point(xiDisplay.getWidth(), xiDisplay.getHeight());
}
return outSize;
}
According to https://stackoverflow.com/a/1016941/2914140:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width = metrics.widthPixels;
int height = metrics.heightPixels;
What is wrong with Display's new function, getSize()? It'd be really easy to turn the Point object into the width and height values you need.
So how do I write code to accommodate for this? I don't want to leave deprecated API calls in my code, but I also don't want to lose the users with (slightly) older devices. Is there some sort of compatibility setting I can implement?
Rel. code
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int screen_width = size.x;
int screen_height = size.y;
vs. the older method:
int screen_width = getWindowManager().getDefaultDisplay().getWidth();
int screen_height = getWindowManager().getDefaultDisplay().getHeight();
i have two functions, sending the context and gettin height and width in pixels.
public static int getWidth(Context mContext){
int width=0;
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.HONEYCOMB){
Point size = new Point();
display.getSize(size);
width = size.x;
}
else{
width = display.getWidth(); // deprecated
}
return width;
}
and
public static int getHeight(Context mContext){
int height=0;
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.HONEYCOMB){
Point size = new Point();
display.getSize(size);
height = size.y;
}else{
height = display.getHeight(); // deprecated
}
return height;
}
The best (and by best, I mean the option that would work pretty much every time) option would be to use reflection. Check out the Android Backwards Compatibility Backwards Compatibility guidelines (updated with new location of the article on reflection).
While tyczj's answer will work perfectly so long as the deprecated functions are still in the SDK, as soon as they are removed you will have no way of using them or running your app on an older device if you still want to build against the latest SDK.
Reflection solves this problem by effectively dynamically detecting the function at runtime, which means that even if you build against ICS, so long as the minSdkVersion is correct, you can have your app run on a device with Gingerbread, or Froyo for example.
you can do something like this
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH){
//do stuff pertaining to this version here
}else{
//other versions
}
I think sort of thing RivieraKid is suggesting, would be something like this:
static Point getDisplaySize(Display d)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
return getDisplaySizeGE11(d);
}
return getDisplaySizeLT11(d);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
static Point getDisplaySizeGE11(Display d)
{
Point p = new Point(0, 0);
d.getSize(p);
return p;
}
static Point getDisplaySizeLT11(Display d)
{
try
{
Method getWidth = Display.class.getMethod("getWidth", new Class[] {});
Method getHeight = Display.class.getMethod("getHeight", new Class[] {});
return new Point(((Integer) getWidth.invoke(d, (Object[]) null)).intValue(), ((Integer) getHeight.invoke(d, (Object[]) null)).intValue());
}
catch (NoSuchMethodException e2) // None of these exceptions should ever occur.
{
return new Point(-1, -1);
}
catch (IllegalArgumentException e2)
{
return new Point(-2, -2);
}
catch (IllegalAccessException e2)
{
return new Point(-3, -3);
}
catch (InvocationTargetException e2)
{
return new Point(-4, -4);
}
}
I usually have a super class ie. BaseActivity with a generic method for getting a point with the current screen size. Keeps everything nice and clean in the actual activity.
/**
* Return screen size as a point.
* #return
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
#SuppressWarnings("deprecation")
protected Point getSize() {
final Point point = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
getWindowManager().getDefaultDisplay().getSize(point);
}
else {
final Display display = getWindowManager().getDefaultDisplay();
point.x = display.getWidth();
point.y = display.getHeight();
}
return point;
}
If I have the minimum target SDK for my app set to 4 then all calls to Drawable.createFromStream resize images.
e.g. if the source image is 480px wide and the Android device my app is running on has a density of 1.5 then the following code returns a BitmapDrawable with a width of 320
URL url = new URL("http://www.examples.com/something.png");
Drawable d = Drawable.createFromStream(url.openStream(), "something.png");
Is there a method to force it to be unchanged or specify the scale (ldpi/mdpi/hdpi etC) to return an image from an InputStream?
Edit: Solution from below.
Bitmap b = BitmapFactory.decodeStream(inputStream);
b.setDensity(Bitmap.DENSITY_NONE);
Drawable d = new BitmapDrawable(b);
You can load image as Bitmap. This way its size will be unchanged.
BitmapFactory.decodeStream(...)
Or you can try BitmapDrawable(InputStream is) constructor. It is deprecated but I guess it should do the job.
Loading images from local resource in ImageView Adapter
I was stuck with OOM error in ion by Koushik Dutta, for as low as 2 images, loading from the resource folder. Even he mentioned it may happen since they are not streams of data. and suggested to load them as InputStream.
I chucked ion Library since i wanted just 4 images from the local.
here is the solution with simple ImageView.
inside the RecyclerViewAdapter/ or any where
InputStream inputStream = mContext.getResources().openRawResource(R.drawable.your_id);
Bitmap b = BitmapFactory.decodeStream(inputStream);
b.setDensity(Bitmap.DENSITY_NONE);
Drawable d = new BitmapDrawable(b);
holder.mImageView.setImageDrawable(d);
Although Drawable d = new BitmapDrawable(b); is deprecated but, does the job quite well.
so instead we can directly get drawable from InputStream just by this line..
Drawable d = Drawable.createFromStream(inputStream,"src");
Hope it helps.
After a long time, I came across the following solution. It works. It retrieves creates bitmap from file at locallink.
private Drawable getDrawableForStore(String localLink) {
Bitmap thumbnail = null;
try {
File filePath = this.getFileStreamPath(localLink);
FileInputStream fi = new FileInputStream(filePath);
thumbnail = BitmapFactory.decodeStream(fi);
} catch (Exception ex) {
Log.e("getThumbnail() on internal storage", ex.getMessage());
return null;
}
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
float scaledDensity = metrics.density;
int width = thumbnail.getWidth();
int height = thumbnail.getHeight();
if(scaledDensity<1){
width = (int) (width *scaledDensity);
height = (int) (height *scaledDensity);
}else{
width = (int) (width +width *(scaledDensity-1));
height = (int) (height +height *(scaledDensity-1));
}
thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true);
Drawable d = new BitmapDrawable(getResources(),thumbnail);
return d;
}
I have two png image files that I would like my android app to combine programmatically into one png image file and am wondering if it is possible to do so? if so, what I would like to do is just overlay them on each other to create one file.
the idea behind this is that I have a handful of png files, some with a portion of the image on the left with the rest transparent and the others with an image on the right and the rest transparent. and based on user input it will combine the two to make one file to display. (and i cant just display the two images side by side, they need to be one file)
is this possible to do programmatically in android and how so?
I've been trying to figure this out for a little while now.
Here's (essentially) the code I used to make it work.
// Get your images from their files
Bitmap bottomImage = BitmapFactory.decodeFile("myFirstPNG.png");
Bitmap topImage = BitmapFactory.decodeFile("myOtherPNG.png");
// As described by Steve Pomeroy in a previous comment,
// use the canvas to combine them.
// Start with the first in the constructor..
Canvas comboImage = new Canvas(bottomImage);
// Then draw the second on top of that
comboImage.drawBitmap(topImage, 0f, 0f, null);
// comboImage is now a composite of the two.
// To write the file out to the SDCard:
OutputStream os = null;
try {
os = new FileOutputStream("/sdcard/DCIM/Camera/" + "myNewFileName.png");
comboImage.compress(CompressFormat.PNG, 50, os)
} catch(IOException e) {
e.printStackTrace();
}
EDIT :
there was a typo,
So, I've changed
image.compress(CompressFormat.PNG, 50, os)
to
bottomImage.compress(CompressFormat.PNG, 50, os)
You can do blending. This is not particular to Android. It's just universal image processing.
EDIT:
You may find these articles & samples & code useful:
http://www.jhlabs.com/ip/
http://kfb-android.blogspot.com/2009/04/image-processing-in-android.html
http://code.google.com/p/jjil/
Image Processing on Android
I use this code
private class PhotoComposition extends AsyncTask<Object, Void, Boolean> {
private String pathSave;//path save combined images
#Override
protected Boolean doInBackground(Object... objects) {
List<String> images = (List<String>) objects[0]; //lsit of path iamges
pathSave = (String) objects[1];//path save combined images
if (images.size() == 0) {
return false;
}
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < images.size(); i++) {
bitmaps.add(BitmapFactory.decodeFile( images.get(i)));
}
int width = findWidth(bitmaps);//Find the width of the composite image
int height = findMaxHeight(bitmaps);//Find the height of the composite image
Bitmap combineBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//create bitmap of composite image
combineBitmap.eraseColor(Color.parseColor("#00000000")); //bcakgraound color of composite image
Bitmap mutableCombineBitmap = combineBitmap.copy(Bitmap.Config.ARGB_8888, true);//create mutable bitmap to create canvas
Canvas canvas = new Canvas(mutableCombineBitmap);// create canvas to add bitmaps
float left = 0f;
for (int i = 0; i < bitmaps.size(); i++) {
canvas.drawBitmap(bitmaps.get(i), left, 0f, null);//Taking photos horizontally
left += bitmaps.get(i).getWidth();//Take right to the size of the previous photo
}
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(pathSave);//path of save composite image
mutableCombineBitmap.compress(Bitmap.CompressFormat.PNG, 80, outputStream);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
#Override
protected void onPostExecute(Boolean isSave) {
if (isSave) {
//iamge save on pathSave
Log.i("PhotoComposition", "onPostExecute: " + pathSave);
}
super.onPostExecute(isSave);
}
private int findMaxHeight(List<Bitmap> bitmaps) {
int maxHeight = Integer.MIN_VALUE;
for (int i = 0; i < bitmaps.size(); i++) {
if (bitmaps.get(i).getHeight() > maxHeight) {
maxHeight = bitmaps.get(i).getHeight();
}
}
return maxHeight;
}
private int findWidth(List<Bitmap> bitmaps) {
int width = 0;
for (int i = 0; i < bitmaps.size(); i++) {
width += bitmaps.get(i).getWidth();
}
return width;
}
USAGE
List<String> images = new ArrayList<>();
images.add("/storage/emulated/0/imageOne.png");//path of image in storage
images.add("/storage/emulated/0/imageTwo.png");
// images.add("/storage/emulated/0/imageThree");
// ... //add more images
String pathSaveCombinedImage = "/storage/emulated/0/CombinedImage.png";//path save result image
new PhotoComposition().execute(images, pathSaveCombinedImage);
And the result of using the above code will be as follows
You may wish to look into the Canvas object, which would make it easy to do other drawing operations as well. You can just draw your bitmaps onto a canvas where you want them, then save the resulting bitmap.
If they have transparent sections, then if you draw one on top of the other, only the non-transparent portions will overlap. It will be up to you to arrange the bitmaps however you like.
For the separate issue of re-saving your image to a png, use bitmap.compress().
Try this .
public Bitmap mergeBitmap(Bitmap frame, Bitmap img){
Bitmap bmOverlay = Bitmap.createBitmap(frame.getWidth(), frame.getHeight(), frame.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(img, 0, 0, null);
canvas.drawBitmap(frame, new Matrix(), null);
return bmOverlay;
}
Returns a bitmap image
Pass two bitmap images to your function as shown below
Bitmap img= mergeBitmap(imgone, imagetwo);
See the entire post or also see merge multiple images in android programmatically