Android 左边滑动菜单栏

Posted XRFirst

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 左边滑动菜单栏相关的知识,希望对你有一定的参考价值。

1、SlidingMenuView类


package com.zhuixingba.view;

import com.zhuixingba.main.R;

import android.content.Context;

import android.graphics.Canvas;

import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;

import android.widget.Scroller;

/**
 * EffectSpace
 * 
 * @author lance
 * 
 */
public class SlidingMenuView extends ViewGroup 

	private static String LOG_TAG = "SlidingMenuView";
	private static final int INVALID_SCREEN = -1;

	private static final int SNAP_VELOCITY = 1000;

	private int mDefaultScreen = 1;

	private int mCurrentScreen;
	private int mNextScreen = INVALID_SCREEN;
	private Scroller mScroller;
	private VelocityTracker mVelocityTracker;

	private float mLastMotionX;
	private float mLastMotionY;

	private final static int TOUCH_STATE_REST = 0;
	private final static int TOUCH_STATE_SCROLLING = 1;

	public int mTouchState = TOUCH_STATE_REST;

	private boolean mAllowLongPress;
	private boolean mLocked;

	private int mTouchSlop;

	public int totalWidth = 0;

	private CloseAnimation closeAnimation;

	public SlidingMenuView(Context context, AttributeSet attrs) 
		this(context, attrs, 0);
	

	public SlidingMenuView(Context context, AttributeSet attrs, int defStyle) 
		super(context, attrs, defStyle);
		initWorkspace();
		postDelayed(new Runnable() 
			@Override
			public void run() 
				scrollTo(findViewById(R.id.main_left_menu).getWidth(), 0);
			
		, 50);
	

	private void initWorkspace() 
		mScroller = new Scroller(getContext());
		mCurrentScreen = mDefaultScreen;

		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
	

	boolean isDefaultScreenShowing() 
		return mCurrentScreen == mDefaultScreen;
	

	public int getCurrentScreen() 
		return mCurrentScreen;
	

	public void setCurrentScreen(int currentScreen) 
		mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));

		invalidate();
	

	void showDefaultScreen() 
		setCurrentScreen(mDefaultScreen);
	

	@Override
	public void computeScroll() 
		if (mScroller.computeScrollOffset()) 
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
		 else if (mNextScreen != INVALID_SCREEN) 
			mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
			mNextScreen = INVALID_SCREEN;
			clearChildrenCache();
		

		if (closeAnimation != null) 
			closeAnimation.closeMenuAnimation();
		
	

	@Override
	public void scrollTo(int x, int y) 
		super.scrollTo(x, y);
		postInvalidate();
	

	@Override
	protected void dispatchDraw(Canvas canvas) 
		final int scrollX = getScrollX();
		super.dispatchDraw(canvas);
		canvas.translate(scrollX, 0);
	

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		measureViews(widthMeasureSpec, heightMeasureSpec);

	

	public void measureViews(int widthMeasureSpec, int heightMeasureSpec) 

		View v1 = findViewById(R.id.main_left_menu);

		v1.measure(v1.getLayoutParams().width + v1.getLeft() + v1.getRight(), heightMeasureSpec);

		View v2 = findViewById(R.id.main_body);
		v2.measure(widthMeasureSpec, heightMeasureSpec);

	

	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) 
		int childLeft = 0;

		final int count = getChildCount();
		for (int i = 0; i < count; i++) 
			final View child = getChildAt(i);
			if (child.getVisibility() != View.GONE) 
				final int childWidth = child.getMeasuredWidth();
				child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
				childLeft += childWidth;
			
		
		totalWidth = childLeft;
	

	@Override
	public boolean dispatchUnhandledMove(View focused, int direction) 
		if (direction == View.FOCUS_LEFT) 
			if (getCurrentScreen() > 0) 
				snapToScreen(getCurrentScreen() - 1);
				return true;
			
		 else if (direction == View.FOCUS_RIGHT) 
			if (getCurrentScreen() < getChildCount() - 1) 
				snapToScreen(getCurrentScreen() + 1);
				return true;
			
		
		return super.dispatchUnhandledMove(focused, direction);
	

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) 

		if (mLocked) 
			return true;
		

		final int action = ev.getAction();
		if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) 
			return true;
		

		final float x = ev.getX();
		final float y = ev.getY();

		switch (action) 
		case MotionEvent.ACTION_MOVE:

			final int xDiff = (int) Math.abs(x - mLastMotionX);
			final int yDiff = (int) Math.abs(y - mLastMotionY);

			final int touchSlop = mTouchSlop;
			boolean xMoved = xDiff > touchSlop;
			boolean yMoved = yDiff > touchSlop;

			if (xMoved || yMoved) 

				if (xMoved) 
					// Scroll if the user moved far enough along the X axis
					mTouchState = TOUCH_STATE_SCROLLING;
					enableChildrenCache();
				
				// Either way, cancel any pending longpress
				if (mAllowLongPress) 
					mAllowLongPress = false;
					// Try canceling the long press. It could also have been
					// scheduled
					// by a distant descendant, so use the mAllowLongPress flag
					// to block
					// everything
					final View currentScreen = getChildAt(mCurrentScreen);
					currentScreen.cancelLongPress();
				
			
			break;

		case MotionEvent.ACTION_DOWN:
			// Remember location of down touch
			mLastMotionX = x;
			mLastMotionY = y;
			mAllowLongPress = true;

			mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;

			break;

		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			// Release the drag
			clearChildrenCache();
			mTouchState = TOUCH_STATE_REST;
			mAllowLongPress = false;
			break;
		

		/*
		 * The only time we want to intercept motion events is if we are in the
		 * drag mode.
		 */
		return mTouchState != TOUCH_STATE_REST;
	

	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);
		
	

	@Override
	public boolean onTouchEvent(MotionEvent ev) 
		if (mLocked) 
			return true;
		

		if (mVelocityTracker == null) 
			mVelocityTracker = VelocityTracker.obtain();
		
		mVelocityTracker.addMovement(ev);

		final int action = ev.getAction();
		final float x = ev.getX();

		switch (action) 
		case MotionEvent.ACTION_DOWN:
			/*
			 * If being flinged and user touches, stop the fling. isFinished
			 * will be false if being flinged.
			 */
			if (!mScroller.isFinished()) 
				mScroller.abortAnimation();
			

			// Remember where the motion event started
			mLastMotionX = x;
			break;
		case MotionEvent.ACTION_MOVE:
			if (mTouchState == TOUCH_STATE_SCROLLING) 
				// Scroll to follow the motion event
				final int deltaX = (int) (mLastMotionX - x);
				mLastMotionX = x;

				if (deltaX < 0) 
					if (getScrollX() > 0) 

						scrollBy(Math.max(-getScrollX(), deltaX), 0);
					
				 else if (deltaX > 0) 
					final int availableToScroll = getChildAt(getChildCount() - 1).getRight() - getScrollX()
							- getWidth();
					if (availableToScroll > 0) 

						scrollBy(Math.min(availableToScroll, deltaX), 0);
					
				
			
			break;
		case MotionEvent.ACTION_UP:
			if (mTouchState == TOUCH_STATE_SCROLLING) 
				final VelocityTracker velocityTracker = mVelocityTracker;
				velocityTracker.computeCurrentVelocity(1000);
				int velocityX = (int) velocityTracker.getXVelocity();

				if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) 
					// Fling hard enough to move left
					snapToScreen(mCurrentScreen - 1);
				 else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) 
					// Fling hard enough to move right
					snapToScreen(mCurrentScreen + 1);
				 else 
					snapToDestination();
				

				if (mVelocityTracker != null) 
					mVelocityTracker.recycle();
					mVelocityTracker = null;
				
			
			mTouchState = TOUCH_STATE_REST;
			break;
		case MotionEvent.ACTION_CANCEL:
			mTouchState = TOUCH_STATE_REST;
		

		return true;
	

	protected void snapToDestination() 

		int whichScreen = 0;
		int count = getChildCount();
		int start = 0;
		int end = 0;
		int viewWidth = 0;
		int tend = 0;
		int tstart = 0;
		final int scrollX = getScrollX();
		for (int i = 0; i < count; i++) 
			viewWidth = getChildAt(i).getWidth();
			tend = end + viewWidth / 2;
			if (i != 0) 
				viewWidth = getChildAt(i - 1).getWidth();
			
			tstart -= viewWidth;
			if (scrollX > tstart && scrollX < tend) 
				break;
			
			start += viewWidth;
			end += viewWidth;
			whichScreen++;
		
		snapToScreen(whichScreen);
	

	/**
	 * 正常点击的切屏动画
	 * 
	 * @param whichScreen
	 *            1代表侧边收起 ,0代表展开侧边
	 * 
	 */
	public void snapToScreen(int whichScreen) 

		enableChildrenCache();

		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		boolean changingScreens = whichScreen != mCurrentScreen;

		mNextScreen = whichScreen;

		View focusedChild = getFocusedChild();
		if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentScreen)) 
			focusedChild.clearFocus();
		

		int newX = 0;

		for (int i = 0; i < whichScreen; i++) 
			newX += getChildAt(i).getWidth();
		
		newX = Math.min(totalWidth - getWidth(), newX);
		final int delta = newX - getScrollX();
		// mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) *
		// 2);
		mScroller.startScroll(getScrollX(), 0, delta, 0, 500);
		invalidate();
	

	/**
	 * 
	 * 点击左侧菜单的切屏动画
	 * 
	 * @param whichScreen
	 */
	public void closeMenu_1(int whichScreen) 

		enableChildrenCache();

		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		boolean changingScreens = whichScreen != mCurrentScreen;

		mNextScreen = whichScreen;

		View focusedChild = getFocusedChild();
		if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentScreen)) 
			focusedChild.clearFocus();
		

		int newX = 0;

		for (int i = 0; i < whichScreen; i++) 
			newX += getChildAt(i).getWidth();
		
		newX = Math.min(totalWidth - getWidth(), newX);
		final int delta = newX - getScrollX();

		mScroller.startScroll(getScrollX(), 0, delta - getWidth(), 0, 260);

		invalidate();

	

	/**
	 * 
	 * 点击左侧菜单的切屏动画
	 * 
	 * @param whichScreen
	 */
	public void closeMenu_2(int whichScreen) 

		enableChildrenCache();

		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		boolean changingScreens = whichScreen != mCurrentScreen;

		mNextScreen = whichScreen;

		View focusedChild = getFocusedChild();
		if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentScreen)) 
			focusedChild.clearFocus();
		

		int newX = 0;

		for (int i = 0; i < whichScreen; i++) 
			newX += getChildAt(i).getWidth();
		
		newX = Math.min(totalWidth - getWidth(), newX);
		final int delta = newX - getScrollX();

		mScroller.startScroll(getScrollX(), 0, getWidth(), 0, 500);

		invalidate();

	

	public int getScreenForView(View v) 
		int result = -1;
		if (v != null) 
			ViewParent vp = v.getParent();
			int count = getChildCount();
			for (int i = 0; i < count; i++) 
				if (vp == getChildAt(i)) 
					return i;
				
			
		
		return result;
	

	public void unlock() 
		mLocked = false;
	

	public void lock() 
		mLocked = true;
	

	void moveToDefaultScreen() 
		snapToScreen(mDefaultScreen);
		getChildAt(mDefaultScreen).requestFocus();
	

	@Override
	protected void onFinishInflate() 
		// TODO Auto-generated method stub
		super.onFinishInflate();
		View child;
		for (int i = 0; i < getChildCount(); i++) 
			child = getChildAt(i);
			child.setFocusable(true);
			child.setClickable(true);
		
	

	public void setCloseAnimation(CloseAnimation closeAnimation) 
		this.closeAnimation = closeAnimation;
	

	public interface CloseAnimation 

		public void closeMenuAnimation();
	

