RecyclerView Recycled ViewHolder Image View wrong size
Solution 1
Add this line
android:adjustViewBounds="true"
to the imageview
in layout file it will automatically resize image view.
in glide change .crossFade()
to .fitCenter()
Solution 2
I ran into the same issue and i solved it as below,
Glide.with(mContext)
.load(model.getImage())
.asBitmap()
.fitCenter()
.placeholder(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
.into(holder.ivImage);
I just added .asBitmap() and .fitCenter() , Issue Resolved.
Solution 3
1 .
add android:adjustViewBounds="true"
to ImageView
<ImageView
android:id="@+id/img_item_my_show_img"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/backgrund_banner"/>
2.Change the following code to Glide
Glide.with(context).asBitmap().load(imgUrl)
.apply(RequestOptions()
.fitCenter()
.placeholder(R.drawable.default_img)
.error(R.drawable.error_img))
.into(ImageView)
Solution 4
you must in onBindViewHolder set width of image
for example:
yourImageView.getLayoutParams().width = GetScreenWidthPx();
public int GetScreenWidthPx() {
DisplayMetrics displayMetrics = MyApp.GetContext().getResources().getDisplayMetrics();
return displayMetrics.widthPixels - DpToPx(your_margin_in_dp);
}
public static int DpToPx(int dp) {
DisplayMetrics displayMetrics =
MyApp.GetContext()
.getResources()
.getDisplayMetrics();
return (int) (dp * displayMetrics.density + 0.5f);
}
DJ-DOO
Updated on June 23, 2022Comments
-
DJ-DOO about 2 years
I have a recycler view with different View Holders.
A couple of view holders have image views which I pass into Glide to display images.
The problem is, when the recycler view starts recycling views, the imageview width/height are that of the recycled view they then display the image incorrectly.
Here is my ImageView:
<ImageView android:id="@+id/image" android:layout_marginTop="@dimen/feed_item_margin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"/>
This is passed into Glide
Glide.with(itemView.getContext()) .load(Uri.parse(MediaUtils .getMedia(feedContent).getMediaUrl())) .placeholder(R.drawable.placeholder) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .crossFade().into(image);
This works well until the Recyclerview starts Recycling so the first image in the recyclerview looks like this which is how it's meant to look.
however, when you scroll away from the item and scroll back it looks like this:
So the image has become distorted and is not not the full width of the parent.
I want the image view to wrap content because all the images will be different heights etc.. To test this I added this line
holder.setIsRecyclable(false);
to prevent recycling of this particular holder and all the images displayed as the should, however, as expected this gave the jarring effect.So I then tried resetting the params of the image view in the
OnViewRecycled
method like so:@Override public void onViewRecycled(AbstractHolder viewHolder){ super.onViewRecycled(viewHolder); int position = viewHolder.getAdapterPosition(); IFeedContent content = mFeedContentList.get(position); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(0, (int) Utils.dpTopx(mContext,10),0,0); params.gravity = Gravity.CENTER; if(isImage(content)){ viewHolder.getImageView().setImageURI(null); viewHolder.getImageView().setImageDrawable(null); viewHolder.getImageView().setImageResource(0); viewHolder.getImageView().setLayoutParams(params); } }
In this I recreate the params in the xml but it doesn't work. The method
isImage()
this just checks the mimetype of the object.Can anyone help on this? It's very frustrating.
Any help on this is appreciated.
EDIT Adapter added
public class ContentFeedAdapter extends RecyclerView.Adapter<AbstractHolder> { private List<IFeedContent> mFeedContentList; private Context mContext; private Activity mMainActivity; private UserHomeFragment mUserHomeFragment; private UserStreamFragment mUserStreamFragment; private AbstractHolder mAbstractHolder; private final Map<YouTubeThumbnailView, YouTubeThumbnailLoader> mThumbnailViewToLoaderMap; private ArrayList<MyMediaPlayer> mMediaPlayerList = new ArrayList<>(); public ContentFeedAdapter(Context ctx, List<IFeedContent> contentList, Activity mainActivity, UserHomeFragment userHomeFragment, UserStreamFragment userStreamFragment){ this.mContext = ctx; this.mFeedContentList = contentList; this.mMainActivity = mainActivity; this.mThumbnailViewToLoaderMap = new HashMap<YouTubeThumbnailView, YouTubeThumbnailLoader>(); this.mUserHomeFragment = userHomeFragment; this.mUserStreamFragment = userStreamFragment; } @Override public AbstractHolder onCreateViewHolder(ViewGroup parent, int viewType) { mAbstractHolder = createAbstractHolder(viewType, parent); return mAbstractHolder; } @Override public void onBindViewHolder(final AbstractHolder holder, final int position) { final IFeedContent content = mFeedContentList.get(position); holder.bindData(content); if((content.getMedia()!=null) && !content.getMedia().isEmpty()){ String mimeType = MediaUtils.getMedia(content).getMimeType(); if(mimeType.contains(mContext.getString(R.string.video)) || mimeType.contains(mContext.getString(R.string.audio)) && !mimeType.contains(mContext.getString(R.string.youtube))){ final ProgressBar progressBar = holder.getProgress(); final ImageView playButton = holder.getPlayImage(); final Button retryButton = holder.getRetryImage(); final RelativeLayout playerOverLay = holder.getPlayerOverlay(); final ImageView mediaThumb = holder.getMediaThumbnail(); final MyMediaPlayer player = new MyMediaPlayer(mContext, holder.getTextureView(), holder.getMediaControllerAnchor(), holder.getProgress(), mimeType, MyConstants.SEEK_TO_DEFAULT, retryButton, playButton, playerOverLay, mediaThumb); player.setRecyclerViewPosition(position); mMediaPlayerList.add(player); playButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { player.startVideo(MediaUtils.getMedia(content).getMediaUrl()); holder.getPlayImage().setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); } }); } } } /** * Release all holders used for the * thumbnail views */ public void releaseYouTubeHolders(){ mAbstractHolder.releaseHolders(); } @Override public int getItemViewType(int position){ int viewType = -1; //Instantiate ViewHolder Utils // viewType = ViewHolderUtils.selectViewHolder(mFeedContentList.get(position)); return viewType; } @Override public int getItemCount() { return mFeedContentList.size(); } @Override public void onViewRecycled(AbstractHolder viewHolder){ super.onViewRecycled(viewHolder); int position = viewHolder.getAdapterPosition(); IFeedContent content = mFeedContentList.get(position); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(0, (int) Utils.dpTopx(mContext,10),0,0); params.gravity = Gravity.CENTER; if(isImage(content)){ viewHolder.getImageView().setImageURI(null); viewHolder.getImageView().setImageDrawable(null); viewHolder.getImageView().setImageResource(0); viewHolder.getImageView().setLayoutParams(params); } } /** * Create instance of * compatible viewholder * * @param viewType * @param parent * @return */ private AbstractHolder createAbstractHolder(int viewType, ViewGroup parent) { AbstractHolder holder = null; switch (viewType) { case MyConstants.HOLDER_TYPE_1: holder = ViewHolder_Var1.create(parent, mUserHomeFragment, mUserStreamFragment); break; case MyConstants.HOLDER_TYPE_2: holder = ViewHolder_Var2.create(parent, mUserHomeFragment, mUserStreamFragment); break; case MyConstants.HOLDER_TYPE_3: holder = ViewHolder_Var3.create(parent, mUserHomeFragment, mUserStreamFragment); L.i(getClass().getSimpleName(), "HOLDER 3"); //holder.setIsRecyclable(false); break; case MyConstants.HOLDER_TYPE_4: holder = ViewHolder_Var4.create(parent, mUserHomeFragment, mUserStreamFragment); L.i(getClass().getSimpleName(), "HOLDER 4"); break; case MyConstants.HOLDER_TYPE_5: holder = ViewHolder_Var5.create(parent, mUserHomeFragment, mUserStreamFragment); L.i(getClass().getSimpleName(), "HOLDER 5"); break; case MyConstants.HOLDER_TYPE_6: holder = ViewHolder_Var6.create(parent, mUserHomeFragment, mUserStreamFragment); L.i(getClass().getSimpleName(), "HOLDER 6"); break; case MyConstants.HOLDER_TYPE_7: holder = ViewHolder_Var7.create(parent, mUserHomeFragment, mUserStreamFragment); L.i(getClass().getSimpleName(), "HOLDER 7"); break; case MyConstants.HOLDER_TYPE_8: holder = ViewHolder_Var8.create(parent, mUserHomeFragment, mUserStreamFragment); L.i(getClass().getSimpleName(), "HOLDER 8"); break; case MyConstants.HOLDER_TYPE_9: holder = ViewHolder_Var9.create(parent, mUserHomeFragment, mUserStreamFragment); break; case MyConstants.HOLDER_TYPE_10: holder = ViewHolder_Var10.create(parent, mThumbnailViewToLoaderMap, mUserHomeFragment, mUserStreamFragment); } return holder; } private boolean isImage(IFeedContent contentItem) { if (MediaUtils.getMedia(contentItem) != null) { String mimeType = MediaUtils.getMedia(contentItem).getMimeType(); if (mimeType.contains("image")) { L.i(getClass().getSimpleName(), "IMAGE HERE"); return true; } else { L.i(getClass().getSimpleName(), "NO IMAGE HERE"); } } return false; } }
EDIT 2 ViewHolder 3
public class ViewHolder_Var3 extends AbstractHolder { @Bind(R.id.text_holder1) TextView heading; @Bind(R.id.text_holder2) TextView body; @Bind(R.id.image)ImageView image; @Bind(R.id.tabs_layout)LinearLayout tabsLayout; @Bind(R.id.hot)TextView hot; @Bind(R.id.comments)TextView children; @Bind(R.id.gif_label)TextView gifTag; @Bind(R.id.user_name)TextView userName; @Bind(R.id.tag1)TextView tag1; @Bind(R.id.tag2)TextView tag2; @Bind(R.id.tag3)TextView tag3; @Bind(R.id.profile_pic) SimpleDraweeView profilePic; private boolean mEllipsize; private boolean mExpanded; private UserHomeFragment mUserHomeFragment; private UserStreamFragment mUserStreamFragment; public ViewHolder_Var3(View itemView, UserHomeFragment userHomeFragment, UserStreamFragment userStreamFragment) { super(itemView); ButterKnife.bind(this, itemView); mUserHomeFragment = userHomeFragment; this.mUserStreamFragment = userStreamFragment; } @Override public void bindData(final IFeedContent feedContent) { userName.setText(feedContent.getAuthor().getDisplayName()); image.setImageResource(0); image.setImageDrawable(null); image.setImageURI(null); TextView [] tagsArray = {tag1, tag2, tag3}; if (feedContent.getName() != null) { heading.setText(feedContent.getName()); } else { heading.setText(feedContent.getUrl()); } if (feedContent.getName() != null) { body.setText((feedContent.getMessage())); } else { body.setText(feedContent.getUrl()); } Log.i(ViewHolder_Var3.class.getSimpleName(), "Number of lines: " + String.valueOf(body.getLineCount())); if(!MediaUtils.getMedia(feedContent).getMimeType().equals("image/gif")){ gifTag.setVisibility(View.GONE); Glide.with(itemView.getContext()).load(Uri.parse(MediaUtils.getMedia(feedContent).getMediaUrl())).placeholder(R.drawable.placeholder).diskCacheStrategy(DiskCacheStrategy.SOURCE).crossFade().into(image); }else { Glide.with(itemView.getContext()).load(Uri.parse(MediaUtils.getMedia(feedContent).getMediaUrl())).asGif().placeholder(R.drawable.placeholder).diskCacheStrategy(DiskCacheStrategy.RESULT).crossFade().into(image); } displayProfilePic(feedContent, profilePic); Glide.with(itemView.getContext()).load(Uri.parse(MediaUtils.getMedia(feedContent).getMediaUrl())).placeholder(R.drawable.placeholder).diskCacheStrategy(DiskCacheStrategy.ALL).crossFade().into(image); if(mUserHomeFragment==null){ userName.setEnabled(false); profilePic.setEnabled(false); }else{ userName.setEnabled(true); profilePic.setEnabled(true); } userName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivityForResult(mUserHomeFragment, feedContent.getAuthor().getId(), feedContent.getParentId()); } }); profilePic.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivityForResult(mUserHomeFragment, feedContent.getAuthor().getId(), feedContent.getParentId()); } }); long hotAmt = feedContent.getLikeCount() - feedContent.getDislikeCount(); hot.setText(String.valueOf(hotAmt)); children.setText(String.valueOf(feedContent.getChildCount())); List<String> tagsList = feedContent.getTags(); populateTags(tagsList, tagsArray); // if (feedContent.getTags().size() > 0) addTags(tags, tabsLayout); ViewTreeObserver vto = body.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { ViewTreeObserver obs = body.getViewTreeObserver(); obs.removeOnGlobalLayoutListener(this); Layout layout = body.getLayout(); if(layout!=null){ int lines = layout.getLineCount(); if(lines>0){ if(layout.getEllipsisCount(lines-1)>0){ mEllipsize = true; } } } } }); body.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mEllipsize) { if (!mExpanded) { ObjectAnimator animation = ObjectAnimator.ofInt(body, "maxLines", 20); //animation.setInterpolator(new BounceInterpolator()); animation.setDuration(200).start(); // Toast.makeText(itemView.getContext(), "I AM CLICKED", Toast.LENGTH_LONG).show(); mExpanded = true; } else { ObjectAnimator animation = ObjectAnimator.ofInt(body, "maxLines", 4); //animation.setInterpolator(new BounceInterpolator()); animation.setDuration(200).start(); // Toast.makeText(itemView.getContext(), "I AM CLICKED", Toast.LENGTH_LONG).show(); mExpanded = false; } } } }); } @Override public ImageView getImageView(){ return image; } public static ViewHolder_Var3 create(ViewGroup parent, UserHomeFragment homeFragment, UserStreamFragment userStreamFragment){ View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.feed_content_item_layout_var3, parent, false); return new ViewHolder_Var3(root, homeFragment, userStreamFragment); } }
-
DJ-DOO over 8 yearsHow will that ensure that the images are displayed correctly? Their height varies also. Plus there are 10 different view holders
-
Mohammad Hossein Gerami over 8 yearsyou must check type of item with holder.getItemViewType()
-
DJ-DOO over 8 yearsthanks for your help, can you explain you answer please. When I make this addition the results are better, however, now there are big margins between the image and the text above and below
-
Mohammad Hossein Gerami over 8 yearsedited code for add margin. i using this method for stop jump width & height of image before and after load image.
-
DJ-DOO over 8 yearsok thanks, I was trying to learn the reasons behind it. What margin are you passing in?
-
Mohammad Hossein Gerami over 8 yearsbecause previous view is reusing and get width of previous item.
-
DJ-DOO over 8 yearsLet us continue this discussion in chat.
-
Mohammad Hossein Gerami over 8 yearsi can chat 2 hours after this time
-
DJ-DOO over 8 yearsHas no one run into this issue before?
-
Mohammad Hossein Gerami over 8 yearsexcuse me. i can't understand your sentence.
-
DJ-DOO over 8 yearsHi, sorry, I was asking if no one else had encountered this problem. I know that this is due to view recycling. Your suggestion didn't work for me unfortunately
-
Daniel Silva almost 6 yearsDidn't work for me, had to add android:adjustViewBounds="true" to work.