PhotoView with Picasso: Some images appears smaller on listview - android

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.

Related

Picasso load images with horizontal lines

I have used Picasso for loading images in my Gallery page with Recyclerview and load images from Server. It works great on all devices good,except Samsung S4 device.In S4 device the images are corrupted and it displays some horizontal lines on all images.What is the problem in my code.Also I think on same S4 device it works fine before updating my supports library versions from 22.2.1 to 23.3.0.
GalleryAdapter.java
#Override
public void onBindViewHolder(GalleryViewHolder galleryViewHolder, final int position) {
// Load images from Assets using Picasso library.
Picasso picasso = Picasso.with(context);
//picasso.setLoggingEnabled(true);
//picasso.setIndicatorsEnabled(true);
if(AppUtilities.isNetworkConnected(context)){
picasso.load(imagesArrayList.get(position).getUrl())
.placeholder(android.R.color.darker_gray)
.fit()
.centerCrop()
.memoryPolicy(MemoryPolicy.NO_CACHE)
.networkPolicy(NetworkPolicy.NO_CACHE)
.into(galleryViewHolder.galleryImageView);
}
else{
picasso.load(imagesArrayList.get(position).getUrl())
.placeholder(android.R.color.darker_gray)
.fit()
.centerCrop()
.into(galleryViewHolder.galleryImageView);
}
}
public class GalleryViewHolder extends RecyclerView.ViewHolder {
protected ImageView galleryImageView;
public GalleryViewHolder(View itemView) {
super(itemView);
this.galleryImageView = (ImageView) itemView.findViewById(R.id.gallery_imageview);
}
}
Try this, before setting the image just set it to null like this:
galleryViewHolder.galleryImageView.setImageDrawable(null);

Center-crop an image into an ImageView with variable size?

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?

Why images loaded by Glide get downsampled to low resolution in a RecyclerView?

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

How to keep ViewHolder's width after view was recycled

I'm using RecyclerView as horizontal list to show my images.
If I scroll to the fifth picture, the first two or three are recycled and ViewHolder loses its width. If I scroll back to the first image, the images are loaded again and that leads to jumps while scrolling.
Here is R.layout.fragment_details_view_img_item
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/details_view_img_item"
android:background="#color/red"
android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ImageView>
My ViewHolder and Adapter:
private class ViewHolder extends RecyclerView.ViewHolder {
ImageView img;
public ViewHolder(ImageView imgV){
super(imgV);
img = imgV;
}
}
private class ImageListAdapter extends RecyclerView.Adapter<ViewHolder> {
[...]
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
View v = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_details_view_img_item, parent, false);
v.setOnClickListener(listener);
logDebug("onCreateViewHolder");
return new ViewHolder((ImageView) v);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
logDebug("onBindViewHolder");
ImageItem item = data.get(i);
if (item != null) {
ImageView imgView = viewHolder.img;
imgView.setTag(item);
String imgurl = ImageUtil.imgUrlForAvailableHeightInPX(item, parentHeight);
ImageLoader.instance().loadASYNC(imgurl, imgView);
}
}
#Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
logDebug("onViewRecycled: " + holder.img.getTag());
}
}
So how can I keep ViewHolder's width?
Before I start to approach this problem, one thing needs to be clear. The ViewHolder doesn't have any width, the ImageView it "holds" does have width, and that's what you're trying to control.
Now, considering this, your issue is (after making certain assumptions from looking at your code) that you need to know the width of a certain image when it is at a certain given height and while maintaining aspect ratio - before it arrives from the server.
This is a tricky one.
One option would be to preload all your images. This, however, is very costly with memory and could lead to memory crashes.
A better option would be to load all the images' details, without actually downloading the images' pixels. Then, you'll need to remember the aspect ratio of all the images in some cache, and set the dimension of the image you're loading prior to actually downloading the image contents.
To download an image's dimensions without downloading the image itself, you should use something like this:
public float getImageAspectRatio(InputStream inputStreamFromServer)
{
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
decodeOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStreamFromServer, null, decodeOptions);
final float imageDesiredWidth = decodeOptions.outWidth;
final float imageDesiredHeight = decodeOptions.outHeight;
return imageDesiredWidth / imageDesiredHeight;
}
Once you have this method, you'll need to preload all your images using this function:
private float[] mAspectRatios;
public void decodeAllAspectRatios(List<String> imageUrls)
{
mAspectRatios = new float[imageUrls.size()];
InputStream inputStream;
int index = 0;
for (String url : imageUrls)
{
// Get the input stream from the image url using whatever method you use.
mAspectRatios[index] = getImageAspectRatio(inputStream);
index++;
}
}
Important: Your RecyclerView should not begin working until this method finished working.
Once you have preloaded all your aspect ratios, we go back to your ViewHolder:
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
logDebug("onBindViewHolder");
ImageItem item = data.get(i);
if (item != null) {
ImageView imgView = viewHolder.img;
// set the layout params of the image, making it fit the correct size prior to loading the bitmap.
imgView.setLayoutParams(new LayoutParams(parentHeight * mAspectRatios[i], parentHeight));
imgView.setTag(item);
String imgurl = ImageUtil.imgUrlForAvailableHeightInPX(item, parentHeight);
ImageLoader.instance().loadASYNC(imgurl, imgView);
}
}
So long as you want your RecyclerView to display varying width images depending on their aspect ratio, I believe this is your best option.

RecyclerView + StaggeredGridLayout + Picasso get GridItem width

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);
}
});
}

Categories

Resources