Android 사용자 정의 슬라이딩 서랍 메뉴 효과 구현

Android 에서 Android 자체 가 가지 고 있 는 구성 요 소 를 사용 합 니 다.예 를 들 어 Sliding Drawer 와 DrawerLayout 는 모두 서랍 효과 의 메뉴 이지 만 프로젝트 에서 실현 해 야 할 많은 기능 이 Android 와 같은 자체 구성 요소 의 제한 을 받 아 프로젝트 의 수 요 를 완성 하기 어렵 고 사용자 정의 구성 요 소 는 각 방면 에서 자신의 통제 하에 수요 에 따라 조정 합 니 다.좋 은 효 과 를 얻 으 려 면 기본적으로 안 드 로 이 드 기반 의 OnTouch 이벤트 가 스스로 응답 하 는 기능 을 수행 합 니 다.
우선 전체적인 효 과 를 보 여 드 리 겠 습 니 다.

미끄럼 의 가속도 효 과 는 모두 있 으 며,구체 적 인 체험 은 설치 후 만 볼 수 있다.
다음,코드 보기:
코드 는 MainActivity 에서 두 가지 유형 으로 확대 되 었 습 니 다.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);
 }
 }
}
원본 다운로드:서랍 효과
미끄럼 기능 에 관 한 글 은 주 제 를 클릭 하 십시오《안 드 로 이 드 슬라이딩 기능》
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기