Android-自定義滑動菜單(抽屜效果)

ReaEtienne 8年前發布 | 10K 次閱讀 Android開發 移動開發

來自: http://blog.csdn.net/qxs965266509/article/details/45200289


在Andoird使用Android自帶的那些組件,像SlidingDrawer和DrawerLayout都是抽屜效果的菜單,但是在項目很多要實現的功能都收到Android這些自帶組件的限制,導致很難完成項目的需求,自定義的組件,各方面都在自己的控制之下,從而根據需求做出調整。想要實現好的效果,基本上都的基于Android的OnTouch事件自己實現響應的功能。
首先,給大家先看一下整體的效果:
向右滑動,菜單打開的效果
向左滑動,菜單關閉的效果
滑動的加速度效果都是有的,具體的體驗,只能安裝后才能查看。
接下來,看代碼:
代碼從MainActivity延伸出了2個類:MainController和MainView,MainController來處理控制層、MainView來操作展示層。
主要代碼:
MainActivity的代碼:

package com.example.wz;

import com.example.wz.controller.MainController; import com.example.wz.util.MyLog; import com.example.wz.view.MainView;

import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent;

public class MainActivity extends Activity {

public MyLog log = new MyLog(this, true);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    log.e("歡迎你加入測試項目.");
    link();
}

public MainController mainController;
public MainView mainView;

private void link() {
    this.mainController = new MainController(this);
    this.mainView = new MainView(this);

    this.mainController.thisView = this.mainView;
    this.mainView.thisController = this.mainController;

    this.mainView.initViews();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    return mainController.onTouchEvent(event);
}

}</pre>

MainController的代碼:

package com.example.wz.controller;

import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent;

import com.example.wz.MainActivity; import com.example.wz.util.MyLog; import com.example.wz.util.OpenLooper; import com.example.wz.util.OpenLooper.LoopCallback; import com.example.wz.view.MainView;

public class MainController {

public MyLog log = new MyLog(this, true);

public MainActivity mainActivity;
public MainController thisController;
public MainView thisView;

public GestureDetector mGesture;

public MainController(MainActivity mainActivity) {
    this.mainActivity = mainActivity;
    this.thisController = this;

    mGesture = new GestureDetector(mainActivity, new GestureListener());
    openLooper = new OpenLooper();
    openLooper.createOpenLooper();
    loopCallback = new ListLoopCallback(openLooper);
    openLooper.loopCallback = loopCallback;
}

public class TouchStatus {
    public int None = 4, Down = 1, Horizontal = 2, Vertical = 3, Up = 4;// LongPress = 5
    public int state = None;
}

public TouchStatus touchStatus = new TouchStatus();

public class BodyStatus {
    public int Fixed = 0, Dragging = 1, Homing = 2, FlingHoming = 3, BoundaryHoming = 4;
    public int state = Fixed;
}

public BodyStatus bodyStatus = new BodyStatus();

public class DrawStatus {
    public int Closed = 0, Open = 1, GoClosing = 2, GoOpening = 3;
    public int state = Closed;
}

public DrawStatus drawStatus = new DrawStatus();

public class AreaStatus {
    public int A = 0, B = 1;
    public int state = A;
}

public AreaStatus areaStatus = new AreaStatus();

public float touch_pre_x;
public float touch_pre_y;

public float currentTranslateX;

public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();

    float x = event.getX();
    float y = event.getY();

