Android实现自定义滑动式抽屉效果菜单

在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

源码下载:抽屉效果

以上就是本文的全部内容,希望对大家的学习有所帮助。

时间: 2024-09-19 12:45:35

Android实现自定义滑动式抽屉效果菜单的相关文章

Android实现自定义滑动式抽屉效果菜单_Android

在Andoird使用Android自带的那些组件,像SlidingDrawer和DrawerLayout都是抽屉效果的菜单,但是在项目很多要实现的功能都收到Android这些自带组件的限制,导致很难完成项目的需求,自定义的组件,各方面都在自己的控制之下,从而根据需求做出调整.想要实现好的效果,基本上都的基于Android的OnTouch事件自己实现响应的功能. 首先,给大家先看一下整体的效果: 滑动的加速度效果都是有的,具体的体验,只能安装后才能查看. 接下来,看代码: 代码从MainActiv

《Android 平板电脑开发实战详解和典型案例》——滑动式抽屉—— SlidingDrawer

2.7 滑动式抽屉-- SlidingDrawerAndroid 平板电脑开发实战详解和典型案例上一节中,介绍了可展开列表ExpandableListView的基本知识及简单案例,本节将介绍Android 2.0控件滑动式抽屉SlidingDrawer,主要内容包括其基本知识以及一个简单的案例. 2.7.1 滑动式抽屉基本知识滑动式抽屉SlidingDrawer在UI布局有限或放不下太多控件的时候可以使用,滑动式抽屉SlidingDrawer可以隐藏屏外的内容,在布局文件中必须指定handle和

Android控件之SlidingDrawer(滑动式抽屉)详解与实例分享_Android

SlidingDrawer效果想必大家也见到过,它就是1.5模拟器上进入应用程序列表的效果.下面是截图 一.简介  SlidingDrawer隐藏屏外的内容,并允许用户通过handle以显示隐藏内容.它可以垂直或水平滑动,它有俩个View组成,其一 是可以拖动的handle,其二是隐藏内容的View.它里面的控件必须设置布局,在布局文件中必须指定handle和content.例如下面 复制代码 代码如下: <SlidingDrawer android:layout_width="fill_

Android控件之SlidingDrawer(滑动式抽屉)详解与实例分享

SlidingDrawer效果想必大家也见到过,它就是1.5模拟器上进入应用程序列表的效果.下面是截图 一.简介  SlidingDrawer隐藏屏外的内容,并允许用户通过handle以显示隐藏内容.它可以垂直或水平滑动,它有俩个View组成,其一 是可以拖动的handle,其二是隐藏内容的View.它里面的控件必须设置布局,在布局文件中必须指定handle和content.例如下面复制代码 代码如下:<SlidingDrawer android:layout_width="fill_pa

Android 自定义View实现抽屉效果

Android 自定义View实现抽屉效果 说明 这个自定义View,没有处理好多点触摸问题 View跟着手指移动,没有采用传统的scrollBy方法,而是通过不停地重新布局子View的方式,来使得子View产生滚动效果menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight); 相应的,由于没有使用scrollBy方法,就没有产生getScrollX值,所以不能通过Scroller的startScroll方法来完成手指离开后的平

Android中自定义view实现侧滑效果_Android

效果图: 看网上的都是两个view拼接,默认右侧的不显示,水平移动的时候把右侧的view显示出来.但是看最新版QQ上的效果不是这样的,但给人的感觉却很好,所以献丑来一发比较高仿的. 知识点: 1.ViewDragHelper 的用法: 2.滑动冲突的解决: 3.自定义viewgroup. ViewDragHelper 出来已经比较久了 相信大家都比较熟悉,不熟悉的话google一大把这里主要简单用一下它的几个方法 1.tryCaptureView(View child, int pointerI

Android编程之ICS式下拉菜单PopupWindow实现方法详解(附源码下载)_Android

本文实例讲述了Android编程之ICS式下拉菜单PopupWindow实现方法.分享给大家供大家参考,具体如下: 运行效果截图如下: 右边这个就是下拉菜单啦,看见有的地方叫他 ICS式下拉菜单,哎哟,不错哦! 下面先讲一下实现原理: 这种菜单实际上就是一个弹出式的菜单,于是我们想到android PopupWindow 类,给他设置一个view 在弹出来不就OK了吗. PopupWindow 的用法也很简单 主要方法: 步骤1.new 一个实例出来,我们使用这个构造方法即可, 复制代码 代码如

Android中Activity滑动关闭的效果

最近感觉有一个Activity关闭的效果挺不错的,就是手势滑动就可以关闭当前Activity,于是就想写一篇博客和大家一起分享下!废话不多说,老规矩,还先上效果图,更直观! 项目地址:https://github.com/xinyitiandi/SlidingFinishDemo 上代码: 1.第一个Activity: package com.ekeguan.slidingfinishdemo; import android.content.Intent; import android.os.B

Android编程之ICS式下拉菜单PopupWindow实现方法详解(附源码下载)

本文实例讲述了Android编程之ICS式下拉菜单PopupWindow实现方法.分享给大家供大家参考,具体如下: 运行效果截图如下: 右边这个就是下拉菜单啦,看见有的地方叫他 ICS式下拉菜单,哎哟,不错哦! 下面先讲一下实现原理: 这种菜单实际上就是一个弹出式的菜单,于是我们想到android PopupWindow 类,给他设置一个view 在弹出来不就OK了吗. PopupWindow 的用法也很简单 主要方法: 步骤1.new 一个实例出来,我们使用这个构造方法即可, 复制代码 代码如