There is simply no need of using third party libraries. A little tweak in the method demonstrated in Google I/O 2016 and Heisenberg on this topic, does the trick.
Since notifyDataSetChanged() redraws the complete RecyclerView, notifyDataItemChanged() is a better option (not the best) because we have the position and the ViewHolder at our disposal, and notifyDataItemChanged() only redraws the particular ViewHolder at a given position.
But the problem is that the premature disappearence of the ViewHolder upon clicking and it's emergence is not eliminated even if notifyDataItemChanged() is used.
The following code does not resort to notifyDataSetChanged() or notifyDataItemChanged() and is Tested on API 23 and works like a charm when used on a RecyclerView where each ViewHolder has a CardView as it's root element:
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final boolean visibility = holder.details.getVisibility()==View.VISIBLE;
if (!visibility)
{
holder.itemView.setActivated(true);
holder.details.setVisibility(View.VISIBLE);
if (prev_expanded!=-1 && prev_expanded!=position)
{
recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.setActivated(false);
recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.findViewById(R.id.cpl_details).setVisibility(View.GONE);
}
prev_expanded = position;
}
else
{
holder.itemView.setActivated(false);
holder.details.setVisibility(View.GONE);
}
TransitionManager.beginDelayedTransition(recycler);
}
});
prev_position is an global integer initialized to -1.
details is the complete view which is shown when expanded and cloaked when collapsed.
As said, the root element of ViewHolder is a CardView with foreground and stateListAnimator attributes defined exactly as said by Heisenberg on this topic.
UPDATE: The above demonstration will collapse previosuly expanded item if one of them in expanded. To modify this behaviour and keep the an expanded item as it is even when another item is expanded, you'll need the following code.
if (row.details.getVisibility()!=View.VISIBLE)
{
row.details.setVisibility(View.VISIBLE);
row.root.setActivated(true);
row.details.animate().alpha(1).setStartDelay(500);
}
else
{
row.root.setActivated(false);
row.details.setVisibility(View.GONE);
row.details.setAlpha(0);
}
TransitionManager.beginDelayedTransition(recycler);
UPDATE: When expanding the last items on the list, it may not be brought into full visibility because the expanded portion goes below the screen. To get the full item within screen use the following code.
LinearLayoutManager manager = (LinearLayoutManager) recycler.getLayoutManager();
int distance;
View first = recycler.getChildAt(0);
int height = first.getHeight();
int current = recycler.getChildAdapterPosition(first);
int p = Math.abs(position - current);
if (p > 5) distance = (p - (p - 5)) * height;
else distance = p * height;
manager.scrollToPositionWithOffset(position, distance);
IMPORTANT: For the above demonstrations to work, one must keep in their code an instance of the RecyclerView & it's LayoutManager (the later for flexibility)