Android-自定義側滑菜單

效果圖:android

 

須要繼承ViewGroup,由於包含了子控件,菜單子控件 與 主頁面子控件ide

Activity Xml佈局相關:佈局

<!-- 自定義側滑菜單 SlideMenu -->

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <view.custom.heimacustomview.my_slide_menu.MySlideMenu
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include layout="@layout/slide_menu" />

        <include layout="@layout/slide_main" />

    </view.custom.heimacustomview.my_slide_menu.MySlideMenu>

</LinearLayout>

 

菜單界面子控件佈局相關:post

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="300dp"
    android:layout_height="match_parent"
    android:background="#ffff80"
    android:orientation="vertical">

    <!-- 這裏的View不能寫 wrap_content 否則在測量後,一直是TextView的寬度 -->
    <TextView
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="側滑菜單"
        android:gravity="center_horizontal"
        />
    
</LinearLayout>

 

主頁界面子控件佈局相關:spa

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#80ffff">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我是首頁"
        android:textSize="30sp"
        android:layout_centerInParent="true"
        android:layout_centerHorizontal="true"/>

</RelativeLayout>

 

自定義側滑菜單類相關:code

package view.custom.heimacustomview.my_slide_menu;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

public class MySlideMenu extends ViewGroup {

    private static final String TAG = MySlideMenu.class.getSimpleName();

    private Scroller mScroller;

    /**
     * 以往我是在onTouchEvent方法中處理,此次我採用手勢識別器
     * 注意:手勢識別器只處理事件相關,不能攔截時間,至關於只過濾處理水,並不能阻止水
     */
    private GestureDetector mGestureDetector;

    float mDistanceX;
    float countX = 0f;

    /**
     * 構造方法,由佈局xml指引來初始化,並傳入屬性集合
     * @param context
     * @param attrs
     */
    public MySlideMenu(final Context context, AttributeSet attrs) {
        super(context, attrs);

        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){

            /**
             * 滑動過程當中的方法
             * @param e1 能夠理解爲 手指按下記錄用到的MotionEvent
             * @param e2 能夠理解爲 手機滑到某個點記錄用到的MotionEvent
             * @param distanceX 計算好的X軸距離值
             * @param distanceY 計算好的Y軸距離值
             * @return
             */
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                // return super.onScroll(e1, e2, distanceX, distanceY);
                /* if (getScrollX() < 0) {
                    distanceX = 0;
                }*/

                Log.d(TAG, "onScroll() distanceX:" + distanceX + " getScrollX:" + getScrollX());
                mDistanceX = distanceX;
                countX += distanceX;

                if (countX > 0) {
                    countX = 0;
                } else if (countX < -slideMenuView.getMeasuredWidth()) {
                    countX = -slideMenuView.getMeasuredWidth();
                }

                scrollTo((int) countX, getScrollY());

                return true;
            }

            /*@Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                // return super.onFling(e1, e2, velocityX, velocityY);
                Log.d(TAG, "onFling() velocityX:" + velocityX + " velocityY:" + velocityY);

                if (countX > -slideMenuView.getMeasuredWidth() / 2) {
                    countX = 0;
                } else if (countX < -slideMenuView.getMeasuredWidth() / 2) {
                    countX = -slideMenuView.getMeasuredWidth();
                }

                int dx =  (int) countX - getScrollX();

                // scrollTo((int) countX, getScrollY());
                mScroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(1000));
                invalidate();
                return true;
            }*/

        });

        // 實現彈性滑動,不要滑動那麼生硬
        mScroller = new Scroller(context);
    }

    /**
     * 定義兩個子控件
     */
    private View slideMenuView;
    private View slideMainView;

    /**
     * 當佈局xml加載完成後,就會調用此方法,而後此方法再去獲取子控件View
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        // 獲取子控件View
        slideMenuView = getChildAt(0);
        slideMainView = getChildAt(1);
    }

    /**
     * 測量方法
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // 測量菜單子控件的高和寬,寬是佈局文件設置的寬度,高度獲取當前MySlideMenu的高度(與屏幕保存一致高度)
        int slideMenuViewWidth = slideMenuView.getLayoutParams().width;
        Log.d(TAG, "獲取佈局中的寬度 slideMenuViewWidth:" + slideMenuViewWidth);
        if (null != slideMenuView) {
            slideMenuView.measure(slideMenuViewWidth, heightMeasureSpec);
        }

        // 測量主頁子控件的高和寬,寬度高度獲取當前MySlideMenu的寬度高度(與屏幕保存一致高度)
        if (null != slideMainView) {
            slideMainView.measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    /**
     * 給子控件位置排版,固定好位置
     * @param changed 當發生改變的時候
     * @param l 父控件距離左手邊左邊線距離
     * @param t 父控件距離頂邊頂邊線距離
     * @param r 父控件距離左手邊右邊線距離
     * @param b 父控件距離頂部邊底部線距離
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        slideMenuView = getChildAt(0);

        Log.d(TAG, "slideMenuView.getMeasuredWidth():" + slideMenuView.getMeasuredWidth());
        Log.d(TAG, "slideMainView.getMeasuredWidth():" + slideMainView.getMeasuredWidth());

        // 給菜單子控件固定好位置
        slideMenuView.layout(-slideMenuView.getMeasuredWidth(), 0, 0, slideMenuView.getMeasuredHeight());

        // 給主頁子控件固定好位置 r和父控件保持一直,b和父控件保持一致
        slideMainView.layout(0, 0, r, b);
    }

    private float downX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        mGestureDetector.onTouchEvent(event);

        if (event.getAction() == MotionEvent.ACTION_UP) {
            if (countX > -slideMenuView.getMeasuredWidth() / 2) {
                countX = 0;
            } else if (countX < -slideMenuView.getMeasuredWidth() / 2) {
                countX = -slideMenuView.getMeasuredWidth();
            }

            int dx =  (int) countX - getScrollX();

            // scrollTo((int) countX, getScrollY()); // 這種方式體驗過於生硬

            mScroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(1000));
            invalidate();
        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
            downX = event.getX();
        }
        return true;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            int currX = mScroller.getCurrX();
            scrollTo(currX, mScroller.getCurrY());
            postInvalidate();
        }
    }
}
相關文章
相關標籤/搜索