Mordag
Mordag

Reputation: 517

BottomSheet animation duration

I am using the BottomSheet view for one of my layouts. For a very specific case, I need the animation duration of the BottomSheet which slides in the layout via the setState(...) method. It should be something around 400ms but I would prefer not to have a "magic" number for that case.

Upvotes: 4

Views: 8280

Answers (2)

Michał Jaroń
Michał Jaroń

Reputation: 624

The duration of BottomSheetBehavior animation triggered by setState or touch events depends on various conditions.

BottomSheetBehavior internally uses ViewDragHelper which determines animation duration time inside computeAxisDuration() private method. This duration depends on some arbitrary values and cannot change it by official API:

public class ViewDragHelper {
    ...
    private static final int BASE_SETTLE_DURATION = 256; // ms
    private static final int MAX_SETTLE_DURATION = 6000; // ms
    ...
}

The hack solution to change show/hide animation time is...

  • Copy whole BottomSheetBehavior and ViewDragHelper classes souce code to your project (with different package name)
  • In your file BottomSheetBehavior.java - change import ViewDragHelper to use your ViewDragHelper instead of original Android API class.
  • In file ViewDragHelper.java - change duration returned by computeAxisDuration to satisfy your requirements, e.g. return exactly 400 ms.
  • Use your BottomSheetBehavior class instead of original one.

It is very bad solution but it works in my project. Details below.

File ViewDragHelper.java

public class ViewDragHelper {
    // Additional fields and methods defined.
    private int mSettleDuration = BASE_SETTLE_DURATION;
    public void setDurationSpeedFactor(final float factor) {
        mSettleDuration = (int)(factor * BASE_SETTLE_DURATION);
    }
    private boolean mSkipAnimation = false;
    public void setSkipAnimation(final boolean skipAnimation) {
        this.mSkipAnimation = skipAnimation;
    }
    // Modified version of private function.
    private int computeAxisDuration(int delta, int velocity, int motionRange) {
        if (delta == 0) {
            return 0;
        }
    
        final int width = mParentView.getWidth();
        final int halfWidth = width / 2;
        final float distanceRatio = Math.min(1f, (float) Math.abs(delta) / width);
        final float distance = halfWidth + halfWidth
            * distanceInfluenceForSnapDuration(distanceRatio);
        
        int duration;
        velocity = Math.abs(velocity);
        if (velocity > 0) {
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
        } else {
            final float range = (float) Math.abs(delta) / motionRange;
            duration = (int) ((range + 1) * mSettleDuration);
        }
        return Math.min(duration, MAX_SETTLE_DURATION);
    }
    // Rest of original class body here...
}

File BottomSheetBehavior.java

// Comment out original ViewDragHelper and use above modified version.
//import androidx.customview.widget.ViewDragHelper;
import your.custom.ViewDragHelper;

public class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {

    /**
     * Allows to set up ViewDragHelper immediately or when it is created.
     */
    public interface IDragHelperConfig {
        void onDragHelperCreated(ViewDragHelper viewDragHelper);
    }
    private IDragHelperConfig iDragHelperConfig;
    public void setIDragHelperConfig(IDragHelperConfig iDragHelperConfig) {
        this.iDragHelperConfig = iDragHelperConfig;
        if (viewDragHelper != null) {
            iDragHelperConfig.onDragHelperCreated(viewDragHelper);
        }
    }
    //...
    public boolean onLayoutChild(...) {
        if (viewDragHelper == null) {
            viewDragHelper = ViewDragHelper.create(parent, dragCallback);
            if (iDragHelperConfig != null) {
                iDragHelperConfig.onDragHelperCreated(viewDragHelper);
            }
        }
    }
    //...
}

Sample code inside any android View initialization

    val params: CoordinatorLayout.LayoutParams = myLayout.layoutParams as CoordinatorLayout.LayoutParams
    val behavior = BottomSheetBehavior<FrameLayout>(myLayout.context, null)
    // Optionally, set the height of the bottom sheet when it is collapsed (usefull for experiments).
    // behavior.peekHeight = 20
    
    // Configure bottom sheet behavior.
    behavior.setIDragHelperConfig {
        // Set how many times to slow the speed of the animation
        // (relative to original Android animation speed).
        it.setDurationSpeedFactor(20f)

        // Optionally, turn off animation
        // Useful when entering a new activity and 
        // initial animation is not desired.
        it.setSkipAnimation(false)
    }
    params.behavior = behavior

Upvotes: 3

Umer Farooq
Umer Farooq

Reputation: 180

Yes you can add animations in bottom sheet. h

      // init the bottom sheet behavior
        BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);

        // change the state of the bottom sheet
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);

        // set callback for changes
        bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                // Called every time when the bottom sheet changes its state.
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                if (isAdded()) {
                    animateBottomSheetArrows(slideOffset);
                }
            }
        });
    }

    private void animateBottomSheetArrows(float slideOffset) {
        // Animate counter-clockwise
        mLeftArrow.setRotation(slideOffset * -180);
        // Animate clockwise
        mRightArrow.setRotation(slideOffset * 180); 
}

Upvotes: 0

Related Questions