側滑選單我們見的太多了,有沒有想過有別的方式彈出選單?
比如,讓 Toolbar 變成選單?
我也不知道怎麼描述這個效果了,直接放效果圖吧:
炸不炸!
其實實現起來超簡單。
思路
看上去好像 Toolbar 變成了選單,但大家也能猜到,這裡面的旋轉選單其實和 Toolbar 是兩個控制元件,左上角的選單按鈕也是也是兩個按鈕,只不過在同樣的位置放了同樣的圖片。
所以我自定義了一個旋轉控制元件 SpringRotateMenu,繼承 FrameLayout,在這裡面實現旋轉動畫及手勢操作。
旋轉動畫
Gif 圖可能不明顯,選單展開和收起的時候是會抖一下的,有一種「DUANG」的感覺。是不是有種彈簧的感覺?沒錯,我用的就是新出的彈簧動畫(SpringAnimation)。
關於 SpringAnimation,我之前的這篇會有更詳細的介紹:
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);
}
});複製程式碼
這樣就完成啦,妥妥的。