1、MainActivity主界面

package com.zhuixingba.main;

import com.zhuixingba.view.SlidingMenuView;
import com.zhuixingba.view.SlidingMenuView.CloseAnimation;

import android.os.Bundle;
import android.app.ActivityGroup;
import android.content.Intent;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Toast;

public class MainActivity extends ActivityGroup implements OnClickListener 
	public static SlidingMenuView slidingMenuView;
	private ViewGroup tabcontent;

	@Override
	public void onCreate(Bundle savedInstanceState) 
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		slidingMenuView = (SlidingMenuView) findViewById(R.id.main_menu_view);
		tabcontent = (ViewGroup) slidingMenuView.findViewById(R.id.main_body);
		initView();
	

	/**
	 * 初始化控件
	 */
	private void initView() 
		// 加载主页
		Intent i = new Intent(this, HomePageActivity.class);
		View v = getLocalActivityManager().startActivity(HomePageActivity.class.getName(), i).getDecorView();
		tabcontent.removeAllViews();
		tabcontent.addView(v);

		findViewById(R.id.btn_homepage).setOnClickListener(this);
		findViewById(R.id.btn_stars).setOnClickListener(this);
		findViewById(R.id.btn_personal).setOnClickListener(this);
		findViewById(R.id.btn_hotapp).setOnClickListener(this);
		findViewById(R.id.btn_set).setOnClickListener(this);
		findViewById(R.id.btn_exit).setOnClickListener(this);

		tabcontent.setOnClickListener(this);
	

	/**
	 * 点击事件
	 */
	@Override
	public void onClick(View v) 
		// TODO Auto-generated method stub

		switch (v.getId()) 
		case R.id.btn_homepage:// 首页
			showActivity(HomePageActivity.class);
			break;

		case R.id.btn_stars:// 关注明星
			showActivity(StarsActivity.class);
			break;
		case R.id.btn_personal:// 个人资料
			showActivity(PersonalActivity.class);
			break;
		case R.id.btn_hotapp:// 热点应用
			showActivity(HotAPPActivity.class);
			break;
		case R.id.btn_set:// 设置
			showActivity(SetActivity.class);
			break;
		case R.id.btn_exit:// 退出
			this.finish();
			break;
		case R.id.main_body:
			slidingMenuView.snapToScreen(1);
			break;
		default:
			break;
		
	

	/**
	 * 切换Activity的方法
	 * 
	 * @param c
	 *            参数为Activity
	 */
	private void showActivity(Class<?> c) 
		Intent i = new Intent(this, c);

		View view = getLocalActivityManager().startActivity(c.getName(), i).getDecorView();
		tabcontent.removeAllViews();
		tabcontent.addView(view);

		slidingMenuView.setCloseAnimation(new CloseAnimation() 

			@Override
			public void closeMenuAnimation() 
				// TODO Auto-generated method stub
				if (-slidingMenuView.getScrollX() == getWindowManager().getDefaultDisplay().getWidth()
						- (slidingMenuView.totalWidth - getWindowManager().getDefaultDisplay().getWidth())) 
					slidingMenuView.closeMenu_2(1);
				
			
		);

		slidingMenuView.closeMenu_1(1);
	

	private long exitTime = 0;

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) 

		if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) 
			if (slidingMenuView.getCurrentScreen() == 1) 

				if ((System.currentTimeMillis() - exitTime) > 2000) 
					Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show();
					exitTime = System.currentTimeMillis();
				 else 
					finish();
					System.exit(0);
				
			 else 
				slidingMenuView.snapToScreen(1);
			

			return true;

		 else if (keyCode == KeyEvent.KEYCODE_MENU && event.getAction() == KeyEvent.ACTION_DOWN) 
			if (slidingMenuView.getCurrentScreen() == 1) 
				slidingMenuView.snapToScreen(0);
			 else 
				slidingMenuView.snapToScreen(1);
			
			return true;
		
		return super.onKeyDown(keyCode, event);
	


