8

This is the project Ich versuche zu laufen. Hier ist mein Code für die onBindViewHolder von RecyclerView.Adapter KlasseRecyclerView Ambiguos setVisibility Funktion, klicken auf eine Ansicht wirkt sich auf mehrere Ansichten

@Override 
    public void onBindViewHolder(ViewHolder holder, final int position) { 

     TextView title = (TextView) holder.view.findViewById(R.id.title); 
     final TextView desc = (TextView) holder.view.findViewById(R.id.desc); 
     final ImageView imageView = (ImageView) holder.view.findViewById(R.id.imageView); 

     title.setText(pojos.get(position).getTitle()); 
     desc.setText(pojos.get(position).getDesc()); 

     imageView.setImageResource(pojos.get(position).getImage()); 

     imageView.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       desc.setText("clicked"); 
       desc.setBackgroundColor(Color.BLUE); 
       imageView.setImageResource(R.drawable.heart_red); 
      } 
     }); 

    } 

Die Liste lädt in Ordnung, das Problem tritt auf, wenn die OnClickListener des Imageview genannt wird.

desc.setText("clicked"); 

In der obigen Zeile wird die Änderung in dem Listenelement vorgenommen, auf das geklickt wurde. aber

Wenn diese Zeile ausgeführt wird, spiegelt sich die Änderung in mehreren Elementen in der Liste wider. Was läuft falsch? In den unten gezeigten Bildern habe ich auf Punkt 0 geklickt, der Text ändert sich auf "geklickt" und die Farbe wird gesetzt. Aber wenn ich nach unten scrolle, ist auch Punkt 12 von meinem Klick auf Punkt 0 betroffen. Nur die Hintergrundfarbe hat sich geändert, nicht die Textänderung. Wie höre ich damit auf?

enter image description here

enter image description here

Ich habe versucht, dies für eine lange Zeit zu lösen, bitte das Projekt herunterzuladen und zu versuchen, die Ausführung von Code zu verstehen, was ich genau meine, wenn meine Frage nicht klar ist.

Antwort

11

Dies geschieht, weil die Aussicht erhalten recycelt und wiederverwendet.

Wenn die Ansicht also wiederverwendet wird, bleiben die Eigenschaften der "alten" Ansicht erhalten, wenn sie nicht erneut geändert werden. Wenn Sie also bis zu Nummer 12 scrollen, wird die Ansicht, die die Nummer 1 enthielt, recycelt (da sie nicht mehr auf dem Bildschirm zu sehen ist) und zur Erstellung von Nummer 12 verwendet. Deshalb ist die blaue Farbe auf der Zahl 12.

Wenn das Element beispielsweise angeklickt wird, müssen Sie einen "angeklickten" Wert in Ihr POJO-Objekt speichern. Wenn das Objekt dann gezeichnet wird, überprüfen Sie diesen Wert und stellen Sie die richtige Bild-/Hintergrundfarbe in Abhängigkeit von diesem Wert ein.

Ich habe dies in dem folgenden Code getan, so sollte es Ihnen eine grobe Vorstellung davon, was zu tun ist:

@Override 
public void onBindViewHolder(ViewHolder holder, final int position) { 
    TextView title = (TextView) holder.view.findViewById(R.id.title); 
    final TextView desc = (TextView) holder.view.findViewById(R.id.desc); 
    final ImageView imageView = (ImageView) holder.view.findViewById(R.id.imageView); 

    final MyPojo pojo = pojos.get(position); 

    title.setText(pojo.getTitle()); 
    if(!pojo.clicked) { 
     desc.setText(pojo.getDesc()); 
     imageView.setImageResource(pojo.getImage()); 
     desc.setBackgroundColor(Color.argb(0,0,0,0)); 
    } else { 
     desc.setText("clicked"); 
     desc.setBackgroundColor(Color.BLUE); 
     imageView.setImageResource(R.drawable.heart_red); 
    } 

    imageView.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      pojo.clicked = true; 
      desc.setText("clicked"); 
      desc.setBackgroundColor(Color.BLUE); 
      imageView.setImageResource(R.drawable.heart_red); 
     } 
    }); 
} 

und ich habe ein „geklickt“ boolean auf die MyPojo Klasse hinzugefügt.

public class MyPojo { 

    String title; 
    String desc; 
    int image; 
    boolean clicked; 
} 
+0

Ich verstehe Ihre Lösung zu lösen. Aber warum funktionieren setText und setBackgroundColor anders? Warum funktioniert der setText genau so, wie wir es wollen? – 55597

+0

Not dinamically, sry :) – delive

+1

Amen dazu! Danke für die tolle Erklärung :) – Cbr

0

Ich hatte ein ähnliches Problem (hatte eine Änderung der Nummer auf mehrere Listenelemente statt nur einer). Ich nehme an, dass es wegen der Funktionsweise einer Recycling-Ansicht funktioniert, und ich konnte es beheben, indem ich alles, was ich plane, auf den Standard ändere, den ich möchte.

IE: Wenn Sie den Hintergrund in Blau ändern möchten, wenn Sie die Liste laden, setzen Sie diejenigen, die nicht blau sein sollten, in Grau (oder was auch immer Sie als Standard haben möchten).

so hier:

ViewHolder vh = new ViewHolder(v); 
return vh; 

Sie die Standardeinstellungen angeben möchten

-1

hier versuchen, diesen Adapter zu verwenden:

public class myAdapter extends RecyclerView.Adapter<CopyOfConversationAdapter.ViewHolder> { 
private ArrayList<conversationItem> pojos; 
// inner class to hold a reference to each item of RecyclerView 
public static class ViewHolder extends RecyclerView.ViewHolder { 

    TextView title; 
    TextView desc; 
    ImageView imageView; 


