We see a lot of slideshow menus. Have you ever thought of another way to pop up menus? For example, make the Toolbar a menu?

I don’t know how to describe this effect, so let’s just show it:

Fry not fry!

It’s actually super simple to implement.

Train of thought

It looks like the Toolbar has become a menu, but as you can guess, the rotation menu is actually a separate control from the Toolbar, as is the menu button in the upper left corner, with the same image in the same place.

So I customized a rotation control SpringRotateMenu, inherit FrameLayout, in this to achieve rotation animation and gesture operations.

Rotating animation

Gifs may not be obvious, but they shake when the menu is expanded and folded, giving it a “DUANG” feel. Does it feel like a spring? Yes, I’m using the new SpringAnimation.

I have a more detailed introduction to SpringAnimation in my previous article:

Implement a ScrollView with a drop-down spring animation

SpringAnimation supports panning, scaling, and rotation effects, and this time we will use its rotation effect.

Let’s first define two angles of expansion and collapse:

private final static int ROTATE_EXPAND = 0;
private final static int ROTATE_COLLAPSE = -90;Copy the code

Then get the rotating spring animation like this:

expandAnimation = new SpringAnimation(this, SpringAnimation.ROTATION, ROTATE_EXPAND);
collapseAnimation = new SpringAnimation(this, SpringAnimation.ROTATION, ROTATE_COLLAPSE);Copy the code

Note the third parameter. In a pan animation, the third parameter is the offset, while in a rotation animation it represents the degree. Here I define unfolding animation (rotate to 0°) and unwinding animation (rotate to -90°).

It then provides two methods to set the expand and collapse buttons:

/** * sets the expansion button */
public void setExpandButton(View expandButton) {
    expandButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) { expand(); }}); }/** * sets the collapse button */
public void setCollapseButton(View collapseButton) {
    collapseButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) { collapse(); }}); }Copy the code

The expand button is the Toolbar button, and the collapse button is the menu button.

The way to expand and collapse is also simple:

/** * expand menu */
public void expand(a) {
    setVisibility(VISIBLE);
    expandAnimation.start();
    if(listener ! =null) {
        listener.expandBegin();
        collapseAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() {
            @Override
            public void onAnimationEnd(DynamicAnimation animation,
                                       boolean canceled,
                                       float value,
                                       float velocity) { setVisibility(INVISIBLE); listener.expandEnd(); }}); }}/** ** collapse menu */
public void collapse(a) {
    collapseAnimation.start();
    if(listener ! =null) {
        listener.collapseBegin();
        collapseAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() {
            @Override
            public void onAnimationEnd(DynamicAnimation animation,
                                       boolean canceled,
                                       float value,
                                       float velocity) { listener.collapseEnd(); }}); }}Copy the code

In fact, the corresponding animation is executed, the menu is displayed when it starts to expand, and hidden when it is completely closed. The listener here is an animation listener that I added to listen for the start and end of two animations for external use.

gestures

The gesture is to override onTouchEvent as follows:

private float mDownX;

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (expandAnimation.isRunning() || collapseAnimation.isRunning()) {
        return super.onTouchEvent(event);
    }
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mDownX = event.getRawX();
            break;
        case MotionEvent.ACTION_MOVE:
            // Slide distance
            float deltaX = event.getRawX() - mDownX;
            // Set the Angle
            float rotation = (deltaX / (screenWidth * 0.8 f)) * ROTATE_COLLAPSE;
            if (rotation <= ROTATE_EXPAND && rotation >= ROTATE_COLLAPSE) {
                setRotation(rotation);
            } else if (rotation > ROTATE_EXPAND) {
                setRotation(ROTATE_EXPAND);
            } else if (rotation < ROTATE_COLLAPSE) {
                setRotation(ROTATE_COLLAPSE);
            }
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            if (getRotation() < ROTATE_COLLAPSE / 3) {
                collapse();
            } else {
                expand();
            }
            break;
    }
    return true;
}Copy the code

The key is to convert the horizontal sliding distance of the finger into the rotation Angle. My calculation is that the rotation Angle of the menu control is equal to the ratio of the horizontal sliding distance to the screen width, multiplied by -90°. As for why I multiplied the width by 0.8, I did it so that I could slide my finger across 80% of the screen width to fold up the menu completely.

There is also the handling of finger lifting. I think most of the time when a user swipes a menu to the right, they want it to go away, so it should be easier to go away. So what I do is, when the finger is up and the menu is more than 30 degrees vertical, I tell it to do the folded animation, otherwise I do the expanded animation.

use

layout

Use SpringRotateMenu as the root layout of the rotation menu and set the rotation center point of the control. The default Toolbar height is 56dp, and if the menu button is centered, you can use:

android:transformPivotX="28dp"
android:transformPivotY="28dp"Copy the code

Then overlay it over the Toolbar with FrameLayout.

It is recommended that the background color of the menu layout be the same as the Toolbar color and use the same menu icon with a parameter inside:

android:rotation="90"Copy the code

Rotate the icon 90 degrees.

code

Find our SpringRotateMenu in the code and simply set it up like this:

springRotateMenu.setExpandButton(findViewById(R.id.iv_menu));
springRotateMenu.setCollapseButton(springRotateMenu.findViewById(R.id.iv_menu));
springRotateMenu.setAnimationListener(new SpringRotateMenu.OnAnimationListener() {

    @Override
    public void expandBegin(a) {
        toolbar.setVisibility(View.INVISIBLE);
    }

    @Override
    public void expandEnd(a) {}@Override
    public void collapseBegin(a) {}@Override
    public void collapseEnd(a) { toolbar.setVisibility(View.VISIBLE); }});Copy the code

There we go. No problem.

The source address