RecyclerView string is shuffled and resized: android

  • Changes in the question as another problem has been fixed.

My problem is that the Recycler View scrolls up and down

  • A line element changes its position during fast scrolling and returns to its original position.
  • The line item makes it part of the fix as a background

Here, as I set the view of My Recycler in a snippet.

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

// set 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 bind my view to the 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); } }); } } } } 

// install 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 the conditions for setting the header and line.

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

}

+8
android adapter fragment android-recyclerview android-viewholder
source share
4 answers

I have a very similar adapter in my application where I show a feed from Facebook instead of Twitter. First of all, switch to Glide instead of Picasso, as it gives better caching and image loading using RecyclerView, check THIS . So, getting to your problem now, call setHasStableIds (true) on your adapter in the first step, override the getItemId method to return the appropriate long from your adapter, since you are using the feed, you can calculate the hash for PostId or some field that uniquely identifies each post in the feed. My feed also has an optional image, so 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 have to do is call these methods from your onBindViewHolder to set the image. Post is my object that contains information about a single post, such as name, user ID, image URL for an image of a person, and image URL for an additional image, which is optional. Let me know if you run into any problems.

 @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 } 
+3
source share

If you see the RecyclerView source code HERE , skip to line 3028 that contains these lines

 /** * Returns a unique key to be used while handling change animations. * It might be child position or stable id depending on the adapter type. */ long getChangedHolderKey(ViewHolder holder) { return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition; } 

and on line 5279 in the source file that contains this

 /** * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()} * would return false this method should return {@link #NO_ID}. The default implementation * of this method returns {@link #NO_ID}. * * @param position Adapter position to query * @return the stable ID of the item at position */ public long getItemId(int position) { return NO_ID; } 

it clearly indicates that overriding setHasStableIds should be followed by an overriding getItemId method. In order for each element inside your RecyclerView to display the correct element when scrolling up and down, getItemId cannot return a position or, even worse, stop. Make sure you return a unique long value that identifies each row in your dataset.

What could be this unique meaning?

  • Unique timestamp when your twitter was made, if you have it?
  • A computed unique long value, such as a hash for one or more elements from your dataset, will give a unique value.

This is important for both animations and displaying the correct line in the correct position. Hope that helps fix your problem.

+2
source share

The problem is that you are not implementing getItemID , or not using it properly.

If you set setHasStableIds to true and you do not implement getItemId to return a unique long for each element in the list of repeating elements of RecyclerView WILL . This is because the default getItemID implementation returns -1 for all items in the list, so when onBindViewHolder is called after reusing the view, it will return the cached view of the last thing displayed by the ViewHolder.

An easy way to implement this is to simply return the hash code for the object in the list associated with this ViewHolder.

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

Obviously what this means for this particular object, you must override its equals() and hashCode() methods to be unique for each object. It is a good idea to show String when it was created, if from a database merged with the name of the object.

+1
source share

Override these 2 methods in your recyclerview adapter and change the getItemViewType code as shown below.

  @Override public long getItemId(int position) { return position; } @Override public int getItemViewType(int position) { if (isPositionHeader(position)) { return TYPE_HEADER; } else { return position; } } 

I also had the same problem. Do it. This will definitely solve your problem.

0
source share

All Articles