RecyclerView Recycled ViewHolder Image View wrong size

16,043

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);
}
Share:
16,043
DJ-DOO
Author by

DJ-DOO

Updated on June 23, 2022

Comments

  • DJ-DOO
    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.

    enter image description here

    however, when you scroll away from the item and scroll back it looks like this:

    enter image description here

    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
    DJ-DOO over 8 years
    How will that ensure that the images are displayed correctly? Their height varies also. Plus there are 10 different view holders
  • Mohammad Hossein Gerami
    Mohammad Hossein Gerami over 8 years
    you must check type of item with holder.getItemViewType()
  • DJ-DOO
    DJ-DOO over 8 years
    thanks 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
    Mohammad Hossein Gerami over 8 years
    edited code for add margin. i using this method for stop jump width & height of image before and after load image.
  • DJ-DOO
    DJ-DOO over 8 years
    ok thanks, I was trying to learn the reasons behind it. What margin are you passing in?
  • Mohammad Hossein Gerami
    Mohammad Hossein Gerami over 8 years
    because previous view is reusing and get width of previous item.
  • DJ-DOO
    DJ-DOO over 8 years
  • Mohammad Hossein Gerami
    Mohammad Hossein Gerami over 8 years
    i can chat 2 hours after this time
  • DJ-DOO
    DJ-DOO over 8 years
    Has no one run into this issue before?
  • Mohammad Hossein Gerami
    Mohammad Hossein Gerami over 8 years
    excuse me. i can't understand your sentence.
  • DJ-DOO
    DJ-DOO over 8 years
    Hi, 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
    Daniel Silva almost 6 years
    Didn't work for me, had to add android:adjustViewBounds="true" to work.