Skip to content

[BottomSheetBehavior] Bottom Sheet State always set to STATE_EXPANDED when swiping up #2335

@SilverEnd

Description

@SilverEnd

Description:
When Bottom Sheet's flag fitToContents is set to default value true, the bottom sheet will always set its state to STATE_EXPANDED whenever a swipe up gesture is performed on the bottom sheet, even tho the content is not long enough to be expanded to full screen, i.e. swiping up result in NO VISUAL CHANGES to the UI.

A side effect of the above is that, in a "non-expandable" bottom sheet, when we do swipe up gesture, it will think itself been set to STATE_EXPANDED and then invalidate the bottom sheet drawable, which will make the top corners of the bottom sheet flat (from rounded top corners)

Take the blow recording as a example:

STATE_COLLAPSED -> STATE_EXPANDED by swiping up

device-2021-09-06-133302.mp4

STATE_EXPANDED -> STATE_COLLAPSED by swiping down

device-2021-09-06-133438.mp4

STATE_EXPANDED -> STATE_COLLAPSED by swiping down on BottomSheetDialog, release it and let itself auto settle

device-2021-09-06-133622.mp4

Expected behavior:
This might be controversial but there are two directions we would expect:

  • NEVER have STATE_COLLAPSED for a short bottom sheet that fitToContent == true, (this will involve fix for other use cases to prevent the state becomes STATE_COLLAPSED) OR
  • Prevent the bottom sheet's state to become STATE_EXPANDED when we tries to swipe up

Source code:

@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
  int top;
  @State int targetState;
  if (yvel < 0) { // Moving up
    if (fitToContents) {
      top = fitToContentsOffset;
      targetState = STATE_EXPANDED;    <<<-------- This is where we need to ad extra logic
    } else {
      int currentTop = releasedChild.getTop();
      if (currentTop > halfExpandedOffset) {
        top = halfExpandedOffset;
        targetState = STATE_HALF_EXPANDED;
      } else {
        top = getExpandedOffset();
        targetState = STATE_EXPANDED;
      }
    }
  } else if (hideable && shouldHide(releasedChild, yvel)) {
    ...
  } else if (yvel == 0.f || Math.abs(xvel) > Math.abs(yvel)) {
    // If the Y velocity is 0 or the swipe was mostly horizontal indicated by the X velocity
    // being greater than the Y velocity, settle to the nearest correct height.
    ...
  } else { // Moving Down
   ...
  }
  startSettlingAnimation(releasedChild, targetState, top, true);
}

Proposed Solution for the 2nd approach above (as it will not impact other uses cases)

if (yvel < 0) { // Moving up
  if (fitToContents) {
    // Keep it at collapsed state when the collapsed height and expanded height are the same,
    // i.e. content is not long enough to visually expand the bottom sheet
    if (fitToContentsOffset == collapsedOffset) {
      top = collapsedOffset;
      targetState = STATE_COLLAPSED;
    } else {
      top = fitToContentsOffset;
      targetState = STATE_EXPANDED;
    }
  }
} else if (...) { ... } 

Android API version: API 29

Material Library version: 1.5.0-alpha02

Device: ONE PLUS 8 PRO

To help us triage faster, please check to make sure you are using the latest version of the library.

We also happily accept pull requests.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions