Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView's row is shuffling and change Images : android

  • Changes in Question as other issue is fixed.

My issue is when Recycler View is scrolling Up and Down

  • Row item changes it's position when scrolling fast and get back to it's original position.
  • Row item makes it's some portion fix as background

Here how i am setting My Recycler view in Fragment.

private void setRecyclerView() {
    recyclerView.setHasFixedSize(true);
    StaggeredGridLayoutManager layoutManager =
            new StaggeredGridLayoutManager(2, 1);
    recyclerView.setLayoutManager(layoutManager);
    adapter = new TwitterTweetAdapter(getActivity());

    // setting every thing false in item animator 
    recyclerView.setItemAnimator(new RecyclerView.ItemAnimator() {

        @Override
        public void runPendingAnimations() {

        }

        @Override
        public boolean animateRemove(RecyclerView.ViewHolder viewHolder) {
            return false;
        }

        @Override
        public boolean animateAdd(RecyclerView.ViewHolder viewHolder) {
            return false;
        }

        @Override
        public boolean animateMove(RecyclerView.ViewHolder viewHolder, int i, int i2, int i3, int i4) {
            return false;
        }

        @Override
        public boolean animateChange(RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder viewHolder2, int i, int i2, int i3, int i4) {
            return false;
        }

        @Override
        public void endAnimation(RecyclerView.ViewHolder viewHolder) {

        }

        @Override
        public void endAnimations() {

        }

        @Override
        public boolean isRunning() {
            return false;
        }
    });
    recyclerView.setAdapter(adapter);
}

Here is My Adapter Class

In My Adapter I am setting One Header

//My ViewHolders

class Header extends RecyclerView.ViewHolder {

    @Bind(R.id.feed_head)
    CardView feedHeader;
    @Bind(R.id.feed_header_count)
    TextView userCount;
    @Bind(R.id.feed_header_text)
    TextView userText;

    public TwitterHeader(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }
}

public static class RowViewHolder extends RecyclerView.ViewHolder {

    @Bind(R.id.feed_row_view)
    CardView feedRow;
    @Bind(R.id.feed_image)
    ImageView feedImage;
    @Bind(R.id.video_play_icon)
    ImageButton playButton;
    @Bind(R.id.feed_description)
    TextView feedDescription;
    @Bind(R.id.feed_user_image)
    ImageView userImage;
    @Bind(R.id.feed_user_name)
    TextView userName;
    @Bind(R.id.feed_time)
    TextView feedDate;
    @Bind(R.id.feed_progress_bar)
    ProgressBar progressBar;

    public RowViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }
}

// declaration of variables

private static final int TYPE_HEADER = 0;
private static final int TYPE_FEED = 1;

Context context;
private Activity activity;
private List<MockData> dataLists;
public static List<MockData> dataListsupdated;

int userSearchCount;

// constructor

public FeedAdapter(Activity activity) {
    this.activity = activity;
}

// setting data list

public void setDataList(List<MockData> dataLists, int userSearchCount, float density) {
    this.dataLists = dataLists;
    this.density = density;
    this.userSearchCount = userSearchCount;
}

// onCreateViewHolder

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    if (viewType == TYPE_HEADER) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.feed_header, viewGroup, false);
        return new Header(view);
    } else {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.feed_row, viewGroup, false);
        return new RowViewHolder(v);
    }
}

// onBindViewHolder i am bind my view with data.

if (holder instanceof Header) {
        Header header = (Header) holder;
        if (userSearchCount == 20) {
            header.userCount.setText(R.string.twitter_default_count);
        } else {
            header.userCount.setText(userSearchCount);
        }
        header.userText.setText(R.string.user);
        header.feedHeader.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, TwitterSearchActivity.class);
                activity.startActivity(intent);
            }
        });

    } else {
        if (holder instanceof RowViewHolder) {
            final RowViewHolder rowViewHolder = (RowViewHolder) holder;
            final MockData responseList = dataLists.get(position);

            rowWidth = rowViewHolder.feedImage.getWidth();
            rowViewHolder.playButton.setVisibility(View.GONE);

            if (responseList.text.startsWith("RT")) {
                rowViewHolder.feedRow.setVisibility(View.GONE);
                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
                params.setMargins(0, 0, 0, 0);
                rowViewHolder.feed.setLayoutParams(params);
            } else {
                setRowImage(rowViewHolder, responseList);
                rowViewHolder.feedDescription.setText(responseList.text);

                rowViewHolder.userImage.setVisibility(View.VISIBLE);
                Glide.with(activity)
                        .load(responseList.user.profileImageUrlHttps)
                        .into(rowViewHolder.userImage);

                rowViewHolder.userName.setText(responseList.user.screenName);

                Date date = new Date(responseList.createdAt);
                DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(activity.getApplicationContext());
                rowViewHolder.feedDate.setText(dateFormat.format(date));

                rowViewHolder.feedRow.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        startActivity(position);
                    }
                });
            }
        }
    }

}

