Android-自定义滑动菜单(抽屉效果)

ReaEtienne 9年前

来自: 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);      }  }

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...");          }      }    }

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);      }  }

日志管理类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);          }      }    }

实现动画效果的核心类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);          }      }  }

如有转载请著名来自http://blog.csdn.net/qxs965266509
源代码下载链接:http://download.csdn.net/detail/qxs965266509/8621503