我把 Toolbar 轉了一下變成了選單

NanBox發表於2017-04-12

側滑選單我們見的太多了,有沒有想過有別的方式彈出選單?
比如,讓 Toolbar 變成選單?

我也不知道怎麼描述這個效果了,直接放效果圖吧:

我把 Toolbar 轉了一下變成了選單

炸不炸!

其實實現起來超簡單。

思路

看上去好像 Toolbar 變成了選單,但大家也能猜到,這裡面的旋轉選單其實和 Toolbar 是兩個控制元件,左上角的選單按鈕也是也是兩個按鈕,只不過在同樣的位置放了同樣的圖片。

所以我自定義了一個旋轉控制元件 SpringRotateMenu,繼承 FrameLayout,在這裡面實現旋轉動畫及手勢操作。

旋轉動畫

Gif 圖可能不明顯,選單展開和收起的時候是會抖一下的,有一種「DUANG」的感覺。是不是有種彈簧的感覺?沒錯,我用的就是新出的彈簧動畫(SpringAnimation)。

關於 SpringAnimation,我之前的這篇會有更詳細的介紹:

實現一個帶下拉彈簧動畫的 ScrollView

SpringAnimation 支援平移、縮放、旋轉等效果,這次我們用到的是它的旋轉效果。

我們先定義展開和收起狀態的兩個角度:

private final static int ROTATE_EXPAND = 0;
private final static int ROTATE_COLLAPSE = -90;複製程式碼

然後這樣來獲取旋轉彈簧動畫:

expandAnimation = new SpringAnimation(this, SpringAnimation.ROTATION, ROTATE_EXPAND);
collapseAnimation = new SpringAnimation(this, SpringAnimation.ROTATION, ROTATE_COLLAPSE);複製程式碼

需要注意的是第三個引數。在平移動畫裡面,第三個引數是偏移量,而在旋轉動畫裡面代表的是度數。在這裡我定義了展開動畫(旋轉到0°)及收起動畫(旋轉到 -90°)。

然後提供兩個方法來設定展開和收起的按鈕:

/**
 * 設定展開按鈕
 */
public void setExpandButton(View expandButton) {
    expandButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            expand();
        }
    });
}

/**
 * 設定摺疊按鈕
 */
public void setCollapseButton(View collapseButton) {
    collapseButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            collapse();
        }
    });
}複製程式碼

展開按鈕就是 Toolbar 上的按鈕,收起按鈕則是選單上的按鈕。

展開、收起的方法也很簡單:

/**
 * 展開選單
 */
public void expand() {
    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();
            }
        });
    }
}

/**
 * 摺疊選單
 */
public void collapse() {
    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();
            }
        });
    }
}複製程式碼

其實就是讓對應的動畫執行,選單在開始展開的時候顯示,在完全收起的時候隱藏。至於這裡的 listener 是我加的一個動畫監聽器,監聽兩個動畫的開始和結束,供外部使用。

手勢操作

手勢操作就是重寫 onTouchEvent,程式碼如下:

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:
            //滑動距離
            float deltaX = event.getRawX() - mDownX;
            //設定角度
            float rotation = (deltaX / (screenWidth * 0.8f)) * 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;
}複製程式碼

核心就是將手指的橫向滑動距離轉換為旋轉角度。我的計算方法是,選單控制元件的旋轉角度,等於橫向滑動距離佔螢幕寬度的比例,乘以 -90°。至於為什麼寬度要乘以 0.8,我是為了讓手指在螢幕上滑過 80% 的寬度,就可以將選單完全收起。

還有就是手指抬起時的處理。我覺得在使用者向右滑動選單時,大部分情況下是希望將選單收起的,應該讓它更容易收起。所以我的做法是,當手指抬起時,選單豎直的角度超過 30°,就讓它執行收起的動畫,否則執行展開的動畫。

使用

佈局

使用 SpringRotateMenu 作為旋轉選單的根佈局,並設定控制元件的旋轉中心點。預設的 Toolbar 高度為 56dp,如果選單按鈕居中顯示的話,可以使用:

android:transformPivotX="28dp"
android:transformPivotY="28dp"複製程式碼

然後用 FrameLayout 將它覆蓋在 Toolbar 上面。

建議讓選單佈局的背景顏色和 Toolbar 的顏色一致,並使用同一個選單圖示,選單圖示裡面加一個引數:

android:rotation="90"複製程式碼

讓圖示旋轉九十度。

程式碼

在程式碼裡面找到我們的 SpringRotateMenu,然後簡單的設定一下,比如這樣:

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

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

    @Override
    public void expandEnd() {

    }

    @Override
    public void collapseBegin() {

    }

    @Override
    public void collapseEnd() {
        toolbar.setVisibility(View.VISIBLE);
    }
});複製程式碼

這樣就完成啦,妥妥的。

原始碼地址

相關文章