年前就想写左右滑动菜单,苦于没有时间,一直拖到现在,这篇代码实现参考了网上流行的SlidingMenu,使用的FrameLayout布局,不是扩展的HorizontalScrollView。

程序中自定义了菜单view:SlidingView,继承自ViewGroup,使用FrameLayout布局。重写了onInterceptTouchEvent(MotionEvent ev)方法实现ontouch的分发拦截,重写了onTouchEvent(MotionEvent ev)方法,实现左右滑动。

public class SlidingView extends ViewGroup {

	private FrameLayout mContainer;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mTouchSlop;
private float mLastMotionX;
private float mLastMotionY;
private static final int SNAP_VELOCITY = 1000;
private View mLeftView;
private View mRightView; public SlidingView(Context context) {
super(context);
init();
} public SlidingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public SlidingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mContainer.measure(widthMeasureSpec, heightMeasureSpec);
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = r - l;
final int height = b - t;
mContainer.layout(0, 0, width, height);
} private void init() {
mContainer = new FrameLayout(getContext());
mContainer.setBackgroundColor(0xff000000);
mScroller = new Scroller(getContext());
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
super.addView(mContainer);
} public void setView(View v) {
if (mContainer.getChildCount() > 0) {
mContainer.removeAllViews();
}
mContainer.addView(v);
} @Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
postInvalidate();
} @Override
public void computeScroll() {
if (!mScroller.isFinished()) {
if (mScroller.computeScrollOffset()) {
int oldX = getScrollX();
int oldY = getScrollY();
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
}
// Keep on drawing until the animation has finished.
invalidate();
} else {
clearChildrenCache();
}
} else {
clearChildrenCache();
}
} private boolean mIsBeingDragged; /**
* 实现了ontouch的分发拦截
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY(); switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mLastMotionY = y;
mIsBeingDragged = false;
break; case MotionEvent.ACTION_MOVE:
final float dx = x - mLastMotionX;
final float xDiff = Math.abs(dx);
final float yDiff = Math.abs(y - mLastMotionY);
if (xDiff > mTouchSlop && xDiff > yDiff) {
mIsBeingDragged = true;
mLastMotionX = x;
}
Log.d("Sliding", "SlidingView_Touch:"+x+"|"+y);
Log.d("Sliding", "SlidingView_Touch:"+xDiff+"|"+mTouchSlop+"|"+yDiff+"|"+mLastMotionY);
Log.d("Sliding", "SlidingView_Touch:"+mIsBeingDragged);
break; }
return mIsBeingDragged;
} @Override
public boolean onTouchEvent(MotionEvent ev) { if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev); final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY(); switch (action) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mLastMotionX = x;
mLastMotionY = y;
if (getScrollX() == -getLeftMenuWidth()
&& mLastMotionX < getLeftMenuWidth()) {
return false;
} if (getScrollX() == getRightMenuWidth()
&& mLastMotionX > getLeftMenuWidth()) {
return false;
} break;
case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
enableChildrenCache();
final float deltaX = mLastMotionX - x;
mLastMotionX = x;
float oldScrollX = getScrollX();
float scrollX = oldScrollX + deltaX; if (deltaX < 0 && oldScrollX < 0) { // left view
final float leftBound = 0;
final float rightBound = -getLeftMenuWidth();
if (scrollX > leftBound) {
scrollX = leftBound;
} else if (scrollX < rightBound) {
scrollX = rightBound;
}
} else if (deltaX > 0 && oldScrollX > 0) { // right view
final float rightBound = getRightMenuWidth();
final float leftBound = 0;
if (scrollX < leftBound) {
scrollX = leftBound;
} else if (scrollX > rightBound) {
scrollX = rightBound;
}
} scrollTo((int) scrollX, getScrollY());
if (scrollX > 0) {
mLeftView.setVisibility(View.GONE);
mLeftView.clearFocus();
mRightView.setVisibility(View.VISIBLE);
mRightView.requestFocus();
} else {
mLeftView.setVisibility(View.VISIBLE);
mLeftView.requestFocus();
mRightView.setVisibility(View.GONE);
mRightView.clearFocus();
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) velocityTracker.getXVelocity();
velocityX = 0;
int oldScrollX = getScrollX();
int dx = 0;
if (oldScrollX < 0) {
// 左边
if (oldScrollX < -getLeftMenuWidth() / 2
|| velocityX > SNAP_VELOCITY) {
// 左侧页面划出
dx = -getLeftMenuWidth() - oldScrollX; } else if (oldScrollX >= -getLeftMenuWidth() / 2
|| velocityX < -SNAP_VELOCITY) {
// 左侧页面关闭
dx = -oldScrollX;
}
} else {
// 右边
if (oldScrollX > getRightMenuWidth() / 2
|| velocityX < -SNAP_VELOCITY) {
// 右侧页面划出
dx = getRightMenuWidth() - oldScrollX; } else if (oldScrollX <= getRightMenuWidth() / 2
|| velocityX > SNAP_VELOCITY) {
// 右侧页面关闭
dx = -oldScrollX;
}
} smoothScrollTo(dx);
clearChildrenCache(); } break; }
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
return true;
} private int getLeftMenuWidth() {
if (mLeftView == null) {
return 0;
}
return mLeftView.getWidth();
} private int getRightMenuWidth() {
if (mRightView == null) {
return 0;
}
return mRightView.getWidth();
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
} public View getRightView() {
return mRightView;
} public void setRightView(View mRightView) {
this.mRightView = mRightView;
} public View getMenuView() {
return mLeftView;
} public void setLeftView(View mLeftView) {
this.mLeftView = mLeftView;
} void toggle() {
int menuWidth = mLeftView.getWidth();
int oldScrollX = getScrollX();
if (oldScrollX == 0) {
smoothScrollTo(-menuWidth);
} else if (oldScrollX == -menuWidth) {
smoothScrollTo(menuWidth);
}
} /**
* 打开(关闭)左侧页面
*/
public void showLeftView() {
mLeftView.setVisibility(View.VISIBLE);
mRightView.setVisibility(View.GONE);
int menuWidth = mLeftView.getWidth();
int oldScrollX = getScrollX();
if (oldScrollX == 0) {
smoothScrollTo(-menuWidth);
} else if (oldScrollX == -menuWidth) {
smoothScrollTo(menuWidth);
}
} /**
* 打开(关闭)右侧页面
*/
public void showRightView() {
mLeftView.setVisibility(View.GONE);
mLeftView.clearFocus();
mRightView.setVisibility(View.VISIBLE);
mRightView.requestFocus();
int menuWidth = mRightView.getWidth();
int oldScrollX = getScrollX();
if (oldScrollX == 0) {
smoothScrollTo(menuWidth);
} else if (oldScrollX == menuWidth) {
smoothScrollTo(-menuWidth);
}
} /**
* 显示中间页面
*/
public void showCenterView() {
int menuWidth = mRightView.getWidth();
int oldScrollX = getScrollX();
if (oldScrollX == menuWidth) {
showRightView();
} else if (oldScrollX == -menuWidth) {
showLeftView();
}
} void smoothScrollTo(int dx) {
int duration = 500;
int oldScrollX = getScrollX();
mScroller.startScroll(oldScrollX, getScrollY(), dx, getScrollY(),
duration);
invalidate();
} void enableChildrenCache() {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View layout = (View) getChildAt(i);
layout.setDrawingCacheEnabled(true);
}
} void clearChildrenCache() {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View layout = (View) getChildAt(i);
layout.setDrawingCacheEnabled(false);
}
} }