    if (action == MotionEvent.ACTION_DOWN) {
        this.touch_pre_x = x;
        this.touch_pre_y = y;

        if (touchStatus.state == touchStatus.None) {
            touchStatus.state = touchStatus.Down;
            log.e("Down ");
            if (x > thisView.maxTranslateX) {
                areaStatus.state = areaStatus.B;
            } else if (x <= thisView.maxTranslateX) {
                areaStatus.state = areaStatus.A;
            }
        }
    } else if (action == MotionEvent.ACTION_MOVE) {
        float Δy = (y - touch_pre_y);
        float Δx = (x - touch_pre_x);
        if (touchStatus.state == touchStatus.Down) {
            if (Δx * Δx + Δy * Δy > 400) {
                if (Δx * Δx > Δy * Δy) {
                    touchStatus.state = touchStatus.Horizontal;
                } else {
                    touchStatus.state = touchStatus.Vertical;
                }
                touch_pre_x = x;
                touch_pre_y = y;
                log.e("ACTION_MOVE ");
            }
        } else if (touchStatus.state == touchStatus.Horizontal) {
            currentTranslateX += Δx;
            this.touch_pre_x = x;
            this.touch_pre_y = y;
            if (currentTranslateX - thisView.maxTranslateX <= 0 && currentTranslateX >= 0) {
                setPosition();
            }
            log.e("Horizontal");
            bodyStatus.state = bodyStatus.Dragging;
        } else if (touchStatus.state == touchStatus.Vertical) {
            log.e("Vertical");
            bodyStatus.state = bodyStatus.Dragging;
        }
    } else if (action == MotionEvent.ACTION_UP) {
        log.e("ACTION_UP");
        if (bodyStatus.state == bodyStatus.Dragging) {
            if (touchStatus.state == touchStatus.Horizontal) {
                bodyStatus.state = bodyStatus.Homing;
                openLooper.start();
            } else if (touchStatus.state == touchStatus.Vertical) {
                if (drawStatus.state == drawStatus.Open && areaStatus.state == areaStatus.B) {
                    bodyStatus.state = bodyStatus.Homing;
                    drawStatus.state = drawStatus.GoClosing;
                    openLooper.start();
                }
            }
        } else if (touchStatus.state == touchStatus.Down && areaStatus.state == areaStatus.B) {
            bodyStatus.state = bodyStatus.Homing;
            drawStatus.state = drawStatus.GoClosing;
            openLooper.start();
        }
        touchStatus.state = touchStatus.Up;
    }
    mGesture.onTouchEvent(event);
    return true;
}

class GestureListener extends SimpleOnGestureListener {

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        if (velocityX * velocityX + velocityY * velocityY > 250000) {
            if (velocityX * velocityX > velocityY * velocityY) {
                log.e("velocityX--" + velocityX);
                if (drawStatus.state == drawStatus.Closed && velocityX < 0) {
                } else if (drawStatus.state == drawStatus.Open && velocityX > 0) {
                } else {
                    dxSpeed = velocityX;
                    bodyStatus.state = bodyStatus.FlingHoming;
                    openLooper.start();
                }
            } else {
                log.e("velocityY");
            }
        }
        return true;
    }

    public void onLongPress(MotionEvent event) {
    }

    public boolean onDoubleTap(MotionEvent event) {
        return false;
    }

    public boolean onDoubleTapEvent(MotionEvent event) {
        return false;
    }

    public boolean onSingleTapUp(MotionEvent event) {
        return false;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        return false;
    }

    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }
}

public void setPosition() {
    thisView.v1.setTranslationX(currentTranslateX - thisView.maxTranslateX);
    thisView.v2.setTranslationX(Math.abs(currentTranslateX));
}

float transleteSpeed = 3f;
OpenLooper openLooper = null;
LoopCallback loopCallback = null;

public class ListLoopCallback extends LoopCallback {
    public ListLoopCallback(OpenLooper openLooper) {
        openLooper.super();
    }

    @Override
    public void loop(double ellapsedMillis) {
        if (bodyStatus.state == bodyStatus.Homing) {
            hommingView((float) ellapsedMillis);
        } else if (bodyStatus.state == bodyStatus.FlingHoming) {
            flingHomingView((float) ellapsedMillis);
        }
    }
}

public float ratio = 0.0008f;

public void flingHomingView(float ellapsedMillis) {
    float distance = (float) ellapsedMillis * transleteSpeed;
    boolean isStop = false;
    if (drawStatus.state == drawStatus.Closed) {
        drawStatus.state = drawStatus.GoOpening;
    } else if (drawStatus.state == drawStatus.Open) {
        drawStatus.state = drawStatus.GoClosing;
    }
    if (drawStatus.state == drawStatus.GoClosing) {
        this.currentTranslateX -= distance;
        if (this.currentTranslateX <= 0) {
            this.currentTranslateX = 0;
            drawStatus.state = drawStatus.Closed;
            isStop = true;
            log.e("-------------1");
        }
    } else if (drawStatus.state == drawStatus.GoOpening) {
        this.currentTranslateX += distance;
        if (this.currentTranslateX >= thisView.maxTranslateX) {
            this.currentTranslateX = thisView.maxTranslateX;
            drawStatus.state = drawStatus.Open;
            isStop = true;
            log.e("-------------2");
        }
    }
    setPosition();
    if (isStop) {
        openLooper.stop();
    }
}

public float dxSpeed;

public void dampenSpeed(long deltaMillis) {

    if (this.dxSpeed != 0.0f) {
        this.dxSpeed *= (1.0f - 0.002f * deltaMillis);
        if (Math.abs(this.dxSpeed) < 50f)
            this.dxSpeed = 0.0f;
    }
}

