年前就想写左右滑动菜单,苦于没有时间,一直拖到现在,这篇代码实现参考了网上流行的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. Memory Barriers

    这回该进入主题了.         上一文最后提到了 Memory Barriers ,即内存屏障.由于对一个 CPU 而言,a = 1; b = 1. 由于在中间加了内存屏障,在 X86 架构下,就 ...

  2. 网页视频播放方案chimee 组件使用

    1.概述 鉴于目前 H5-Video 在 Web 生态环境中存在较大差异化(兼容性)的表现,本文主要介绍使用奇舞团开发chimee 组件进行视频播放的用法. chimee 组件地址: http://c ...

  3. linux/unix核心设计思想

    1) 程序应该小而专一,程序应该尽量的小,且仅仅专注于一件事上.不要开发那些看起来实用可是90%的情况都用不到的特性: 2) 程序不仅仅要考虑性能, 程序的可移植性更重要,shell和perl.pyt ...

  4. 【DB2】报错:-30090 25000 指定的操作对远程执行失败

    场景描述: 数据库:DB_1,DB_2 现在在DB_1中建立NICKNAME为CST_INFO_NICK,并且该别名指向数据库DB_2的CST_INFO表,在DB_1中建立存储过程,该存储过程需要  ...

  5. jQuery 文档操作 - prependTo() ,appendTo()方法

    其他jquery文档操作方法:http://www.w3school.com.cn/jquery/jquery_ref_manipulation.asp jQuery 参考手册 - 文档操作 appe ...

  6. NodeJS 安装不存在的模块

    设置npm镜像: npm config set registry https://registry.npm.taobao.org 我遇到的报错情况: Error: Cannot find module ...

  7. EPH接收Event Hub Message

    简介: 使用Python SDK,基于EPH方式接收Azure Event Hub中存储的message,EventProcessorHost()中使用Azure Storage存储offerset等 ...

  8. 20181011_matplotlib

    import numpy as np import pandas as pd import matplotlib.pyplot as plt df=pd.DataFrame(np.random.ran ...

  9. /u200B 8203 Zero-width space 问题

    [TestMethod] public void TestBom() { string str = "123​";//这个字符串是错误的有问题 长度4 ).Select(x =&g ...

  10. 通过windows自带的系统监视器来查看IIS并发连接数(perfmon.msc)

    如果要查看IIS连接数,最简单方便的方法是通过“网站统计”来查看,“网站统计”的当前在线人数可以认为是当前IIS连接数.然而,“网站统计”的当前在线人数统计时间较长,一般为10分钟或15分钟,再加上统 ...