Android打造專屬自定義SlidingMenu

/**
       在衆多的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) {
當不斷滑動的時候將不斷的執行此方法,你只需要將需要執行的動畫添加到這裏即可

效果~



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章