Android: RecyclerView content messed up after scrolling [closed]
Asked Answered
B

5

33

I'm using RecyclerView to display a list of marks, and each mark of the value is shown as a CardView. But some contents of the cards are lost after the scrolling the RecyclerView down and scrolling back, as shown in the two screenshots below. The contents in the red rectangle is lost after scrolling.

BEFORE THE SCROLLING; enter image description here

AFTER THE SCROLLING; enter image description here

I'm wondering whether or not it's a bug of RecyclerView and find no solution after Googling for it.

All views are invisible except the title, their visibilities are depends on the mark's value.

Does anyone know why this would happen?

Britt answered 17/4, 2015 at 14:43 Comment(4)
I suggest you to write custom view components. Settings visibility is not a good implementation. Do your project is on git? So i can create a pull request to it dostum ;) ?Parasitology
I would highly suggest refactoring your code before you continue. It is extremely hard to follow and you have WAY to many views. You should be able to create a lot of those dynamically to help cut back on some code. I really believe the issues is in onBindViewHolder.Akee
@EmreAktürk Unfortunately its not on git because of it has some special web services from my university. What can i implement with custom view, i can't realize :) i can't think that what to do, any help would be great. -JaredBurrows Actually the web service of the view is so dirty. Service has 10 Vize field and all of them contains the lecture marks. I am trying to check if the Vize field (mark) is not empty from the service then show the textview.Britt
Ok then... Here is the solve... You have a setValue method check values and set to view. If neccessary it calls another method "showView". You need to implement else statement (which is value is 0 or null) and hideView there...Parasitology
K
34

onBindHolder() is called several times as recycler needs a view, unless there's a new one when view type is changed. So each time you set visibility in child views, other views states are also changing.

Whenever you scroll up and down, these views are getting re-drawn with wrong visibility options.

Solution :

You have a setValue method check values and set to view. If neccessary it calls another method "showView". You need to implement else statement (which is value is 0 or null) and hideView there...

void setValue(Object value, TextView textView, TableRow row, View seperator) {
    if (value != null) {
        if (!isEmpty(value.toString())) {
            textView.setText(String.valueOf(value));
            showViews(row, seperator);
        }
    } else
        hideViews(row, seperator);
}

private void showViews(TableRow row, View seperator) {
    row.setVisibility(View.VISIBLE);
    seperator.setVisibility(View.VISIBLE);
}

private void hideViews(TableRow row, View seperator) {
    row.setVisibility(View.INVISIBLE); // if there is a empty space change it with View.GONE
    seperator.setVisibility(View.INVISIBLE);
}
Keto answered 17/4, 2015 at 14:56 Comment(1)
in OnBindHolder method.Parasitology
V
43

After battling with this same issue for about 24 hours, I found a solution that worked for me. The key was using the setIsRecyclable() method of RecyclerView.ViewHolder class.

Here is a section of my onBindViewHolder() code.

@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
    final DataSource dataSource = dataSourceList.get(position);

    holder.setIsRecyclable(false);

    holder.name.setText(dataSource.getName());
    holder.active.setChecked(dataSource.getActive());

    String logoStr = dataSource.getLogo();

    //Logo
    /**
     * Do all the logo insertion stunts here
     */
    /**
     * Register the changes to the Switch
     */
    holder.active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){
            dataSource.setActive(isChecked);
        }
    });
}
Venesection answered 26/7, 2016 at 15:27 Comment(2)
Setting recyclerview as not recyclable forfeit the purpose of recyclerview.Babblement
@YaMiN is right, this is much better answer https://mcmap.net/q/439899/-android-recyclerview-content-messed-up-after-scrolling-closedHelsie
K
34

onBindHolder() is called several times as recycler needs a view, unless there's a new one when view type is changed. So each time you set visibility in child views, other views states are also changing.

Whenever you scroll up and down, these views are getting re-drawn with wrong visibility options.

Solution :

You have a setValue method check values and set to view. If neccessary it calls another method "showView". You need to implement else statement (which is value is 0 or null) and hideView there...

void setValue(Object value, TextView textView, TableRow row, View seperator) {
    if (value != null) {
        if (!isEmpty(value.toString())) {
            textView.setText(String.valueOf(value));
            showViews(row, seperator);
        }
    } else
        hideViews(row, seperator);
}

private void showViews(TableRow row, View seperator) {
    row.setVisibility(View.VISIBLE);
    seperator.setVisibility(View.VISIBLE);
}

