Android UI开发第二十七篇——实现左右划出菜单
年前就想写左右滑动菜单,苦于没有时间,一直拖到现在,这篇代码实现参考了网上流行的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 张兴业