SlidingMenu对SlidingView做了进一步封装处理:

public class SlidingMenu extends RelativeLayout {

	private SlidingView mSlidingView;
private View mLeftView;
private View mRightView;
// menu width
private int alignScreenWidth; public SlidingMenu(Context context) {
super(context);
} public SlidingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
} public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} public void setAlignScreenWidth(int alignScreenWidth) {
this.alignScreenWidth = alignScreenWidth;
} public void setLeftView(View view) {
LayoutParams behindParams = new LayoutParams(alignScreenWidth,
LayoutParams.MATCH_PARENT);
addView(view, behindParams);
mLeftView = view;
} public void setRightView(View view) {
LayoutParams behindParams = new LayoutParams(alignScreenWidth,
LayoutParams.MATCH_PARENT);
behindParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
addView(view, behindParams);
mRightView = view;
} public void setCenterView(View view) {
LayoutParams aboveParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
mSlidingView = new SlidingView(getContext());
addView(mSlidingView, aboveParams);
mSlidingView.setView(view);
mSlidingView.invalidate();
mSlidingView.setLeftView(mLeftView);
mSlidingView.setRightView(mRightView);
} public void showLeftView() {
mSlidingView.showLeftView();
} public void showRightView() {
mSlidingView.showRightView();
} public void showCenterView() {
mSlidingView.showCenterView();
} }

SlidingMenu的使用代码

public class SlidingActivity extends Activity implements OnClickListener{
SlidingMenu mSlidingMenu; @Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.main); DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm); mSlidingMenu = (SlidingMenu) findViewById(R.id.slidingMenu);
mSlidingMenu.setAlignScreenWidth((dm.widthPixels / 5) * 2); View leftView=getLayoutInflater().inflate(R.layout.left_menu, null);
View rightView=getLayoutInflater().inflate(R.layout.right_menu, null);
View centerView=getLayoutInflater().inflate(R.layout.center, null); mSlidingMenu.setLeftView(leftView);
mSlidingMenu.setRightView(rightView);
mSlidingMenu.setCenterView(centerView); Button showLeftMenu=(Button)centerView.findViewById(R.id.center_left_btn);
showLeftMenu.setOnClickListener(this);
Button showRightMenu=(Button)centerView.findViewById(R.id.center_right_btn);
showRightMenu.setOnClickListener(this);
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.center_left_btn:
mSlidingMenu.showLeftView();
break;
case R.id.center_right_btn:
mSlidingMenu.showRightView();
break;
default:
break;
}
} }

                                

