I am trying to use a custom icon for marker in a location based android application and its resizing itself in different devices.
I have 4 different sizes (drawable-mdpi,drawable-hdpi,drawable-xhdpi,drawable-xxhdpi) for that icon and paced in different drawable folders.
I am using simple code for using that icon.
Bitmap mIconBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.some_icon);
marker = map.addMarker(new MarkerOptions()
.position(pinOfferLocation)
.title("Some Title")
.icon(BitmapDescriptorFactory
.fromBitmap(mIconBitmap))
.snippet("Some Snippet").anchor(0.5f, 1));
I also tried using :
BitmapDescriptorFactory.fromResources(SOME DRAWABLE RESOURCE)
Another option I tried was to create a scaled bitmap like this:
Bitmap resized = Bitmap.createScaledBitmap(mIconBitmap, 70, 90, true);
But the problem with the above line is only good when your application runs in any high density device and it will be extremely scaled on low density devices.
The issue I am facing that the marker icons are not resizing itself according to different devices.
Is there a better way to use an icon in google maps for different device sizes?
Any help is really appreciated.. thanks
I think I had a similar problem. I used this workaround, even it is not very beautiful.
I loaded the bitmap from asset-folder using this method:
public static Bitmap getBitmapFromAsset(Context context, String strName,
int sampleFactor) {
AssetManager assetManager = context.getAssets();
InputStream istr;
Bitmap bitmap = null;
try {
Options opt = new Options();
opt.inSampleSize = sampleFactor; // IMPORTANT PART
istr = assetManager.open(strName);
bitmap = BitmapFactory.decodeStream(istr, null, opt);
} catch (IOException e) {
return null;
}
return bitmap;
}
The sample factor can be extracted using something like this:
DisplayMetrics metrics = new DisplayMetrics();
// not sure if this is just testing or necessary for metrics
getActivity().getWindowManager().getDefaultDisplay()
.getMetrics(metrics);
// factor (adjust in your app)
int sampleFactorDensity = Math.round(960 / metrics.densityDpi);
currPosMarker = MyTools.getBitmapFromAsset(this.getActivity(),
"marker_azure.png", sampleFactorDensity);
Related
I am want to display Barcode on android. As input I get SVG string. As a SVG library I use AndroidSVG. I used sample code from library website and everything seem to be fine. But when I zoom on image, I get distorted edges (Anti-alias?). I tried to disable all the flags. But the image still has fuzzy edges. What can be wrong with my code?
Picture:
Try to zoom to max, you will see the fuzzy edges.
Code:
private void loadQRCode(String svgString) {
SVG svg = null;
try {
svg = SVG.getFromString(svgString);
} catch (SVGParseException e) {
e.printStackTrace();
}
if (svg.getDocumentWidth() != -1) {
int widthPx = Utils.pxFromDp(400);
int heightDp = Utils.pxFromDp(300);
svg.setDocumentWidth(widthPx);
svg.setDocumentHeight(heightDp);
int width = (int) Math.ceil(svg.getDocumentWidth());
int height = (int) Math.ceil(svg.getDocumentHeight());
Bitmap newBM = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas bmcanvas = new Canvas(newBM);
final DrawFilter filter = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG| Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG, 0);
bmcanvas.setDrawFilter(filter);
barcode.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
bmcanvas.drawRGB(255, 255, 255);
svg.renderToCanvas(bmcanvas);
barcode.setImageBitmap(newBM);
}
}
If the edges of the bars do not lie exactly on pixel boundaries, you will get anti-aliasing. On a high resolution screen, this should not normally be visible.
However, in your code, you are rendering the SVG to a bitmap and setting the bitmap to an ImageView. If that ImageView has a size larger than the bitmap - ie. greater than 400 x 300, then the anti-aliased pixels in that bitmap will likely be rendered larger and thus more visible.
One solution is to avoid using a bitmap. Use a Picture/PictureDrawable instead. That way the barcode will be rendered at highest quality no matter what size it is. As vector graphics are supposed to be.
Follow the example on this page:
http://bigbadaboom.github.io/androidsvg/use_with_ImageView.html
So your code should probably look something like the following:
private void loadQRCode(String svgString) {
try {
SVG svg = SVG.getFromString(svgString);
barcode.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
Drawable drawable = new PictureDrawable(svg.renderToPicture());
barcode.setImageDrawable(drawable);
} catch (SVGParseException e) {
e.printStackTrace();
}
}
If for some reason you need to use bitmaps - maybe you are caching them or something - then you should watch for changes in the size of the ImageView and then recreate the bitmap at the new size. So the bitmap is always the same size as the ImageView to which it is assigned.
The app I'm creating requires a number of images to be pulled from our server, and displayed on a page. The user can go into several different categories, and each will have their own images. The problem is after going to 2-3 categories (depending on how many images are in those categories) in a row, the app has no more memory and cannot display the Bitmaps without crashing.
What I'd like to be able to do is clear the memory every time the user goes to a new category so that the old category's images won't be stored in memory anymore, freeing up space for the relevant category's images. I'm not sure if this is a good way to do it, or even how to do it if it was.
If anyone has a better solution let me know. One idea that was thrown around was loading only ~20 images at once, and waiting until the user scrolls to the bottom before loading more, however since our customers are paying to have their images on the app, that would cause less traffic to certain images, so this is not the ideal solution. However it's not out of the question.
Here is the code I'm using to load the images:
EDIT: My Mistake I posted the wrong code, this is the real code I'm using:
#SuppressWarnings("deprecation")
public Drawable loadImageFromWebOperations(String url, String imagePath) {
try {
if(Global.couponBitmaps.get(imagePath) != null){
scaledHeight = Global.couponBitmaps.get(imagePath).getHeight();
return new BitmapDrawable(getResources(), Global.couponBitmaps.get(imagePath));
}
Drawable d = null;
File f = new File(getBaseContext().getFilesDir().getPath().toString() + "/" + imagePath + ".png");
if (f.exists()) {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
int scaledWidth = 0;
try {
display.getSize(size);
scaledWidth = size.x;
} catch (java.lang.NoSuchMethodError ignore) {
scaledWidth = display.getWidth();
}
Bitmap bitmap = null;
BitmapScaler scaler = new BitmapScaler(f, scaledWidth);
bitmap = scaler.getScaled();
scaledHeight = bitmap.getHeight();
d = new BitmapDrawable(getResources(), bitmap);
Global.couponBitmaps.put(imagePath, bitmap);
} else {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
int scaledWidth = 0;
try {
display.getSize(size);
scaledWidth = size.x;
} catch (java.lang.NoSuchMethodError ignore) {
scaledWidth = display.getWidth();
}
Bitmap bitmap = BitmapFactory.decodeStream((InputStream) new URL(url).getContent());
int height = bitmap.getHeight();
int width = bitmap.getWidth();
scaledHeight = (int) (((scaledWidth * 1.0) / width) * height);
f.getParentFile().mkdirs();
f.createNewFile();
OutputStream output = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, output);
output.close();
bitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false);
d = new BitmapDrawable(getResources(), bitmap);
Global.couponBitmaps.put(imagePath, bitmap);
}
return d;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (OutOfMemoryError e){
e.printStackTrace();
return null;
}
}
If anyone knows if there is a more efficient way of loading the images, or if there is a way to clear the memory before drawing them, it would be greatly appreciated, thank you.
Since you are loading your Bitmaps from a server, you should probably use an image loading library.
Powerful libraries are for example:
Picasso, by square
UniversalImageLoader, by nostra13
They allow you to do pretty much anything with your Bitmap and during loading. Chaching is also supported.
UniversalImageLoader is slightly more powerful, Picasso is easier to use, in my opinion.
Bitmaps in android are a bit tricky.. My first app which required a large number of images in a gridview - I ran into a lot of OOM problems as well.
I ended up using nostra13's "Universal Image Loader" as it seemed to be the best solution for what I needed. It has a lot of built in features such as disk cache, memory cache, bitmap size, thread pool size, image scaling, etc. There are working examples too. :)
Nostra13 Universal Image Loader
There are a few things to keep in mind:
Recycle your bitmaps before displaying the next one else they will
pile up in memory and you'll get OOM
If using Universal Image Loader, make sure you use .bitmapConfig(Bitmap.Config.RGB_565) as it uses the least amount of memory per image.
If you plan on displaying a lot of images in a gridview or listview, the approach I used is to load two different images from your APIs, one being extremely small (100x100 ish) for thumbnail view, and the other being the full size image. This way you wont run out of memory when showing the thumbnails. Then only when the user clicks a thumbnail, will it load the the full size image for that position.
Hopefully this helps. :)
Every time you create new Drawable and BitmapDrawable use one Drawable and refresh image on it.
I make use of the following code to set my static images as wallpaper from my android app.. The image dimensions are like 425*700, 280*180, 600*400 etc., so the image dimensions are not the same.
try {
WallpaperManager myWallpaperManager = WallpaperManager
.getInstance(context);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int fullWidth = size.x;
int fullHeight = size.y;
// int fullWidth = wManager.getDesiredMinimumWidth();
// int fullHeight = wManager.getDesiredMinimumHeight();
Log.d("Debug", Integer.toString(fullWidth));
Log.d("Debug", Integer.toString(fullHeight));
Bitmap bitmap = BitmapFactory.decodeStream(getResources()
.openRawResource(R.drawable.hello));
Bitmap bitmapResized = Bitmap.createScaledBitmap(bitmap, fullWidth,
fullHeight, true);
myWallpaperManager.suggestDesiredDimensions(
bitmapResized.getWidth(), bitmapResized.getHeight());
myWallpaperManager.setBitmap(bitmapResized);
} catch (IOException e) {
e.printStackTrace();
}
But the images are quite streched and doesn't look good after setting as a wallpaper in the phone.. What am I doing wrong?
I guess problem lies in the resources as you have already mentioned. For a good view/quality of wallpaper we have to use different resources of images as according to the resolution of your device, if you want to use your application of setting the wallpaper to be used by many devices without making any changes(an apk). The crux is, you need a better quality image matching the resolution of your device. Hope it helps.
Edit: The distortion is again because you are resizing your image for covering full screen size. I guess if you do not resize it, it might work, though I havent use it till now.
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'm having an odd problem with my map pin sizes. To preserve dynamic-ness, the map pins for different categories are stored on a site's server so that they can be changed at any point even after the app is published.
I'm caching the pins every time I download them and I only ever re-download them if the server sends back a bit saying that one has changed since last I downloaded it. The first time I grab the pins, I use the bitmaps before I save them to files and the map markers are the correct size. Every time after that I'm loading a saved version of the pins straight from the image file. These are displaying considerably smaller than they are when using the bitmaps from the first download.
At first, I thought it was a problem with the way I'm saving the PNGs, but their sizes are correct (64 x 64). Is this a dip/px issue or do I need to decompress the image files with some sort of option?
Here's how I grab the images the first time:
public static Bitmap loadMapPin(String category, int width, int height) {
URL imageUrl;
category = category.toLowerCase().replace(" ", "");
try {
imageUrl = new URL(PIN_URL+category+".png");
InputStream is = (InputStream) imageUrl.getContent();
Options options = new Options();
options.inJustDecodeBounds = true; //Only find the dimensions
//Decode without downloading to find dimensions
BitmapFactory.decodeStream(is, null, options);
boolean scaleByHeight = Math.abs(options.outHeight - height) >= Math.abs(options.outWidth - width);
if(options.outHeight * options.outWidth >= width * height){
// Load, scaling to smallest power of 2 that'll get it <= desired dimensions
double sampleSize = scaleByHeight
? options.outHeight / height
: options.outWidth / width;
options.inSampleSize =
(int)Math.pow(2d, Math.floor(
Math.log(sampleSize)/Math.log(2d)));
}
options.inJustDecodeBounds = false; //Download image this time
is.close();
is = (InputStream) imageUrl.getContent();
Bitmap img = BitmapFactory.decodeStream(is, null, options);
return img;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
And here's how I'm loading them from the cached file:
BitmapFactory.decodeFile(filepath);
Thanks in advance!
I've found that, by default, decompressing an image to a bitmap doesn't scale with high density screens. You have to set the density to none. In other words, you specify that the image is meant for an unknown density.
Solution:
Bitmap b = BitmapFactory.decodeFile(filepath);
b.setDensity(Bitmap.DENSITY_NONE);