material-components/material-components-android · error · UnsupportedOperationException
Only one RevealableListItem with start gravity is supported.
Error message
Only one RevealableListItem with start gravity is supported.
What it means
ListItemLayout supports at most one RevealableListItem child aligned to the start (left gravity in LTR). addView stores it in swipeToRevealLayoutLeft; a second start-aligned RevealableListItem makes the two candidates ambiguous and the layout throws UnsupportedOperationException.
Source
Thrown at lib/java/com/google/android/material/listitem/ListItemLayout.java:250
positionState = MIDDLE_STATE_SET;
break;
}
refreshDrawableState();
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
if (child instanceof RevealableListItem) {
if (isRightAligned(child)) {
if (swipeToRevealLayoutRight != null) {
throw new UnsupportedOperationException(
"Only one RevealableListItem with end gravity is supported.");
}
swipeToRevealLayoutRight = child;
} else {
if (swipeToRevealLayoutLeft != null) {
throw new UnsupportedOperationException(
"Only one RevealableListItem with start gravity is supported.");
}
swipeToRevealLayoutLeft = child;
}
// Start the reveal view at a desired width of 0
((RevealableListItem) child).setRevealedWidth(0);
// Make sure reveal view has lower elevation
child.setElevation(getElevation() - 1);
} else if (contentView != null && child instanceof SwipeableListItem) {
throw new UnsupportedOperationException(
"Only one SwipeableListItem view is allowed in a ListItemLayout.");
} else if (child instanceof SwipeableListItem) {
contentView = child;
}
}
@Override
public void onViewRemoved(View child) {View on GitHub (pinned to ac7e18efee)
Solutions
- Have exactly one start-gravity RevealableListItem; embed multiple start actions within it.
- Dynamically replacing: remove the previous start reveal view before adding a new one.
- Run a layout lint/inspection for duplicate RevealableListItem siblings with identical gravity.
Example fix
// before (dynamic)
listItemLayout.addView(newStartReveal); // old start reveal still attached -> throws
// after
if (listItemLayout.findViewById(R.id.start_reveal) != null) {
listItemLayout.removeView(listItemLayout.findViewById(R.id.start_reveal));
}
listItemLayout.addView(newStartReveal); Defensive patterns
Strategy: validation
Validate before calling
View existing = listItemLayout.findViewById(R.id.start_reveal); if (existing != null) listItemLayout.removeView(existing); listItemLayout.addView(newStartRevealView);
Prevention
- Exactly one start-gravity RevealableListItem per layout.
- Give reveal children stable ids so duplicates are detectable before addView.
- Lint custom XML for repeated reveal children with identical gravity.
When it happens
Trigger: Adding two RevealableListItem children both with start/left gravity to one ListItemLayout, via XML or addView; swapping a reveal view at runtime without removing the old one.
Common situations: A/B testing two reveal designs where the old view is not removed; refactoring layouts and leaving a stray reveal container with layout_gravity="start".
Related errors
- Only one RevealableListItem with end gravity is supported.
- Only one SwipeableListItem view is allowed in a ListItemLayo
- No RevealableListItem with gravity {}
- Invalid state to get swipe offset: {}
- revealView must be a child of ListItemLayout.
AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14).
Data as JSON: /api/errors/e1d8863183e6e468.
Report an issue: GitHub.