在衆多的App中都用到了SlidingMenu,使用SlidingMenu不僅可以增加顯示的內容,也讓用戶體驗更多的舒適,當然現在github上有相應的SlidingMenu的庫在其它的博客也有很多的關於SlidingMenu的介紹,也當然此文章也會有很多和別人重複,但是相信堅持總結、堅持學習,總有一天能寫出不一樣的文章,fighting!!
*/
首先附上github上第三方的庫
https://github.com/jfeinstein10/SlidingMenu
然後開始我們的自定義SlidingMenu類之旅,在開始之前我們先明確自定義類繼承什麼呢?要是使用ViewGroup的話會比較麻煩因爲你需要在類裏面進行測量,在很多自定義的控件中都會繼承其子類來實現相應的功能例如FrameLayout和HorizontalScrollView等等。
Part 1、繼承HorizontalScrollView實現
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
int dx = getScrollX();//(1)
if (dx > menuWidth / 2) {
smoothScrollTo(menuWidth, 0);
} else {
smoothScrollTo(0, 0);
}
return true;//(2)
}
return super.onTouchEvent(ev);
}
tips:
這裏使用的smoothScrollTo而不是scrollTo,純粹是爲了體驗更加的平滑。
(1)、getScrollX() 就是當前view的左上角相對於母視圖的左上角的X軸偏移量。
(2)、這裏應該返回true來屏蔽掉下面的super.onTouchEvent(ev)方法,不然的話就沒有任何的效果
ok,效果~
這樣簡易的側滑就搞定了~
如果你想要在添加一些酷炫的動畫效果的話需要重寫
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale = l * 1.0f / menuWidth; // 1 ~ 0
float fraction = 1 - scale;
當滾動發生的時候會不斷的去執行,裏面也有變化量得到的方法,其實要想實現動畫效果此時只需要一個變化量那一切動畫so easy!
平移縮放等等
ViewHelper.setTranslationX(menuContent, evaluate(fraction,menuWidth*0.5f, 0));
ViewHelper.setScaleX(menuContent, evaluate(fraction, 0.5f, 1.0f));
ViewHelper.setScaleY(menuContent, evaluate(fraction, 0.5f, 1.0f));
最後在加上顏色漸變的效果
getBackground().setColorFilter((Integer) evaluateColor(fraction, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.DST_OUT);
tips:
1、nineoldandroid庫
compile 'com.nineoldandroids:library:2.4.0'
2、Android提供的差值器
如:顏色漸變
/**
* 顏色變化過渡
* @param fraction
* @param startValue
* @param endValue
* @return
*/
public Object evaluateColor(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction * (endA - startA))) << 24) |
(int) ((startR + (int) (fraction * (endR - startR))) << 16) |
(int) ((startG + (int) (fraction * (endG - startG))) << 8) |
(int) ((startB + (int) (fraction * (endB - startB))));
}
ok,效果~
這樣就實現了繼承HorizontalScrollView的側滑菜單~
Part 2 繼承FrameLayout實現
當然我們也可以通過繼承FrameLayout或者ViewGroup來實現,針對上面的側滑接下來要說的這個大致是一樣的只不過增加了一個ViewDragHelper類
dragHelper = ViewDragHelper.create(this, new DragCallBack());
}
class DragCallBack extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
@Override
public int getViewHorizontalDragRange(View child) {
return range;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// left = oldLeft + dx;
if (child == mainContent) {
left = fixLeft(left);
}
return left;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
int newLeft = left;
if (changedView == leftContent) {
newLeft = mainContent.getLeft() + dx;
}
newLeft = fixLeft(newLeft);
if (changedView == leftContent) {
leftContent.layout(0, 0, mWidth, mHeight);
mainContent.layout(newLeft, 0, newLeft + mWidth, mHeight);
}
//執行動畫
animPerform(newLeft);
// 爲了兼容低版本, 每次修改值之後, 進行重繪
invalidate();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
// View releasedChild 被釋放的子View
// float xvel 水平方向的速度, 向右爲+
// float yvel 豎直方向的速度, 向下爲+
// 判斷執行 關閉/開啓
if (xvel == 0 && mainContent.getLeft() > range / 2.0f) {
open();
} else if (xvel > 0) {
open();
} else {
close();
}
}
tips:
1、
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
根據返回值來決定當前的child是否可以拖拽,child:當前拖拽的View;poterId:多點觸控的id
2、
public void onViewCaptured(View capturedChild, int activePointerId) {
當child被捕獲時,基本上沒有多大用可以忽略
3、
@Override
public int getViewHorizontalDragRange(View child) {
return range;
}
得到水平拖拽的範圍,這個值不會實際起作用,通過查看源碼可知只是在滑動速度上有一定的作用
4、
public int clampViewPositionHorizontal(View child, int left, int dx) {
根據建議值修正要移動的位置,left=oldleft+dx; 注意:此時還沒有發生移動
5、
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
當View位置發生改變的時候要處理的邏輯(動畫、重繪界面等) 注意:此時已經發生移動
6、
public void onViewReleased(View releasedChild, float xvel, float yvel) {
當View被釋放的時候執行此方法,可以在這裏面執行一些動畫
創建了ViewDragHelper之後,只需要在onTouch和onIntercept方法裏面將事件授權給ViewDragHelper
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 傳遞給mDragHelper
return dragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
dragHelper.processTouchEvent(event);
return true;
}
這樣也可以進行滑動了,但是通過滑動你會發現效果並不是像上面那樣平滑,原因是上面HorizontalScrollView裏面有smoothScrollTo方法,而在ViewGroup或FrameLayout裏面卻沒有,爲了也實現平滑的移動應使用Scroller類,由於ViewDragHelper類裏面有Scroller,所以只需要使用ViewDragHelper提供的方法即可
if (dragHelper.smoothSlideViewTo(mainContent, 0, 0)) {
//ViewCompat.postInvalidateOnAnimation(this);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (dragHelper.continueSettling(true)) {
// 如果返回true, 動畫還需要繼續執行
//ViewCompat.postInvalidateOnAnimation(this);
invalidate();
}
}
tips:
public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) {
參數一看便知,這裏調用invalidate方法,實際內部是回調了computeScroll()方法,然後通過在computeScroll方法裏面調用invalidate實現遞歸效果從而達到平滑移動的效果。
最後在實現動畫效果上和part1是一樣的,在onViewPositionChanged方法裏面添加即可.
效果~
Part 3 使用android.support.v4.widget.DrawerLayout實現
佈局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
tools:context="com.andly.administrator.andlyviewpager.drawer.DrawerActivity">
<include
layout="@layout/hsv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include
layout="@layout/hsv_menu"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>
爲DrawerLayout設置監聽事件
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
float fraction = slideOffset;
View mainContent = mDrawerLayout.getChildAt(0);
View menuContent = drawerView;
ViewHelper.setTranslationX(menuContent,evaluate(fraction, menuContent.getMeasuredWidth()/2,0));
ViewHelper.setScaleX(menuContent,evaluate(fraction,0.5f,1.0f));
ViewHelper.setScaleY(menuContent,evaluate(fraction,0.5f,1.0f));
ViewHelper.setScaleY(mainContent,evaluate(fraction,1.0f,0.8f));
ViewHelper.setScaleY(mainContent,evaluate(fraction,1.0f,0.8f));
ViewHelper.setTranslationX(mainContent,evaluate(fraction,0,menuContent.getMeasuredWidth()));
mDrawerLayout.getBackground().setColorFilter((Integer) evaluateColor(fraction,Color.BLACK,Color.TRANSPARENT), PorterDuff.Mode.DST_OUT);
}
@Override
public void onDrawerOpened(View drawerView) {
}
@Override
public void onDrawerClosed(View drawerView) {
//mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
}
@Override
public void onDrawerStateChanged(int newState) {
}
});
tips:
1、
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
關閉向右側滑的效果
2、
public void onDrawerSlide(View drawerView, float slideOffset) {
當不斷滑動的時候將不斷的執行此方法,你只需要將需要執行的動畫添加到這裏即可
效果~