3、MainActivity界面xml文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <com.zhuixingba.view.SlidingMenuView
        android:id="@+id/main_menu_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <LinearLayout
            android:id="@+id/main_left_menu"
            android:layout_width="240dip"
            android:layout_height="fill_parent"
            android:background="@android:color/darker_gray"
            android:orientation="vertical"
            android:padding="5dip" >

            <Button
                android:id="@+id/btn_homepage"
                android:layout_width="240dip"
                android:layout_height="wrap_content"
                android:text="@string/left_menu_homepage" />

            <Button
                android:id="@+id/btn_stars"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/left_menu_stars" />

            <Button
                android:id="@+id/btn_personal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/left_menu_personal" />

            <Button
                android:id="@+id/btn_hotapp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/left_menu_hotapp" />

            <Button
                android:id="@+id/btn_set"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/left_menu_set" />

            <Button
                android:id="@+id/btn_exit"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/left_menu_exit" />
        </LinearLayout>

        <FrameLayout
            android:id="@+id/main_body"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" >
        </FrameLayout>
    </com.zhuixingba.view.SlidingMenuView>

</RelativeLayout>

4、菜单界面点击显示左边菜单事件


if (MainActivity.slidingMenuView.getCurrentScreen() == 1) 
MainActivity.slidingMenuView.snapToScreen(0);
 else 
MainActivity.slidingMenuView.snapToScreen(1);



以上是关于Android 左边滑动菜单栏的主要内容,如果未能解决你的问题,请参考以下文章

vue项目侧边栏二级菜单只有一个时父级菜单不显示的问题

xamarin 形式的侧边栏抽屉

android侧边栏的滑动,以及实现滑动启动另外的activity

如何实现像 spotify 这样的侧边栏菜单?

Android简易音乐重构MVVM Java版-新增推荐菜单及侧边栏展示

Android App 侧边栏菜单的简单实现