I have added an Image View, and set these values.
<ImageView
android:id="#ivNewsHeader
android:layout_width="match_parent"
android:layout_height="250dp"
android:scaleType="centerCrop" />
I get images from server, and they are very high res images, if i add them as is, i end up getting OutOfMemoryError.
So i learnt that picasso offers a great way to tackle this. I used following, and this scale down any image which is higher than the mentioned dimensions.
Picasso.with(getApplicationContext()).load(imageURL).
resize(180, 130).
onlyScaleDown()
.into(ivNews);
Now when i choose these resize(180, 130), the performance is very good and i don't get any OutOfMemoryError, but the image quality is very poor.
So my question is how to choose resize number, and what equation should is use to put correct width-height numbers in resize() method to get the perfect image without compromising the quality and performance.
My imageView height is 250dp and width is match_parent
Specify scale value based on device size
Device width in pixel
public static int getScreenWidth() {
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
Convert DP to Pixel
public static int dpToPx(int dp)
{
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}
Now use these two function
Picasso.with(getApplicationContext()).load(imageURL).
resize(getScreenWidth(), dpToPx(250)).
onlyScaleDown()
.into(ivNews);
I think fit() is what you need, because will crop your image to ImageView size on screen. In this case, you don't need do to any more calculations.
Picasso.with(getApplicationContext())
.load(imageURL)
.fit()
.centerInside()
.into(imageView);
Try using fit() which is measuring the dimensions of the target ImageView and internally uses resize() to reduce the image size to the dimensions of the ImageView.
Quoting from this article
Related
I have been using Picasso from a long time. Today, I am migrating to Glide. In Picasso, I used to use following loading pattern:
Picasso.get()
.load(file)
.resize(targetSize, 0)
.onlyScaleDown()
.placeholder(R.color.default_surface)
.error(R.color.default_surface_error)
.into(imageView)
According to resize(int, int) documentation,
Use 0 as desired dimension to resize keeping aspect ratio
According to onlyScaleDown() documentation,
Only resize an image if the original image size is bigger than the target size specified by resize(int, int)
Here's what I am trying:
Glide.with(imageView)
.log(this, thumbnailUrl?.toString())
.load(thumbnailUrl)
.override(600)
.placeholder(R.color.default_surface)
.error(R.color.default_surface_error)
.into(imageView)
Glide uses a default downsampling strategy when loading images using DownsampleStrategy.CENTER_OUTSIDE. It says that image is upscaled to match the overridden size such that one of the dimension (smallest?) is equal to overridden size. And, following comment:
Scales, maintaining the original aspect ratio, so that one of the image's dimensions is exactly equal to the requested size and the other dimension is greater than or equal to the requested size.
This method will upscale if the requested width and height are greater than the source width and height. To avoid upscaling, use {#link #AT_LEAST}, {#link #AT_MOST}, or {#link #CENTER_INSIDE}.
Options in DownsampleStrategy.java confused me. I don't know which one I should use. I want the large images to scale down to overridden size, and small images to never upscale. How to achieve this in Glide?
I have found an answer in Github Issue #3215 where following is suggested:
Pick a useful DownsampleStrategy, in particular CENTER_INSIDE may be what you're looking for. The default DownsampleStrategy will upscale to maximimize Bitmap re-use, but typically there's an equivalent strategy available that will not upscale.
And, DownsampleStrategy.CENTER_INSIDE fits what I wanted:
Returns the original image if it is smaller than the target, otherwise it will be downscaled maintaining its original aspect ratio, so that one of the image's dimensions is exactly equal to the requested size and the other is less or equal than the requested size. Does not upscale if the requested dimensions are larger than the original dimensions.
I was confused by the documentation for DownsampleStrategy.CENTER_INSIDE in code.
The first screenshot is with Picasso, the second one with Coil (both in latest versions). Any idea why is this happening?
Picasso: fit().centerInside()
Coil: scale(Scale.FILL).crossfade(true) (I tried with FIT also, same results)
ImageView: adjustViewBounds = true; scaleType = CENTER_INSIDE with MATCH_PARENT width and constant height in pixels.
Coil automatically adjusts to the scale type of the ImageView so you don't need to configure the scale.
Picasso does not, and Picasso's .fit().centerInside() is actually not equivalent to ImageView's CENTER_INSIDE but to FIT_CENTER (it will enlarge the image so that at least one dimension matches the ImageView). There is no equivalent to CENTER_INSIDE with Picasso but these are the closest options:
You can simply remove .fit().centerInside() and let the ImageView scale down the image if it's larger, but if the image is very large it will consume a lot of memory (and may fail to load if larger than the max texture size of the device).
You can use .resize(width, height).centerInside().onlyScaleDown() after measuring the size of the ImageView manually.
If you want Coil to resize the image the same way Picasso does with .fit().centerInside(), then just change the scale type of the ImageView to FIT_CENTER.
I spent all day for this issue :(
In my Android application, I download the images with different size from the server. For example, Image A has 500x330 size, Image B has 500x700 size and so on. I also added some text below the images. I am using a placeholder image with 500x400 size.
I can load the images and show placeholder by using Glide:
XML:
<android.support.v7.widget.AppCompatImageView
android:id="#+id/myImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitXY" />
Kotlin:
GlideApp.with(myImage.context)
.load(url)
.skipMemoryCache(false)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(myImage)
Issue:
While the image is loading, the placeholder is shown with 400 height. When finish loading, users will see changes height from 400 to 700 for example. Then users also see that the text below the images are jumping up or down depend on the image size:(
Question:
How can I scale or keep the placeholder the same size with every loading images which I want to show on the UI. So that the UI does not change after loading?
Notes: I don't want to hard code the height of my images static like this layout_height="100dp". They should be kept the same size and radio as they are.
If Glide cannot do that. Do you guys have any other suggestions?
Thank you so much.
Don't know if this issue is still active. I hope it will help you if you set fitCenter() to Glide. This will fit the image into the placeholder and not change the dimensions. Buuut....you will see that there will be some white space if the photo doesn't have the exact aspect ratio as the placeholder image.
If it's not working as you want, you can try also to override the width and the height of the whole imageView by setting the width and the height of the placeholder image.
.override(w, h) // set exact size
.fitCenter() // keep memory usage low by fitting into (w x h) [optional]
where
w = placeholderDrawable width
h = placeholderDrawable height
I really hope this helps.
More details here
I need to load some images from URI to Bitmap variables and perform some operation with them togheter. I need the bitmaps to be squared images with fixes size, scaled down and cropped. By now I use this code:
return Picasso.with(c).load(imageUri).resize(size, size).get();
but, obviously, the image will be resized without keep its aspect ratio.
I want to resize the image with these requirements:
the smaller dimension (width or height) should be equals to size
the greater dimension should be cropped to size, keep image centered
The key is using centerInside after resize. See link
Picasso.with(c).load(imageUri).resize(size, size).centerInside().get()
set your imageview height and width fix inside xml and then set image to imageview like
Picasso.with(YourActivityName.this)
.load(imageUri)
.into(imageview_id);
AFAIK, the inSampleSize attribute of BitmapFactory.options will read a sampled image as per the inSampleSize value. Eg: If value is 4, it will effectively read 1/16 pixels and thus memory required to load it will drastically reduce.
Here in fact, it is maintaining the aspect ratio in the sense that it has skipped 1/4th pixels along height and 1/4th pixels along the width.
When I load this bitmap in a smaller ImageView, aspect ratio is maintained and it looks good. I have used the following formula to derive the inSampleSize = max(Width/reqWidth, height/reqHeight)
size of the imageview = 100dp * 100dp, I have converted 100dp to pixels as per the screen density and used that result as the reqWidth and reqHeight.
(Note: All my images are bigger than the reqWidth and reqHeight)
However If I apply another operation Bitmap.createScaledBitmap() on above reduced version of bitmap, the image gets stretched and does not look good in the View.
I am not able to understand what createScaledBitmap() exactly does?
Given a Bitmap, let's call it bmp1, the create Scaled bitmap method creates a new Bitmap from bmp1 which is upscaled/downscaled to a new size.
However, since you're doing the scaling yourself, perhaps you should simply call createBitmap() instead? That one will respect the new size you tell it to be, and won't scale the original image, which is what you want from what i understood.
Please correct me if I'm wrong, however.