yuliskov/SmartTube · error · IllegalStateException
Cannot settleCapturedViewAt outside of a call to Callback#on
Error message
Cannot settleCapturedViewAt outside of a call to Callback#onViewReleased
What it means
settleCapturedViewAt() animates the currently captured view to its resting position using the release velocity tracked by the helper. ViewDragHelper sets mReleaseInProgress only while dispatching Callback#onViewReleased; calling settle outside that window means there is no captured view/velocity to settle, so it throws IllegalStateException.
Source
Thrown at slidableactivity/src/main/java/com/r0adkll/slidr/util/ViewDragHelper.java:526
mCapturedView = null;
}
return continueSliding;
}
/**
* Settle the captured view at the given (left, top) position.
* The appropriate velocity from prior motion will be taken into account.
* If this method returns true, the caller should invoke {@link #continueSettling(boolean)}
* on each subsequent frame to continue the motion until it returns false. If this method
* returns false there is no further work to do to complete the movement.
*
* @param finalLeft Settled left edge position for the captured view
* @param finalTop Settled top edge position for the captured view
* @return true if animation should continue through {@link #continueSettling(boolean)} calls
*/
public boolean settleCapturedViewAt(int finalLeft, int finalTop) {
if (!mReleaseInProgress) {
throw new IllegalStateException("Cannot settleCapturedViewAt outside of a call to " +
"Callback#onViewReleased");
}
return forceSettleCapturedViewAt(finalLeft, finalTop,
(int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
(int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId));
}
/**
* Settle the captured view at the given (left, top) position.
*
* @param finalLeft Target left position for the captured view
* @param finalTop Target top position for the captured view
* @param xvel Horizontal velocity
* @param yvel Vertical velocity
* @return true if animation should continue through {@link #continueSettling(boolean)} calls
*/
private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) {
final int startLeft = mCapturedView.getLeft();View on GitHub (pinned to 3de8d90593)
Solutions
- Move the settleCapturedViewAt call inside Callback#onViewReleased — that is the only sanctioned window.
- For programmatic moves outside a release, use smoothSlideViewTo(child, finalLeft, finalTop) (no such gate) and drive it with continueSettling(true) each frame.
- Keep a reference to the draggable child so smoothSlideViewTo can target it when no view is captured.
Example fix
// before — outside any release
closeButton.setOnClickListener(v ->
mDragHelper.settleCapturedViewAt(getWidth(), getTop())); // throws IllegalStateException
// after — programmatic move uses smoothSlideViewTo
closeButton.setOnClickListener(v -> {
mDragHelper.smoothSlideViewTo(mDraggableChild, getWidth(), mDraggableChild.getTop());
ViewCompat.postInvalidateOnAnimation(mParentView);
});
// settle stays inside the release callback only
@Override public void onViewReleased(View child, float xvel, float yvel) {
mDragHelper.settleCapturedViewAt(targetLeft(child, xvel), child.getTop());
ViewCompat.postInvalidateOnAnimation(mParentView);
} Defensive patterns
Strategy: validation
Validate before calling
// legal window only: inside Callback#onViewReleased
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (mDragHelper.getCapturedView() != null) {
mDragHelper.settleCapturedViewAt(targetLeft(releasedChild, xvel), releasedChild.getTop());
ViewCompat.postInvalidateOnAnimation(mParentView);
}
}
// everywhere else, use the ungated API:
boolean ok = mDragHelper.smoothSlideViewTo(child, finalLeft, finalTop); Prevention
- settleCapturedViewAt only inside onViewReleased — treat it as part of the release contract.
- Programmatic moves use smoothSlideViewTo + continueSettling(true) each frame.
- Keep a field with the draggable child so you never need settle outside a release.
When it happens
Trigger: Invoking settleCapturedViewAt(...) from a click handler, menu item, animation tick, or any code path other than inside your ViewDragHelper.Callback's onViewReleased(releasedChild, xvel, yvel) override.
Common situations: Adding a 'close' button that tries to snap the sliding panel shut; porting Scroller-based snap logic and reusing the same call; sharing one method between user-release and programmatic resets.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot flingCapturedView outside of a call to Callback#onVie
- Parent view may not be null
- Callback may not be null
- captureChildView: parameter must be a descendant of the View
- VideoGridFragment doesn't attached to the activity
AI-assisted analysis of yuliskov/SmartTube@3de8d90593 (2026-08-22).
Data as JSON: /api/errors/7a40a0c2da094dcb.
Report an issue: GitHub.