// setting Row Image.

private void setRowImage(RowViewHolder rowViewHolder, Tweet responseList) {
    if (responseList.entities.media != null) {
        MediaEntity mediaEntity = responseList.entities.media.get(0);
        if (mediaEntity.mediaUrlHttps != null) {
            int height = mediaEntity.sizes.medium.h;
            int width = mediaEntity.sizes.medium.w;

            int ratio = width / 143;
            int newHeight = (int) ((height / ratio) * density);

            Glide.with(activity)
                    .load(mediaEntity.mediaUrlHttps)
                    .override(width, newHeight)
                    .placeholder(R.color.colorAccent)
                    .into(rowViewHolder.feedImage);

        } else {
            rowViewHolder.feedImage.setImageDrawable(null);
        }
    } else {
        rowViewHolder.feedImage.setImageDrawable(null);
    }
}

// View condition to set header and row.

@Override
public int getItemViewType(int position) {
    if (isPositionHeader(position))
        return TYPE_HEADER;
    return TYPE_FEED;
}

private boolean isPositionHeader(int position) {
    return position == 0;
}

@Override
public int getItemCount() {
    return dataLists == null ? 0 : dataLists.size();
}

}

like image 342
Abhishek Avatar asked Nov 07 '15 05:11

Abhishek


2 Answers

I have a very similar Adapter in my app where I display feed from Facebook instead of Twitter, First of all, switch to Glide instead of Picasso since it has gives better caching and image loading with RecyclerView, check THIS. So coming to your problem now, call setHasStableIds(true) on your Adapter in the 1st step, override the getItemId method to return a suitable long from your Adapter, since you are using a feed, you can compute a hash say for the postId or some field that unique identifies each post within a feed. My feed has an optional image too which is why I have these 2 methods

public void setProfilePicture(String uri) {

        //As per the solution discussed here http://stackoverflow.com/questions/32706246/recyclerview-adapter-and-glide-same-image-every-4-5-rows
        if (uri != null) {
            Glide.with(mContext)
                    .load(uri)
                    .asBitmap()
                    .transform(new CropCircleTransform(mContext))
                    .into(mProfilePicture);
        } else {
            Glide.clear(mProfilePicture);
            mProfilePicture.setImageResource(R.drawable.com_facebook_profile_picture_blank_square);
        }
    }

    public void setPostPicture(String uri) {

        //As per the solution discussed here http://stackoverflow.com/questions/32706246/recyclerview-adapter-and-glide-same-image-every-4-5-rows
        if (uri != null) {
            Glide.with(mContext)
                    .load(uri)
                    .asBitmap()
                    .transform(new CropTransformation(mContext, mPostImageWidth, mPostImageHeight))
                    .into(mPostPicture);
        } else {
            Glide.clear(mPostPicture);
            mPostPicture.setImageDrawable(null);
        }
    }

and then all you gotta do is call these methods from your onBindViewHolder to set the Image. Post is my object that contains the details of a single post such as name, userid , image url for the person s picture and image url for the post picture which is optional. Let me know if you encounter any issues further.

@Override
    public void onBindViewHolder(ItemHolder holder, int position) {
        Post post = mResults.get(position);
        holder.setUserName(post.getUserName());
        holder.setUpdatedTime(post.getUpdatedTime());
        holder.setMessage(post.getMessage(), mState, position);
        holder.setPostPicture(post.getPicture());
        holder.setProfilePicture(post.getUserPicture());
        // Check for an expanded view, collapse if you find one

    }
like image 199
PirateApp Avatar answered Oct 22 '22 16:10

PirateApp


The issue you have is that you are not implementing getItemID, or you are not implementing it properly.

If you set setHasStableIds to true and you do not implement getItemId to return a unique long for each item in the list, the RecyclerView WILL duplicate items. This is because the default implementation of getItemID returns -1 for all items in the list, therefore, when onBindViewHolder is called after a view is recycled, it's going to return a cached view of the last thing that ViewHolder displayed.

An easy way for you to implement this is just to return the hashcode for the object in the list bound to that ViewHolder.

@Override
public long getItemId(int position) {     
    Object listItem = listItems.get(position);
    return listItem.hashCode();  
}

Obviously, this means for that particular object, you must have overriden it's equals() and hashCode() methods to be unique for each object. A good idea is a String showing when it was created if from a database concatenated with the object's name.

like image 41
Tunji_D Avatar answered Oct 22 '22 18:10

Tunji_D