I have a recycler view that has items with variable sizes. Now, I'm loading images using Picasso and I'd like to resize the pictures to fill the ImageView, preserving aspect ratio and by cropping the image so that it fits the ImageView perfectly. However, the width of the ImageView is 0 during the time the view holder is bound. How do I get the size?
The code looks something like this:
#Override
public void onBindViewHolder(ViewHolder holder, final int position)
{
Foo foo = mFoos.get(position);
// Get the size - THIS IS WHAT I WANT TO FIND OUT
int width = ...;
int height = 100;
// Load the image using Picasso
Picasso.with(mContext)
.load(foo.getImageURL())
.centerCrop()
.resize(width, height)
.into(holder.image);
}
That's because view measurement has not jet been made. So at that point width is actually 0.
You need to implement ViewTreeObserver
Take a look at some of these examples:
How to get the width and height of an Image View in android?
How to get the width and height of an android.widget.ImageView?
Related
Into this adapter, I build dynamically the imageview size into the viewholder constructor:
ContentViewHolder(View view, Context context) {
super(view);
ButterKnife.bind(this, view);
// Set image width + height
mImageView.getLayoutParams().width = Math.round(UIUtils.getScreenWidth() / 4f);
mImageView.getLayoutParams().height = Math.round(
mImageView.getLayoutParams().width / Defines.FORM_LIST_FORMAT);
}
To show images, I use Glide (with crossfade effect; I tried without and the result is the same...).
And the result is not good: the first images have a bad image displaying...
Could you give some ways to fix it please?
Add android:scaleType="centerCrop" in your XML layout, or mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP) if you want to do it programmatically.
I am using PhotoView with Picasso and when I load images into ListView
1) It makes some images smaller.
2) not all images are loaded on launch, when i scroll down an then up then all the images are loaded.
Without PhotoView images loads just fine. I tried so many ImageView zooming libraries but PhotoView works better and I have also tried it with Glide the result is same.
Is there anyway I could force PhotoView to fit to ImageView attributes (which are Match_Parent)?
Here is my Adapter
#Override
public void onBindViewHolder( final ViewHolder holder, int position) {
Picasso.with(context)
.load((Integer) data.get(position))
.resize(530,999)
.onlyScaleDown()
.centerInside()
.into(holder.image);
holder.mAttacher = new PhotoViewAttacher(holder.image);
holder.image.setTag(holder);
}
#Override
public int getItemCount() {
return data.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public PhotoView image;
public PhotoViewAttacher mAttacher;
public ViewHolder(View view) {
super(view);
image = (PhotoView) view.findViewById(R.id.imagview);
I am loading images from resources/drawable not from any url
Unfortunately, there is an issue at Picasso, more details at issue #364
I switched to another library Glide
You can change your attacher scale type:
holder.mAttacher = new PhotoViewAttacher(holder.image);
holder.mAttacher.setScaleType(ImageView.ScaleType.CENTER_CROP);
CENTER_CROP: Scale the image uniformly (maintain the image's aspect ratio) so that
both dimensions (width and height) of the image will be equal to or
larger than the corresponding dimension of the view (minus padding).
The image is then centered in the view.
Im trying to find a way to measure an ImageView after an image has been loaded into it using Glide or Picasso (or anything really). Basically, im trying to layout other views on top of the image in certain positions, but need the final ImageViews dimensions to do so accurately.
Im not sure what the best layout to use to try and do this would be, but im currently using this:
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/viewingImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
and loading the image into viewingImageView. These are both inside a root FrameLayout as well but I dont think that matters.
This is my latest attempt, but as commented, using .getWidth() and .getHeight() on the ImageView return 0. The width/height of the resource returns the size of the original image.
Glide
.with(this)
.load(entry.getImageUrl())
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
mImageView.setImageBitmap(resource);
int width = mImageView.getMaxWidth(); //prints 0
int height = mImageView.getMaxHeight(); //prints 0
int resw = resource.getWidth(); //returns original image width
}
});
So how can I get the image loaded and then measure the ImageView (or its wrapping FrameLayout) after the image has been loaded? Or if possible, measuring the dimensions of the finally laid out image would be better, as I know the image doesnt always fill the entire ImageView depending on scale types. I'm open to any solutions, the above is only what ive tried so far.
You should wait for the view to be drawn, you can use OnGlobalLayoutListener like this
ViewTreeObserver vto = mImageView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
this.mImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// Get the width and height
int width = mImageView.getMeasuredWidth();
int height = mImageView.getMeasuredHeight();
}
});
Create a custom class with image view.
Use this onMeasure in the image view we can get the height and width
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mNeedsInitialScale) { // Set by setImage when a new image is set
// The measured width and height were set by super.onMeasure above
setInitialScale(getMeasuredWidth(), getMeasuredHeight());
mNeedsInitialScale = false;
}
}
m using RecyclerView with StaggeredGrid (spanCount is variable). Each GridItem (a CardView) contains an a square ImageView. What i want is to load an Image from my backend with desired width and height to exactly fit an ImageView inside a CardView. Any suggestions i can achieve this? Code example is below:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
Picasso.with(context)
.load(String.format("http://mybackend.com/image/w_%d,h_%d/%s",
holder.image.getMeasuredWidth(), // This returns 0, but i want here a real width
holder.image.getMeasuredWidth(),
myCollection.get(position).getImagePid()))
.tag(context)
.placeholder(R.drawable.placeholder_logo)
.into(holder.image);
In 2 words: somehow i need to get imageView dimensions (width) before my ViewHolder is bound.
Have you looked into using .fit() ? I believe that will resize the image to exactly fit an ImageView.
You can use ViewTreeObserver:
final int width = holder.image.getMeasuredWidth();
if (width > 0) {
Picasso.with(context)
.load(String.format("http://mybackend.com/image/w_%d,h_%d/%s",
holder.image.getMeasuredWidth(), // This returns 0, but i want here a real width
holder.image.getMeasuredWidth(),
myCollection.get(position).getImagePid()))
.tag(context)
.placeholder(R.drawable.placeholder_logo)
.into(holder.image);
} else {
holder.image.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
holder.image.getViewTreeObserver().removeGlobalOnLayoutListener(this);
Picasso.with(context)
.load(String.format("http://mybackend.com/image/w_%d,h_%d/%s",
holder.image.getMeasuredWidth(), // This returns 0, but i want here a real width
holder.image.getMeasuredWidth(),
myCollection.get(position).getImagePid()))
.tag(context)
.placeholder(R.drawable.placeholder_logo)
.into(holder.image);
}
});
}
I have an image which is smaller than the container I would like it to fit inside of. I would like the image to stretch, keeping it's aspect ratio, to it's largest possible size.
To illustrate this problem:
<ImageView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="#drawable/thumbnail"
android:scaleType="fitXY"
android:adjustViewBounds="true"/>
The ImageView above would be stretched to fill the width of the container. The #drawable it contained would also stretch along the x axis to fit the width of ImageView which is perfect. The problem however is that the dimension labelled wrap_content, in this case height, remains the same size as the #drawables initial height.
I have read the documentation regarding ScaleType here and can't find the answer there.
The following image describes the above code:
Current behaviour Desired Behaviour
Edit
An ImageView given scaleType="fitCenter" will accurately expand/shrink the #drawable inside of it to grow as large as possible while retaining it's aspect ratio.
The ImageViews dimensions are defined before the #drawable is scaled in any way. The ImageView dimensions are not effected by scaling of it's contained #drawable.
XML
The only solution to this in XML is to use "match_parent" or a discrete maximum value instead of "wrap_content" where possible. This will ensure the ImageView is the correct size, which will then meaning adding scaleType="fitCenter" will ensure the #drawable will then scale correctly.
Programatically
It's ugly, but you can resize the ImageView after it's dimensions have been given discrete values:
final ImageView thumbnailView = (ImageView) toReturn.findViewById(R.id.thumbnail);
ViewTreeObserver thumbnailViewVto = thumbnailView.getViewTreeObserver();
thumbnailViewVto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
private boolean changed = false;
#Override
public void onGlobalLayout() {
if(!changed) {
Drawable image = getResources().getDrawable(R.drawable.thumbnail);
float heighToWidthRatio = image.getIntrinsicWidth()/image.getIntrinsicHeight();
int height = thumbnailView.getHeight();
thumbnailView.setLayoutParams(
new LayoutParams(
(int) (height * heighToWidthRatio), height));
changed = true;
}
}
});
EDIT
final ImageView thumbnailView = (ImageView) toReturn.findViewById(R.id.thumbnail);
thumbnailView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remove the GlobalOnLayout Listener so it only fires once.
thumbnailView.getViewTreeObserver().removeGlobalOnLayoutListener(this)
// Find the images Height to Width ratio
Drawable image = getResources().getDrawable(R.drawable.thumbnail);
float heighToWidthRatio = image.getIntrinsicWidth()/image.getIntrinsicHeight();
// Use this ratio to discover the ratio of the ImageView to allow it to perfectly contain the image.
int height = thumbnailView.getHeight();
thumbnailView.setLayoutParams(new LayoutParams(
(int) (height * heighToWidthRatio), height));
}
});
Looks like you want fitCenter, which uses Matrix.ScaleToFit CENTER.