4

My question id directly related to @Richard's one about the method onBindViewHolder() within the RecyclerView.Adapter is not called as he (and I) expected it to do.

I noticed that this method is called correctly until the end of my dataset (9 CardViews, scrolling down), but when I try to get back (scrolling up) it's not called anymore. The real problem is that in there I make my changes in the dataset and call notifyDataSetChanged(), but with this strange (to me) behavior my modifications don't take place when they are supposed to do.

The picture I attach wants to try to clarify:

  • I reach the bottom of the Rec.View (cardView - Supine: everything's fine);
  • dealing with the cards already showed completely or partially there is no problem (Supine, Gerund and Participle);
  • but when I reach the first cardView completely obscured, onBindViewHolder() is not called anymore and I can see from the debug that the dataset linked to the adapter is the "Supine" one, and here it is: the Supine cardView is showed.

Scrolling up from the bottom of the RecyclerView - Sorry for the quality

I thought that it was the exact same issue Richard faced in his question, and I tried his exact same solution: I forced setHasStableIds() to true in my Adapter's constructor,

public CardAdapter(List<Object> items){
    this.items = items;
    adapterList = new ArrayList<String>();
    formAdapt = new ConjFormAdapter(adapterList);
    itemMap = new HashMap<Object, Long>();
    setHasStableIds(true);
}

where itemMap is the Map I implement in my activity to set the unique ids of my dataset,

and overrode getItemId() this way:

public long getItemId(int position) {
    Object item = items.get(position);
    return itemMap.get(item);
}

But as you can see from the picture I still get this result: any idea, please?

Edit

The implementation of itemMap in my activity:

for(int i=0, j=0; i<conj_items.size(); i++, j++)
            conjAdapter.getItemMap().put(conj_items.get(i), (long) j);

where conj_items is the ArrayList I pass to the Adapter.

Community
  • 1
  • 1
Massimo Baldrighi
  • 365
  • 2
  • 6
  • 17
  • *any idea, please?* yes, your implementation of "unique ids" is wrong ... why don't you use "real stables ids" ? Don't you have it? – Selvin Jan 29 '15 at 15:20
  • Why not just return the position as the ID? How is `itemMap` populated? – darnmason Jan 29 '15 at 15:22
  • @darnmason: I edited my question with the missing code. To Selvin: I don't understand what you're suggesting: what the "real stable ids" you are mentioning? – Massimo Baldrighi Jan 29 '15 at 15:38
  • Just return `position` from `getItemId` if your data items don't have actual IDs – darnmason Jan 29 '15 at 15:43
  • 1
    Actually I have already tried that solution but nothing changed... Could you give me an example of "actual IDs", please? I assume those are the same "real stable ids" @Selvin was talking about! – Massimo Baldrighi Jan 29 '15 at 15:52
  • and how you compare the items? if you don't then HashMap compare references so even "the same" object is "not the same" ... please add to the question item object structure then i'll be able to tell you what you can use as "stable ids" (assumption: data come from DB/Webservice ... and should have some unique identifier ... and it should be used as stable id) – Selvin Jan 29 '15 at 16:09
  • and by "the same" is "not the same" i mean: https://ideone.com/ELhFOs – Selvin Jan 29 '15 at 16:16
  • For now just leave that I wrote that my dataset is a List of Objects (I just realized that that's not necessary) and let's say my items are just ArrayLists (thing that for the part of Rec.View pictured in the GIF is already true). And only the raw data I use to build all my application come from a DB. Anyway, you're telling me that for sure the problem are IDs "not really stable", is it correct? And referring to your previous comments, I didn't need to compare the items. – Massimo Baldrighi Jan 29 '15 at 16:53
  • I have the same problem, you solved this problem ? –  Oct 13 '15 at 12:52
  • @delive: unfortunately I had to put aside this project for some time, so I couldn't fix this issue. I decided, though, to try MaterialListView project by dexafree. I had to use it for other apps and it worked fine! [link](https://github.com/dexafree/MaterialList) – Massimo Baldrighi Oct 19 '15 at 09:17
  • Stumbled on this problem as well. If you enable stableIDs, make sure to return unique long value in getItemId() method implementation. Otherwise, it won't work and onBind will not call for the view. – Beemo Oct 20 '15 at 21:49

2 Answers2

7

When you setHasStableIds(true) you must implement getItemId(int position) with return position.

You current piece of code setHasStableIds(true) only told your adapter that items will not change for given position and adapter no need to call onBindViewHolder for this position again

Victory
  • 5,811
  • 2
  • 26
  • 45
  • 1
    `return position` is not advisable unless you know that your lists items (more exactly their "identities") will not change during updates. If it's possible that new items are added, removed, or moved, this will not work. You should use a unique identifier of the item instead. – User Dec 04 '18 at 08:28
0

I had the same exception, but it was actually caused by a different issue. After trying setHasStableIds(true) and setLayoutTransition(null), each with no luck, I realized that I was adding the view to the parent in onCreateViewHolder (which I shouldn't have been doing because the adapter takes care of that for you). I removed that, and the issue was resolved.

@Override
public TopTrayIconAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    DMImageView icon = new DMImageView(mContext);
    LinearLayout.LayoutParams faceParams = new LinearLayout.LayoutParams(ICON_WIDTH, ICON_WIDTH);
    faceParams.gravity = Gravity.CENTER;
    faceParams.leftMargin = 15;
    faceParams.rightMargin = 15;

    parent.addView(icon, faceParams);  <- THIS WAS THE ISSUE
wildcat12
  • 975
  • 6
  • 13