    public ViewHolder(View itemLayoutView) { 
     super(itemLayoutView); 
     title= (TextView) itemLayoutView.findViewById(R.id.title); 
     desc= (TextView) itemLayoutView.findViewById(R.id.desc); 
     imageView= (ImageView) itemLayoutView.findViewById(R.id.imageView); 
    } 
} 

// Return the size of your itemsData (invoked by the layout manager) 
@Override 
public int getItemCount() { 
    return pojos.size(); 
} 

public CopyOfConversationAdapter(Pojos[] pojos) { 
    this.pojos = new ArrayList<conversationItem>(); 
    this.pojos.addAll(Arrays.asList(Items)); 
} 
// Create new views (invoked by the layout manager) 
@Override 
public CopyOfConversationAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    // create a new view 
    View itemLayoutView; 
     itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.comments_item_layout_, null); 

    ViewHolder viewHolder = new ViewHolder(itemLayoutView); 
    return viewHolder; 
} 

// Replace the contents of a view (invoked by the layout manager) 
@Override 
public void onBindViewHolder(ViewHolder viewHolder, final int position) { 

    // - get data from your itemsData at this position 
    // - replace the contents of the view with that itemsData 

    viewHolder.title.setText(pojos.get(position).getSender()); 
    viewHolder.desc.setText(pojos.get(position).getSnippet()); 
    viewHolder.imageView.setText(pojos.get(position).getIcon()); 
    viewHolder.imageView.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      // TODO Auto-generated method stub 
      desc.setText("clicked"); 
       desc.setBackgroundColor(Color.BLUE); 
       imageView.setImageResource(R.drawable.heart_red); 
     } 
    }); 

} 

}

2

Es scheint, dass Sie eine Verwirrung über die Verwendung RecyclerView haben, indem sie in onBindViewHolder zu findViewById aufrufen. Diese kostspieligen Nachschlagevorgänge sollten in onCreateViewHolder stattfinden, wo Sie alle Ansichten nachschlagen und ihre Verweise auf Ihren benutzerdefinierten Ansichtshalter speichern. Ich ging weiter an an Ihrem Code in GitHub Repo geschaut und schlagen vor, folgende Änderungen:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { 

private ArrayList<MyPojo> pojos; 

// Provide a reference to the views for each data item 
// Complex data items may need more than one view per item, and 
// you provide access to all the views for a data item in a view holder 
public static class ViewHolder extends RecyclerView.ViewHolder { 
    // each data item is just a string in this case 
    public TextView title; 
    public TextView desc; 
    public ImageView imageView; 

    public ViewHolder(View v) { 
     super(v); 

     // all expensive findViewById lookups happen in ViewHolder constructor, 
     // which is called only when onCreateViewHolder is called 
     this.title = (TextView) v.findViewById(R.id.title); 
     this.desc = (TextView) v.findViewById(R.id.desc); 
     this.imageView = (ImageView) v.findViewById(R.id.imageView); 
    } 
} 

// Provide a suitable constructor (depends on the kind of dataset) 
public MyAdapter(ArrayList<MyPojo> pojos) { 
    this.pojos = pojos; 
} 

// Create new views (invoked by the layout manager) 
@Override 
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, 
               int viewType) { 
    // create a new view 
    View v = LayoutInflater.from(parent.getContext()) 
      .inflate(R.layout.row, parent, false); 
    // set the view's size, margins, paddings and layout parameters 
    ViewHolder vh = new ViewHolder(v); 
    return vh; 
} 

// Replace the contents of a view (invoked by the layout manager) 
@Override 
public void onBindViewHolder(ViewHolder holder, final int position) { 
    // this callback will be constantly called during scrolling 
    // therefore, to make it smooth, we should not make any expensive operations here 
    // - get element from your dataset at this position 
    // - replace the contents of the view with that element 
    holder.title.setText(pojos.get(position).getTitle()); 
    holder.desc.setText(pojos.get(position).getDesc()); 
    holder.imageView.setImageResource(pojos.get(position).getImage()); 

    // you'll need to implement this function based on the way you decide to save clicked state for each clicked view 
    if(isClickedState(position)) { 
      holder.imageView.setImageResource(R.drawable.heart_red); 
    } else { 
      // provide some default background 
      holder.imageView.setImageResource(R.drawable.default); 
    } 

    holder.imageView.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      // you'll need to implement this function to save clicked position 
      saveClickForPosition(position) 
      imageView.setImageResource(R.drawable.heart_red); 
     } 
    }); 
} 

// Return the size of your dataset (invoked by the layout manager) 
@Override 
public int getItemCount() { 
    return pojos.size(); 
} 
} 

Dieses dieses Muster der Ausgangspunkt für Ihre Debuggen sollte ordnungsgemäße Verwertung garantieren, wie folgend.

Da es auch in einer anderen Antwort erwähnt wird, müssen Sie sich den Klickzustand für jedes Element merken, und diesen Zustand im MyPojo-Objekt oder an einem anderen Ort zu halten, sollte relativ einfach zu bewerkstelligen sein.

6

einfach eine Methode in der Adapterklasse nach dem Verfahren getItemCount hinzufügen

@Override 
    public int getItemViewType(int position) { 
     return position; 
    } 

es wird das Problem

+1

Dies wird buchstäblich auch den gesamten Punkt mit einem RecyclerView brechen. Dies deaktiviert die Wiederverwendung ALLER Ansichten. Sie sagen dem RecyclerView grundsätzlich, dass jedes einzelne Element in der Liste eine eigene Ansichtszelle haben sollte, die niemals recycelt werden kann. Wenn Sie also Bilder in diese RecyclerView-Zellen laden, wird Ihre Speicherauslastung durch das Dach gehen. – Moonbloom

Verwandte Themen