8

This is the project Próbuję uruchomić. Oto mój kod na onBindViewHolder z klasy RecyclerView.AdapterZestaw RecyclerView ambiguosWiadomość, kliknięcie jednego widoku wpływa na wiele widoków

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

    } 

Lista mnóstwo drobnych, problem się dzieje, gdy onclicklistener w ImageView nazywa.

desc.setText("clicked"); 

Powyższy wiersz powoduje zmianę pozycji na liście, na którą została kliknięta. ale gdy ta linia zostanie wykonana, zmiana odzwierciedli się w wielu elementach na liście. Co idzie źle? Na poniższych obrazkach kliknąłem element 0, tekst zmienia się na "kliknięty", a kolor jest ustawiony. Ale kiedy przewijam w dół, pozycja 12 również została dotknięta przez kliknięcie na pozycji 0. Zmieniła się tylko zmiana koloru tła, a nie zmiana tekstu. Jak mogę to zatrzymać?

enter image description here

enter image description here

I starali się rozwiązać ten problem przez długi czas, uprzejmie pobrać projekt i spróbować wykonywanie kodu zrozumieć, co dokładnie mam na myśli, jeśli moje pytanie nie jest oczywista.

Odpowiedz

11

Dzieje się tak dlatego, że poglądy dostać poddane recyklingowi i ponownie użyte.

Gdy widok zostanie ponownie przetworzony, zachowuje właściwości "starego" widoku, jeśli nie zmieni się go ponownie. Więc kiedy przewiniesz w dół do numeru 12, widok, który trzymał numer 1, zostaje poddany recyklingowi (ponieważ nie można go już zobaczyć na ekranie) i jest używany do utworzenia numeru 12. Dlatego kolor niebieski jest pod numerem 12.

Po kliknięciu przedmiotu musisz zapisać "klikniętą" wartość w swoim obiekcie POJO. Następnie, gdy element zostanie narysowany, sprawdź tę wartość i ustaw odpowiedni kolor obrazu/tła w zależności od tej wartości.

Robiłem to w poniższym kodzie, więc powinien dać przybliżone pojęcie o tym, co należy zrobić:

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

I dodałem „kliknięciu” boolean do klasy MyPojo.

public class MyPojo { 

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

Rozumiem swoje rozwiązanie. Ale dlaczego setText i setBackgroundColor działają inaczej? dlaczego setText działa tak, jak chcemy wyjść z pudełka? – 55597

+0

Nie działa dinamicznie, sry :) – delive

+1

Amen to! Dzięki za świetne wyjaśnienie :) – Cbr

0

Miałem podobny problem, (miałem zmianę numeru na wielu elementach listy zamiast jednego). Zakładam, że z powodu tego, jak działa widok kosza, udało mi się go naprawić, ustawiając wszystko, co planowałem, zmieniając na wszystko, co chcę, żeby było domyślne.

IE: jeśli chcesz zmienić tło na niebieskie, podczas ładowania listy ustaw te, które nie powinny być niebieskie lub szare (lub cokolwiek chcesz, aby były domyślne).

więc tutaj:

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

chcesz określić ustawienia domyślne

-1

tutaj spróbować użyć tego adaptera:

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

Wydaje się, że masz zamieszanie dotyczące korzystania RecyclerView dzwoniąc do findViewById w onBindViewHolder. Te drogie wyszukiwania powinny się zdarzać w onCreateViewHolder, gdzie można wyszukać wszystkie widoki i zapisać ich odniesienia do niestandardowego właściciela widoku. I poszedł do przodu, co wyglądało na kodzie w github repo i zaproponować następujące zmiany:

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

ta powinna być punktem wyjścia dla debugowania w następujący sposób ten wzór gwarantuje prawidłowego recyklingu.

Jak wspomniano w innej odpowiedzi, należy pamiętać stan kliknięcia dla każdego elementu, a utrzymanie tego stanu w obiekcie MyPojo lub w innym miejscu powinno być stosunkowo łatwe.

6

Wystarczy dodać metodę w swojej klasie adaptera po metody getItemCount

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

będzie rozwiązać problem

+1

To dosłownie przełamie cały punkt posiadania RecyclerView. Spowoduje to wyłączenie ponownego użycia WSZYSTKICH widoków. Zasadniczo mówisz RecyclerView, że każdy pojedynczy element na liście powinien mieć własną komórkę widoku, której nigdy nie można odzyskać. Więc jeśli załadujesz obrazy do tych komórek RecyclerView, twoje wykorzystanie pamięci przejdzie przez dach. – Moonbloom

Powiązane problemy