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;
}
}
Related
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?
I'm switching from Picasso to Glide in my app. I have a RecyclerView, each of whose items is a CustomView. This CustomView adds TextView or ImageView dynamically based on different data sources.
A FillWidthImageView is used to display these images, it fill the screen width automatically. When I use Picasso, it works well. However, when I use Glide to load images, the image displayed in the FillWidthImageView looks like a mosaic with extremely low resolution, because it's heavily downsampled.
And if I use a normal ImageView instead, size of loaded image is the same as the placeholder, and images still look like a mosaic. If there is no placeholder, the images look tiny on the screen. And if I display the images in an empty Activity, they will be loaded with full resolution and correct size.
Code of adding ImageView in my CustomView(extends LinearLayout), called in RecyclerView.onCreateViewHolder()
public void addImageView() {
ImageView imageView = new FillWidthImageView(getContext());
imageView.setPadding(0, 0, 0, 10);
imageView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
addView(imageView);
}
Code of FillWidthImageView
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable drawable = getDrawable();
if (drawable != null) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int diw = drawable.getIntrinsicWidth();
if (diw > 0) {
int height = width * drawable.getIntrinsicHeight() / diw;
setMeasuredDimension(width, height);
} else
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Code of loading images, called in RecyclerView.onBindItemViewHolder()
Glide.with(getContext()).load(url).into(imageView);
Thanks #Hitesh Kr Sahu, but I have solved of the bug by myself. It's because the Glide didn't get the right dimensions of my CustomView wrapper. I solved this bug by applying the dimensions by calling .override(int,int) and .fitCenter(). I passed the width and height of screen to it, so it will be scaled fitting the screen size.
Image loaded by Glide has the worse quality compared to Picasso because Glide's default Bitmap Format is set to RGB_565 & it consumed just 50% memory footprint compared to ARGB_8888.
That make Glide faster than Picasso as you can see in this video.
Good news is you can switch Bitmap Format to ARGB_8888 by creating a new class which extended from GlideModule like this :-
public class GlideConfiguration implements GlideModule {
#Override
public void applyOptions(Context context, GlideBuilder builder) {
// Apply options to the builder here.
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
#Override
public void registerComponents(Context context, Glide glide) {
// register ModelLoaders here.
}
}
and adding metadata field in manifest
<meta-data android:name="com.inthecheesefactory.lab.glidepicasso.GlideConfiguration"
android:value="GlideModule"/>
You can read more about it here glide Configuration page of Glide's wiki and as you have switched from Picasso you will find this an interesting read
I have an action bar, text view and an imageview in my layout. Imageview has widht and height match_parent. Imageview takes the rest of the space left in the view. I want to set a bitmap to the view but that is too huge. So I first want to get the width and height of the imageview and use them to scale down the bitmap and then set it the imageview. I have tried getting width and height of the imageview in OnCreate, but that is coming as 0,0 probably because imageview has not been created yet.
Please suggest how to do it ?
Thanks.
you need to use the ViewTreeObserver onPreDrawListener, this gets called after everything is measured and right before it is drawn
ImageView iv = (ImageView)findViewBYId(R.id.imageview);
ViewTreeObserver observer = iv.getViewTreeObserver()
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw(){
observer.removeOnPreDrawListener(this);
//do your stuff here
}
}
If you're only interested in layout changes of the particular ImageView, then a layout listener on it might be the way to go:
imageView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
// do stuff
}
});
Have you tried this link?
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap
There is basically such a method especially used for preventing memory problems because of large bitmaps. Your first read the dimensions and then laod the scaled sample.
I am trying to scale and rotate an image. And as the method Bitmap.createScaledBitmap() demands to specify the desired width and height, I want to set the desired width and the desired height to be exactly the same as my imageView's width and height.
Now, How can I get the width and height of any given imageView?
given that the imageView initially is empty, but just defined with findViewById()
You must wait until the view is laid out to have dimensions. You can achieve this with a ViewTreeObserver:
final ImageView view = ...;
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
public void onGlobalLayout()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
getViewTreeObserver().removeOnGlobalLayoutListener(this);
else
getViewTreeObserver().removeGlobalOnLayoutListener(this);
// width and height are available here.
}
});
Another option is to have your own class derived from ImageView and override onLayout()
An ImageView gets an image and wraps the content so if the image has dimensions like 100x100, the ImageView has the same dimensions. I want to specify only the layout_height of this ImageView and when it matches the image to re-size properly the layout_width of the ImageView.
Here is an example:
image size = 100x100;
ImageView's layout_height = 200;
ImageView's layout_width should become 200 not 100.
Is this possible?
You'd need to create a custom ImageView and override onMeasure to make the view always square.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
SQUARE_DIMENSION = this.getMeasuredHeight(); // whatever height you want here
this.setMeasuredDimension(SQUARE_DIMENSION, SQUARE_DIMENSION);
}