I'm trying to scale an image in an ImageView on Android, but no matter what settings I try, the scaled-down image always looks ugly. Here is an image produced by my code:
http://i.imgur.com/4yubR.png?1
The image on the left is pre-scaled using ImageMagick on my desktop computer. ImageView has no problem showing this image. The image on the right is the one that is actually scaled by the ImageView, and as you can clearly see, it looks quite ugly :/
The code that produces both of the above images is as follows:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
RelativeLayout body = new RelativeLayout(this);
for(int i = 0; i < 2; ++i)
{
ImageView iv;
iv = new ImageView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(34, 34);
params.leftMargin = i*50;
body.addView(iv, params);
AssetManager am = this.getAssets();
try
{
BufferedInputStream buf;
if(i == 0)
buf = new BufferedInputStream(am.open("images/search_34x34.png"));
else
buf = new BufferedInputStream(am.open("images/search.png"));
Bitmap bitmap = BitmapFactory.decodeStream(buf);
BitmapDrawable bd = new BitmapDrawable(bitmap);
bd.setAntiAlias(true);
iv.setImageDrawable(bd);
buf.close();
}
catch(java.io.IOException e)
{
}
}
setContentView(body);
}
Please tell me if I am missing something. I just cannot believe that Android scales images this badly (actually, I find it hard to believe that this is the default behavior).
Also note that I do NOT want to pre-scale the images, as my app has to deal with images from unknown origin, so I have to scale on the fly.
I don't see where you are scaling down your bitmap. I think you are trying to jam the high resolution bitmap in to a ImageView with smaller dimensions.
Here is a document on scaling the bitmaps. The technique described works well with scaling the bitmaps on the fly also.
Maybe You have to set the scaleType inside Your xml Layout. For example
<ImageView
android:scaleType="center"
</ImageView>
There are following scaleTypes:
CENTER
CENTER_CROP
CENTER_INSIDE
FIT_CENTER
FIT_END
FIT_START
FIT_XY
MATRIX
You could see about here:
http://developer.android.com/reference/android/widget/ImageView.ScaleType.html
EDIT
Tried some stuff and now I get the same result even with other pics. I recognized, that when I am scaling down an image inside a mobile phone, all looks good. I used for example an imageView with 120x120 dp and a resource with 300x300px. If I use the same imageView on an tablet, even 120x120, and the same resource image with the same size, it looks very bad. I think the only way to get rid of this is to scale the image outside your app to the needed sizes before setting it to Your project. For Example, if You use an imageView with 200x200dp, scale the image near to this size. If You support multiple screen sizes, and you set the sizes of this imageView for a tablet to 500x500dp, scale Your image near to this size. Sure, this only will work good, if You got an Image that has a higher resolution than the biggest imageView.
I Know this no satisfying answer, but this is the only way I see for this problem.
Related
I am trying to stretch images that I load with volley. XML isn't much help, while it can shrink them it doesn't help enlarging them. My exploration of the topic led me to the conclusion that this can be achieved only programmatically.
What is my idea ?
To extend the volley library and overriding one of the methods, resizing the bitmap right after download and before displaying it.
I used this code to resize images that were already on the phone, but this doesn't help me much with random images from the internet.
Point outSize=new Point();
float destWidth=1;
float destHeight=1;
#SuppressWarnings("deprecation")
#TargetApi(13)
private Bitmap scaleBitmap(int mFile){
Display display = getActivity().getWindowManager().getDefaultDisplay();
if (android.os.Build.VERSION.SDK_INT >= 13){
display.getSize(outSize);
destWidth = outSize.x;
destHeight = outSize.y;
}else{
destWidth=display.getWidth();
destWidth=display.getHeight();
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap orig = BitmapFactory.decodeResource(getResources(), mFile);
float srcWidth = orig.getWidth();
float srcHeight = orig.getHeight();
float inSampleSize = 1;
if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
inSampleSize=(destHeight/2)/srcHeight;
}else{
inSampleSize=(destHeight/4)/srcHeight;}
options = new BitmapFactory.Options();
Bitmap resized=Bitmap.createScaledBitmap(orig, (int)Math.round(orig.getWidth()*inSampleSize), (int)Math.round(orig.getHeight()*inSampleSize), true);
destWidth=1;
destHeight=1;
return resized;
}
Basically I want to assign to orig the image that is downloaded, then resize it and then display it.
My question is: What class do I extend ? I took a look there but since I am inexperienced I couldn't figure out what exactly to look for. Is it ImageLoader ? And more specifically: should i Override the getBitmap() method and add an edited version of the code for scaling ?
Disclaimer: I am very inexperienced and I would accept other ideas too.
I solved it a few days ago. Here is how the regular ImageView handles resizing of images: If you are using Match_parent as width and wrap_content as height(I tested with both as match_parent and the results were the same) it will NOT scale the image IF the Height of your picture is lower than the width (which is quite typical) . Meaning that image view scales based on the smaller side. This meant that there are 2 solutions:
Do what I wanted to do originally which involves writing a new class that extends imageview/networkimageview and then programmatically adding the code that i listed to enlarge the picture.
Or simply specify the desired Height of the view programmatically and imageview will then simply scale the image to fit the height of the new View. This options invalidates the wrap_content for Width and simply puts in as much as you need.
Here is the code I used:
Display display = getActivity().getWindowManager().getDefaultDisplay();
Point outSize=new Point();
int destWidth;
if (android.os.Build.VERSION.SDK_INT >= 13){
display.getSize(outSize);
destWidth = outSize.x;
}else{
destWidth=display.getWidth();
}
img.setLayoutParams(new RelativeLayout.LayoutParams( LayoutParams.MATCH_PARENT,destWidth*5625/10000));
In my case I needed to fit a 640x360 image so I used a ratio of 0,5625. This method will work if you do not know the picture's parameters BUT you have to get the parameters beforehand and put them in place, so I guess it might be more useful to use the first strategy, but since I solved my problem I don't have an example for the first method.
I think the issue that you're seeing is caused by the default behavior of how the underlying code for NetworkImageView works.
Have a look at the code for ImageRequest which is used by NetworkImageView . There is a function there called getResizedDimension which says something like "Scales one side of a rectangle to fit aspect ratio" . This may have caused your issue since the image(s) that you're downloading may not have the perfect aspect ratio computed every time for a specific device.
Are you displaying a single image, or images in a grid? If it is just a single image, there might be a work around. I haven't seen your full code, on where you set the image or image url, so I am going to assume the flow here:
1.) Use the regular ImageView.
2.) Use the regular Volley request pointing to the image's URL
3.) When you receive the response (parseNetworkResponse), decode (the array of bytes) it yourself, using the method you mentioned above that works for you. Some tips can also be found here on how to do that: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
4.) After decoding, programmatically set the ImageView's bitmap with your decoded result
If this is a grid type thing, then maybe you need to make your own version of NetworkImageView and its underlying support classes (sub-class).
Suggestion:
Have a look at Picasso as well, IF you have the freedom to choose the library. Looks very simple to use for your stated needs.
I am new in android development. I have an issue regarding the image scaling in my app. When i set images using setImageDrawable() images look proper like in screenshot:
But if i set image using setImageBitmap() method it does not look proper. Some black color occurs around it like in screenshot bellow:
I have tried it in multiple applications and i observed that in some applications the image size is becoming smaller than the size ofter setting image through setImageDrawable() or setImageResource().
the reason for setting images through setImageBitmap() is that i want to set images to the imageviews which are kept in assets folder and not in drawable.
Following is the code i used to create a bitmap and set it to imageview:
BitmapFactory.Options opts = new Options();
opts.inPurgeable = true;
try
{
Bitmap preview_bitmap = BitmapFactory.decodeStream(assetManager.open(drawableFolder+"/"+tmpArr[0]),null,opts);
view.setImageBitmap(preview_bitmap);
}
catch (Exception e)
{
LNLog.writeTOErrorToLog(e.getMessage());
}
So, please can anyone tell me where i'm i going wrong or missing something to set.
I tried to set Options but did not worked. I want to set images as in first screenshot that is how they looks by using setImageDrawable().
Thanx in advance.
Note: in screenshots images are referent in text on it only.IF i set hindi images i.e. the images on which text is in hindi language using setImageDrawable() then they looks proper as in like in screen shot one.So , no issue of image files.
You can get rid of the black borders by using
android:adjustViewBounds="true" in xml
or
imageView.setAdjustViewBounds(true) in Java.
EDIT:
Then try using drawable only
Drawable d = Drawable.createFromStream(assetManager.open(filePath), null);
view.setImageDrawable(d)
This worked for me :
try
{
view.setAdjustViewBounds(true);
int height = view.getDrawable().getIntrinsicHeight();
int width = view.getDrawable().getIntrinsicWidth();
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPurgeable = true;
opts.inScaled = true;
Bitmap preview_bitmap = BitmapFactory.decodeStream(assetManager.open("drawable-mdpi/FAQ_hi_in.png",null,opts);
Bitmap bmp = Bitmap.createScaledBitmap(preview_bitmap, width, height, true);
view.setImageBitmap(bmp);
}
In this code I have taken the height and width of actual image and then scaled my bitmap to that height and width. Then I converted this scaled bitmap to drawable and set it into my ImageView, thats it. It resolved my problem of black border around image.
I have this code:
<ImageView
android:id="#+id/listitem_logo"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
and
imageview_logo.setImageBitmap(bit); // comes from assets
imageview_logo.setAdjustViewBounds(true);
imageview_logo.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageview_logo.setBackgroundColor(0x00000000);
imageview_logo.setPadding(0, 0, 0, 0);
imageview_logo.setVisibility(v.VISIBLE);
When loading the image this way, no scaling seems to have been done. However, if I load an image through setImageDrawable() r.res. the image inside the ImageView is resized. However, I need to use setImageBitmap() since I load my images through assets folder.
Am I missing some setting here? That will make Android resize and scale the bitmap, so it uses the full width of the ImageView? I guess I can do it myself in code, but offhand I would think what I want to do would be supported just by setting some properties.
Can't trust system for everything specially android which behaves very differently sometimes.
You can resize the bitmap.
height = (Screenwidth*originalHeight)/originalWidth;
this will generate the appropriate height.
width is equal to screen width as you mentioned.
Bitmap pq=Bitmap.createScaledBitmap(pq,Screenwidth,height, true);
As I needed a bit of time to figure out the complete code to make this work, I am posting a full example here relying on the previous answer:
ImageView bikeImage = (ImageView) view.findViewById(R.id.bikeImage);
AssetLoader assetLoader = new AssetLoader(getActivity());
Bitmap bitmap = assetLoader.getBitmapFromAssets(
Constants.BIKES_FOLDER + "/" + bike.getName().toLowerCase()
.replace(' ', '_').replace('-', '_') + ".png");
int width = getActivity().getResources().getDisplayMetrics().widthPixels;
int height = (width*bitmap.getHeight())/bitmap.getWidth();
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
bikeImage.setImageBitmap(bitmap);
With bit as your Bitmap image, you can directly set yout imageView and then crop it using the properties of the imageView as:
imageview_logo.setImageBitmap(bit);
imageview_logo.setScaleType(ImageView.ScaleType.CENTER_CROP);
I'm downloading a bitmap from internet, then I cast this bitmap as a Drawable to set it as a background in my LinearLayout.
I see that unfortunately the image is scaled to fill the view, how can I scale the image until the smallest size reach the border (so it's not stretched)?
(And possibly centered)
I've tried something like this but without any success.
Am I on the way?
LinearLayout layout = (LinearLayout) context.findViewById(R.id.main);
Drawable picture = new BitmapDrawable(context.getResources(), bitmap);
Display display = context.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
picture.setBounds(0, 0, size.x, size.y);
layout.setBackgroundDrawable(picture);
I think you need something like this?
setScaleType(ScaleType.CENTER_CROP);
See http://developer.android.com/reference/android/widget/ImageView.ScaleType.html
I think,It is not advisable use scaled images on an app. It's a best practice use a repeated pattern instead. So, I would try something like that:
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/background-image-repeatable-pattern.png"
android:tileMode="repeat" />
Save this to drawable/background.xml file (for example)
I updated my build to build against Android 1.6, and now my bitmaps are scaled down on high density screens. I do NOT want this behavior. I gave this a shot:
http://blog.tomgibara.com/post/190539066/android-unscaled-bitmaps
but the images are STILL scaling, that is UNLESS I set them to a specific height & width. If I use wrap_content they are scaled down.
I have an image loader using the unscaled bitmap loader to create a drawable like so:
Bitmap bm = UnscaledBitmapLoader.loadFromResource(imageBufferInputStream);
drawable = new BitmapDrawable(bm);
which I later assign to an ImageView like so:
imageView.setImageDrawable( copyBitmapDrawable( (BitmapDrawable) drawable) );
In order to have an image not scaled when loading it from a resource (e.g. with BitmapFactory.decodeResource) you have to locate it in res/drawable-nodpi instead of the usual drawable, drawable-ldpi and so on.
Using
new BitmapDrawable(this.getResources(), bmp);
instead of
new BitmapDrawable(bmp);
should solve the issue.
Wrapping the bitmap with a Drawable is the problem. The Drawable scales the Bitmap. Instead of using a Drawable, assign the Bitmap to the ImageView directly using imageView.setImageBitmap(Bitmap).
Use ImageView.ScaleType. The CENTER constant preforms no scaling, so I guess that's what you are looking for.
By the way, do you use pixels or dips as size units? Using dips (density-independent-pixels) is a means of standardizing display on multiple resolutions. You'll find a couple of tips here.