public void hommingView(float ellapsedMillis) {
    float distance = (float) ellapsedMillis * transleteSpeed;
    boolean isStop = false;
    if (drawStatus.state == drawStatus.Closed && this.currentTranslateX < thisView.maxTranslateX / 5) {
        this.currentTranslateX -= distance;
        if (this.currentTranslateX <= 0) {
            this.currentTranslateX = 0;
            drawStatus.state = drawStatus.Closed;
            isStop = true;
        }
    } else if (drawStatus.state == drawStatus.Closed && this.currentTranslateX >= thisView.maxTranslateX / 5) {
        this.currentTranslateX += distance;
        if (this.currentTranslateX >= thisView.maxTranslateX) {
            this.currentTranslateX = thisView.maxTranslateX;
            drawStatus.state = drawStatus.Open;
            isStop = true;
        }
    } else if (drawStatus.state == drawStatus.Open && this.currentTranslateX < thisView.maxTranslateX / 5 * 4) {
        this.currentTranslateX -= distance;
        if (this.currentTranslateX <= 0) {
            this.currentTranslateX = 0;
            drawStatus.state = drawStatus.Closed;
            isStop = true;
        }
    } else if (drawStatus.state == drawStatus.Open && this.currentTranslateX >= thisView.maxTranslateX / 5 * 4) {
        this.currentTranslateX += distance;
        if (this.currentTranslateX >= thisView.maxTranslateX) {
            this.currentTranslateX = thisView.maxTranslateX;
            drawStatus.state = drawStatus.Open;
            isStop = true;
        }
    } else if (drawStatus.state == drawStatus.GoClosing) {
        this.currentTranslateX -= distance;
        if (this.currentTranslateX <= 0) {
            this.currentTranslateX = 0;
            drawStatus.state = drawStatus.Closed;
            isStop = true;
        }
    }
    setPosition();
    if (isStop) {
        openLooper.stop();
        log.e("looper stop...");
    }
}

}</pre>

MainView的代碼:

package com.example.wz.view;

import android.graphics.Color; import android.util.DisplayMetrics; import android.view.ViewGroup.LayoutParams; import android.widget.RelativeLayout; import android.widget.TextView;

import com.example.wz.MainActivity; import com.example.wz.R; import com.example.wz.controller.MainController; import com.example.wz.util.MyLog;

public class MainView {

public MyLog log = new MyLog(this, true);

public MainActivity mainActivity;
public MainController thisController;
public MainView thisView;

public MainView(MainActivity mainActivity) {
    this.mainActivity = mainActivity;
    this.thisView = this;
}

public DisplayMetrics displayMetrics;

public float screenWidth;
public float screenHeight;
public float density;

public float maxTranslateX;

public RelativeLayout maxView;
public RelativeLayout v1;
public RelativeLayout v2;

public void initViews() {
    this.displayMetrics = new DisplayMetrics();
    this.mainActivity.getWindowManager().getDefaultDisplay().getMetrics(this.displayMetrics);
    this.screenHeight = this.displayMetrics.heightPixels;
    this.screenWidth = this.displayMetrics.widthPixels;
    this.density = this.displayMetrics.density;
    this.maxTranslateX = this.screenWidth * 0.8f;
    this.mainActivity.setContentView(R.layout.activity_main);
    this.maxView = (RelativeLayout) this.mainActivity.findViewById(R.id.maxView);
    v1 = new RelativeLayout(mainActivity);
    v1.setBackgroundColor(Color.RED);
    RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) this.maxTranslateX, LayoutParams.MATCH_PARENT);
    this.maxView.addView(v1, params1);
    TextView t1 = new TextView(mainActivity);
    t1.setText("left menu bar");
    t1.setTextColor(Color.WHITE);
    v1.addView(t1);
    v1.setTranslationX(0 - this.maxTranslateX);
    v2 = new RelativeLayout(mainActivity);
    v2.setBackgroundColor(Color.parseColor("#0099cd"));
    RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams((int) this.screenWidth, LayoutParams.MATCH_PARENT);
    this.maxView.addView(v2, params2);
    v2.setTranslationX(0);
    TextView t2 = new TextView(mainActivity);
    t2.setText("body content");
    t2.setTextColor(Color.WHITE);
    v2.addView(t2);
}

}</pre>

日志管理類MyLog:

package com.example.wz.util;

import android.util.Log;

