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)