private void hideViews(TableRow row, View seperator) {
    row.setVisibility(View.INVISIBLE); // if there is a empty space change it with View.GONE
    seperator.setVisibility(View.INVISIBLE);
}
Keto answered 17/4, 2015 at 14:56 Comment(1)
in OnBindHolder method.Parasitology
S
17

onBindHolder called several times as Recycler View needs a view unless new one. So each time you set visilibity in child views, other views states are also changes.

Whenever you scroll up and down, these views are getting re-drawed with wrong visibility options so always specify both the conditions cause recycler view doesn't know the previous state/conditions/values of our widgets.

Solution :

If in If block you set visibility of any android widget.setVisibility(View.Gone) then in else block you have to set it's visibility opposite vwith widget.setVisibility(View.Visible) to overcome the above problem.

 @Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {

    viewHolder.tvName.setText(ModelCategoryProducts.name.get(i));
    viewHolder.tvPrice.setText("Rs."+String.format("%.2f", Float.parseFloat(ModelCategoryProducts.price.get(i))));
    if(ModelCategoryProducts.special_price.get(i).equals("null")) {
        viewHolder.tvSpecialPrice.setVisibility(View.GONE); // here visibility is gone and in else it's opposite visibility i set.
        viewHolder.tvPrice.setTextColor(Color.parseColor("#ff0000"));
        viewHolder.tvPrice.setPaintFlags(0);// here paint flag is 0 and in else it's opposite flag that i want is set.
    }else if(!ModelCategoryProducts.special_price.get(i).equals("null")){
        viewHolder.tvPrice.setTextColor(Color.parseColor("#E0E0E0"));
        viewHolder.tvSpecialPrice.setVisibility(View.VISIBLE);
        viewHolder.tvSpecialPrice.setText("Rs." + String.format("%.2f", Float.parseFloat(ModelCategoryProducts.special_price.get(i))));
        viewHolder.tvPrice.setPaintFlags(viewHolder.tvPrice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
    }
    if (!ModelCategoryProducts.image_url.get(i).isEmpty()) {
        Picasso.with(context)
                .load(ModelCategoryProducts.image_url.get(i))
                .into(viewHolder.ivProduct);
    }

    viewHolder.setClickListener(new ItemClickListener() {
        @Override
        public void onClick(View view, int position, boolean isLongClick) {
            if (isLongClick) {
//                    Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position) + " (Long click)", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position), Toast.LENGTH_SHORT).show();
                Intent i = new Intent(context, ProductDetail.class);
                i.putExtra("position",position);
                i.putExtra("flagHlvCheck", 5);
                context.startActivity(i);
            }
        }
    });
}
Swisher answered 11/12, 2015 at 6:41 Comment(0)
K
1

If you are facing problems with the the visibility issue for ex. if u have set the visibility of the view based on condition , but if you scroll down, the views get refreshed and the visibility of the view inside that recycler view item gets changes and hence violating the condition.

Here's a solution:

1. First assign tag to that view whose visibility you want to maintain.

    holder.myImageView.setTag(myTeamLists.get(position));
    MyDTOClass checkWetherToShow=(MyDTOClass)holder.myImageView.getTag();

2. Now apply your condition and toggle the visibility

    if (checkWetherToShow.getHasToShowImage()){
        holder.myImageView.setVisibility(View.VISIBLE);
    }else{
        holder.myImageView.setVisibility(View.GONE);
    }

The key to the answer here is don't forget the else part.

Kirkkirkcaldy answered 9/9, 2016 at 10:10 Comment(0)
V
0

This is due to the views being reused when scrolling occurs. To fix this you will need to reset any views that have been made visible for other cells (YUZME in your example).

Inside setValue(Object value, TextView textView, TableRow row, View seperator) simply make all txtVize* hidden again.


Recycler view starts off with 3 views:

[0] FIZ104

[1] MAT102

[2] REK361

When the view is scrolled to the bottom views [0] and [1] are recycled. When you scroll back to the top view [2] is used to display the data contained in FIZ104 and MAT102 and any changes made for REK361 are still there.

Vaccaro answered 17/4, 2015 at 14:50 Comment(1)
Thank you for the response. I know the viewholder mechanism actually what i want to do is do not recycle the views or keep their states even the user scrolls. I'm almost tring to figure it out about 6 hours and tried everything but none of them worked.Britt

© 2022 - 2024 — McMap. All rights reserved.