public class MyLog {

public static boolean isGlobalTurnOn = true;

public boolean isTurnOn = true;
public String tag = null;

public MyLog(String tag, boolean isTurnOn) {
    this.tag = tag;
    this.isTurnOn = isTurnOn;
}

public MyLog(Object clazz, boolean isTurnOn) {
    this.tag = clazz.getClass().getSimpleName();
    this.isTurnOn = isTurnOn;
}

public void v(String message) {
    this.v(this.tag, message);
}

public void d(String message) {
    this.d(this.tag, message);
}

public void i(String message) {
    this.i(this.tag, message);
}

public void w(String message) {
    this.w(this.tag, message);
}

public void e(String message) {
    this.e(this.tag, message);
}

public void v(String tag, String message) {
    if (isTurnOn && isGlobalTurnOn) {
        Log.v(tag, message);
    }
}

public void d(String tag, String message) {
    if (isTurnOn && isGlobalTurnOn) {
        Log.d(tag, message);
    }
}

public void i(String tag, String message) {
    if (isTurnOn && isGlobalTurnOn) {
        Log.i(tag, message);
    }
}

public void w(String tag, String message) {
    if (isTurnOn && isGlobalTurnOn) {
        Log.w(tag, message);
    }
}

public void e(String tag, String message) {
    if (isTurnOn && isGlobalTurnOn) {
        Log.e(tag, message);
    }
}

}</pre>

實現動畫效果的核心類OpenLooper:

package com.example.wz.util;

import android.annotation.TargetApi; import android.os.Build; import android.os.Handler; import android.os.SystemClock; import android.view.Choreographer;

public class OpenLooper {

public LegacyAndroidSpringLooper legacyAndroidSpringLooper = null;
public ChoreographerAndroidSpringLooper choreographerAndroidSpringLooper = null;
public LoopCallback loopCallback = null;

public void createOpenLooper() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        choreographerAndroidSpringLooper = new ChoreographerAndroidSpringLooper();
    } else {
        legacyAndroidSpringLooper = new LegacyAndroidSpringLooper();
    }
}

public void start() {
    if (choreographerAndroidSpringLooper != null) {
        choreographerAndroidSpringLooper.start();
    } else if (legacyAndroidSpringLooper != null) {
        legacyAndroidSpringLooper.start();
    }
}

public void stop() {
    if (choreographerAndroidSpringLooper != null) {
        choreographerAndroidSpringLooper.stop();
    } else if (legacyAndroidSpringLooper != null) {
        legacyAndroidSpringLooper.stop();
    }
}

public class LoopCallback {

    public void loop(double ellapsedMillis) {

    }
}

public void loop(double ellapsedMillis) {
    if (this.loopCallback != null) {
        this.loopCallback.loop(ellapsedMillis);
    }
}

public class LegacyAndroidSpringLooper {

    public Handler mHandler;
    public Runnable mLooperRunnable;
    public boolean mStarted;
    public long mLastTime;

    public LegacyAndroidSpringLooper() {
        initialize(new Handler());
    }

    public void initialize(Handler handler) {
        mHandler = handler;
        mLooperRunnable = new Runnable() {
            @Override
            public void run() {
                if (!mStarted) {
                    return;
                }
                long currentTime = SystemClock.uptimeMillis();
                loop(currentTime - mLastTime);
                mHandler.post(mLooperRunnable);
            }
        };
    }

    public void start() {
        if (mStarted) {
            return;
        }
        mStarted = true;
        mLastTime = SystemClock.uptimeMillis();
        mHandler.removeCallbacks(mLooperRunnable);
        mHandler.post(mLooperRunnable);
    }

    public void stop() {
        mStarted = false;
        mHandler.removeCallbacks(mLooperRunnable);
    }
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class ChoreographerAndroidSpringLooper {

    public Choreographer mChoreographer;
    public Choreographer.FrameCallback mFrameCallback;
    public boolean mStarted;
    public long mLastTime;

    public ChoreographerAndroidSpringLooper() {
        initialize(Choreographer.getInstance());
    }

    public void initialize(Choreographer choreographer) {
        mChoreographer = choreographer;
        mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                if (!mStarted) {
                    return;
                }
                long currentTime = SystemClock.uptimeMillis();
                loop(currentTime - mLastTime);
                mLastTime = currentTime;
                mChoreographer.postFrameCallback(mFrameCallback);
            }
        };
    }

    public void start() {
        if (mStarted) {
            return;
        }
        mStarted = true;
        mLastTime = SystemClock.uptimeMillis();
        mChoreographer.removeFrameCallback(mFrameCallback);
        mChoreographer.postFrameCallback(mFrameCallback);
    }

    public void stop() {
        mStarted = false;
        mChoreographer.removeFrameCallback(mFrameCallback);
    }
}

}</pre>

如有轉載請著名來自http://blog.csdn.net/qxs965266509
源代碼下載鏈接:http://download.csdn.net/detail/qxs965266509/8621503

</div>

 本文由用戶 ReaEtienne 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!