代码:http://download.csdn.net/detail/xyz_lmn/5109965

/**
* @author 张兴业
*  iOS入门群:83702688
*  android开发进阶群:241395671
*  我的新浪微博:@张兴业TBOW
*  我的邮箱:xy-zhang#163.com(#->@)
*/

Android UI开发第二十七篇——实现左右划出菜单的更多相关文章

  1. Android UI开发第二十八篇——Fragment中使用左右滑动菜单

    Fragment实现了Android UI的分片管理,尤其在平板开发中,好处多多.这一篇将借助Android UI开发第二十六篇——Fragment间的通信. Android UI开发第二十七篇——实 ...

  2. Android UI开发第二十四篇——Action Bar

    Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...

  3. 【转】Android UI开发第二十四篇——Action Bar

    Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...

  4. Android UI开发第二十六篇——Fragment间的通信

    为了重用Fragment的UI组件,创建的每个Fragment都应该是自包含的.有它自己的布局和行为的模块化组件.一旦你定义了这些可重用的Fragment,你就可以把它们跟一个Activity关联,并 ...

  5. Android UI开发第二十九篇——Android中五种常用的menu(菜单)

    Android Menu在手机的应用中起着导航的作用,作者总结了5种常用的Menu. 1.左右推出的Menu 前段时间比较流行,我最早是在海豚浏览器中看到的,当时耳目一新.最早使用左右推出菜单的,听说 ...

  6. Android UI开发第三十篇——使用Fragment构建灵活的桌面

    http://www.lupaworld.com/article-222973-1.html 当我们设计应用程序时,希望能够尽最大限度的适配各种设备,包括4寸屏.7寸屏. 10寸屏等等,Android ...

  7. Android UI开发第三十五篇——AppCompat实现Action Bar

    每一位Android开发者对Action Bar这种设计都不陌生了,毕竟它已经发布了至少两年了.Android团队发布Action Bar设计规范时同时放出了ActionBar的Api来支持这种设计. ...

  8. Android UI开发第四十一篇——墨迹天气3.0引导界面及动画实现

    周末升级了墨迹天气,看着引导界面做的不错,模仿一下,可能与原作者的代码实现不一样,但是实现的效果还是差不多的.先分享一篇以前的文章,android动画的基础知识,<Android UI开发第十二 ...

  9. Android UI开发第三十九篇——Tab界面实现汇总及比较

    Tab布局是iOS的经典布局,Android应用中也有大量应用,前面也写过Android中TAb的实现,<Android UI开发第十八篇——ActivityGroup实现tab功能>.这 ...

随机推荐

  1. oneapm的技术博客(简书),用来追溯群里的讨论,mark

    http://www.jianshu.com/users/572133740c3f/latest_articles

  2. Unity多媒体展示项目经验分享-ImageEffect+动态绑定

    Unity多媒体展示项目经验分享-ImageEffect+动态绑定+网络通信 <ignore_js_op> “海尔科技展墙”是去年年初我们为上海家电博览会制作的一个多媒体展项,有限的工期以 ...

  3. mui 根据 json 数据动态创建列表

    使用 underscore.js 模块解析 Underscore提供了一个轻量级的模板解析函数,它可以帮助我们有效地组织页面结构和逻辑. 实例: <!DOCTYPE html> <h ...

  4. 用JDOM读取XML文件

    用JDOM读取XML文件需先用org.jdom.input.SAXBuilder对象的build()方法创建Document对象,然后用Document类.Element类等的方法读取所需的内容.IB ...

  5. Nginx主动连接与被动连接的差别

    1.主动连接是指Nginx主动发起的同上游server的连接:被动连接是指Nginx接收到的来自client主动发起的连接; 2.主动连接用ngx_peer_connection_t结构体表示:被动连 ...

  6. <转>巧用notepad++ 批量转换ansi 和 utf8

    原方出处:http://stackoverflow.com/questions/7256049/notepad-converting-ansi-encoded-file-to-utf-8 Here s ...

  7. Android内存优化大全(中)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...

  8. 调用getChildFragmentManager时出现的Bug

    异常: java.lang.IllegalStateException: Activity has been destroyed at android.support.v4.app.FragmentM ...

  9. STL vector的构造函数和析构函数(2)

    原文来自:点击打开链接 译文: public member function vector的构造器:这里我仅仅翻译C++11的,C++98的就不翻译了. 构造器原型: <vector> s ...

  10. 固态继电器SSR

    s107.  LH1521.  CPC1035N http://e22a.com/h.bXsDYw?cv=AAOzhSfJ&sm=53e30b