1

I'm using RecyclerView with CardView to display a list of Cards. Every card has 2 linearLayout (1 for the header, and 1 for the expand, the second one became visible only when the card is pressed).

When I modify the priority of a card, the card is moved in a new position of the list (I change it's position in the "array" of item, and then call adapter.notifyItemMoved(oldPosition,newPosition) ) and it works, I also called RecyclerView.scrollToPosition(newPosition) to show the new position in the list.

Now the problem, I want to expand the card after I moved it in the new position and to do so I need the linearLayout inside the card, they are in the ViewHolder that hold the card. I'm trying to use RecyclerView.findViewHolderAtPosition(newPosition), but it returns the ViewHolder only if the card is visible where I was before the "scrollTo", if the card is moved out of these "visible cards" it returns always null. (I'm also using mRecyclerView.setItemViewCacheSize(myDataset.size()) to solve other problems so I was expecting the correct ViewHolder even when it's not in vision)

public void moveTask(int oldPosition, int position, Task task) {
    if (oldPosition < position) {
        myDataset.add(position, task);
        myDataset.remove(oldPosition);
    } else {
        myDataset.remove(oldPosition);
        myDataset.add(position, task);
    }
    mAdapter.notifyItemMoved(oldPosition, position);
    mAdapter.notifyDataSetChanged();
    mRecyclerView.smoothScrollToPosition(position);
    MyViewHolder holder =(MyViewHolder)mRecyclerView.findViewHolderForPosition(position);
    mAdapter.expandCard(holder);
}

mAdapter.notifyDataSetChanged() shouldn't be called (notifyItemMoved should be enough) but sometimes it doesn't work without it.

expandCard just set the visibility of the linearLayout "expandable" to Visible and the other linearLayout "expandable" already visible to Gone.

What can I do to make it works? I'm doing it completely wrong? I hope I have explained the problem in a comprehensible way. If i need to explain better how I'm doing something I'm always here, thanks for the help.

2 Answers2

1

So I kinda resolved it, I was thinking in the wrong way.

You shouldn't try to find the right ViewHolder from "outside" like I was doing, but just "expand" it in the OnBindViewHolder when the one binding is the right one.

In the moveTask I do was I was doing before, but I set the position of the card to be opened:

public void moveTask(int oldPosition, int position, Task task) {
    if (oldPosition < position) {
        myDataset.add(position, task);
        myDataset.remove(oldPosition);
    } else {
        myDataset.remove(oldPosition);
        myDataset.add(position, task);
    }
    mAdapter.notifyItemMoved(oldPosition, position);
    //this is the important line, it set the variable
    //openPos with the right number
    mAdapter.setOpenPos(position);

    mAdapter.notifyItemChanged(position);
    mRecyclerView.scrollToPosition(position);
}

and then in the adapter's onBindViewHolder I check when I'm in the right position:

@Override
public void onBindViewHolder(MyViewHolder holder, int pos) {
    --set other fields--
    // expand the right card
    // if it's the first open it
    if (pos == 0) {
        holder.expand(holder.mHeader, holder.mExpanded);
        // and collapse the opened one
        if (openPos != -1 && openPos != pos) {
            open.collapse(open.mHeader, open.mExpanded, true);
        }
        isFirst = holder;
        open = holder;
        openPos = 0;
    } 
     // I'm in the position of the one I should open
     // expand it
     else if (openPos != -1 && pos == openPos) {
        holder.expand(holder.mHeader, holder.mExpanded);
        // and if I changed openPos with setOpenPos()
        // collapse the "old" open card
        if(openPos != open.getPosition()){
            open.collapse(open.mHeader, open.mExpanded, true);
        }
        open = holder;
    }
     // if I'm in another card just collapse it 
     else {
        holder.collapse(holder.mHeader, holder.mExpanded, true);
    }

}

And like this it works fine. It should work even without the use of "setItemViewCacheSize", but it has other strange problems without it so for now I leave it.

0

First of all, you should not call mRecyclerView.setItemViewCacheSize(myDataset.size()). It means creating a ViewHolder for each item in the list, not much different than use a LinearLayout.

Secondly, findViewHolderForPosition only checks the childrens. This will be added to docs for clarification.

In your case, mAdapter.notifyItemMoved is not enough because RV does not know it has to rebind the item. You also need to call notifyItemChanged so that RV knows the view should be rebound.

Create two view types (for simplicity, you dont have to). One collapsed, one expanded. Then when you want to move and expand the item, call:

public void moveTask(int oldPosition, int position, Task task) {
    if (oldPosition < position) {
        myDataset.add(position, task);
        myDataset.remove(oldPosition);
    } else {
        myDataset.remove(oldPosition);
        myDataset.add(position, task);
    }
    task.expanded = true;
    mAdapter.notifyItemMoved(oldPosition, position);
    mAdapter.notifyItemChanged(position);
}

//adapter
getItemViewType(int position) {
    return myDataset.get(position).expanded ? TYPE_EXPANDED : TYPE_HEADER;
}

Because you've called notifyItemChanged RV will rebind that view.

yigit
  • 37,683
  • 13
  • 72
  • 58
  • I know I shouldn't call setItemViewCacheSize, but without it we have some strange problems, for example if I expand the first card and scroll down, the 12th one (in my phone, the 14th in another phone) is also expanded. I'm not sure how to use the two view types like you suggested, because I'm using an animator to animate the transition: private void expand(LinearLayout header, RelativeLayout expanded) { expanded.setVisibility(View.VISIBLE); ... ValueAnimator animator = slideAnimator(header.getMeasuredHeight(), expanded.getMeasuredHeight(), expanded); animator.start(); } – Matteo Tessarotto Dec 24 '14 at 09:19