web-dev-qa-db-fra.com

Android: le contenu de RecyclerView foiré après le défilement

J'utilise RecyclerView pour afficher une liste de marques, et chaque marque de la valeur est affichée sous forme de CardView. Mais certains contenus des cartes sont perdus après le défilement de RecyclerView vers le bas et le retour en arrière, comme indiqué dans les deux captures d'écran ci-dessous. Le contenu du rectangle rouge est perdu après le défilement.

AVANT LE DÉFILEMENT; enter image description here

APRÈS LE DÉFILEMENT; enter image description here

Je me demande s'il s'agit ou non d'un bogue de RecyclerView et je ne trouve aucune solution après la recherche sur Google.

Toutes les vues sont invisibles sauf le titre, leur visibilité dépend de la valeur de la marque.

Est-ce que quelqu'un sait pourquoi cela se produirait?

26
Swisyn

onBindHolder appelé plusieurs fois car le recycleur a besoin d'une vue à moins qu'une nouvelle en cas de changement de type de vue. Ainsi, chaque fois que vous définissez la visilibité dans les vues enfant, les autres états des vues sont également modifiés.

Chaque fois que vous faites défiler vers le haut ou vers le bas, ces vues sont redessinées avec de mauvaises options de visibilité.

Solution:

Vous disposez d'une méthode setValue et vérifiez les valeurs. Si nécessaire, il appelle une autre méthode "showView". Vous devez implémenter l'instruction else (dont la valeur est 0 ou null) et y masquer la vue ...

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);
}
29
Emre Aktürk

Après avoir lutté avec ce même problème pendant environ 24 heures, j'ai trouvé une solution qui fonctionnait pour moi. La clé utilisait la méthode setIsRecyclable() de la classe RecyclerView.ViewHolder.

Voici une section de mon code onBindViewHolder().

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

onBindHolder appelé plusieurs fois car Recycler View a besoin d'une vue à moins qu'elle ne soit nouvelle. Ainsi, chaque fois que vous définissez la visilibité dans les vues enfant, les autres états des vues sont également modifiés.

Chaque fois que vous faites défiler vers le haut ou vers le bas, ces vues sont redessinées avec de mauvaises options de visibilité, alors spécifiez toujours les deux conditions car la vue du recycleur ne connaît pas l'état/les conditions/les valeurs précédentes de nos widgets.

Solution :

Si dans le bloc If, vous définissez la visibilité de tout Android widget.setVisibility (View.Gone), dans un autre bloc, vous devez définir sa visibilité en face de vwith widget.setVisibility (View.Visible) pour surmonter le problème ci-dessus.

 @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);
            }
        }
    });
}
12
Sachin Mandhare

Cela est dû au fait que les vues sont réutilisées lors du défilement. Pour résoudre ce problème, vous devrez réinitialiser toutes les vues qui ont été rendues visibles pour d'autres cellules (YUZME dans votre exemple).

Dans setValue (valeur de l'objet, TextView textView, ligne TableRow, View seperator), il suffit de masquer à nouveau tous les txtVize *.


La vue Recycler commence par 3 vues:

[0] FIZ104

[1] MAT102

[2] REK361

Lorsque la vue défile vers le bas, les vues [0] et [1] sont recyclées. Lorsque vous faites défiler vers le haut, la vue [2] est utilisée pour afficher les données contenues dans FIZ104 et MAT102 et toutes les modifications apportées pour REK361 sont toujours là.

0
Numan1617

Si vous rencontrez des problèmes avec le problème de visibilité pour ex. si vous avez défini la visibilité de la vue en fonction de la condition, mais si vous faites défiler vers le bas, les vues sont actualisées et la visibilité de la vue à l'intérieur de cet élément de vue recycleur est modifiée et viole donc la condition.

Voici une solution:

1. Attribuez d'abord la balise à la vue dont vous souhaitez conserver la visibilité.

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

2. Maintenant, appliquez votre condition et basculez la visibilité

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

La clé de la réponse ici est n'oubliez pas la partie else.

0
Udit Kapahi