Android实战之 SlidingMenu视觉差动画简单版和Activity视觉差动画+自定义View实现仿系统DrawerLayout覆盖型菜单几种方式

      转载请标明原地址:http://blog.csdn.net/gaolei1201/article/details/50404941


首先介绍本篇文章的重点:

1.视觉差侧滑菜单,即菜单和主界面都滑动,其实也比较常见。有开源项目SlidingMenu,但太复杂了,且在我使用过程中无法完美实现透明状态栏,中间会有一条分割线。看我简单实现

2.Activity视觉差动画

3.支持侧滑销毁Activity,且不用担心被子控件消耗手势监听事件


其实我也是借鉴别人的,自己发明创造毕竟毕竟困难。正所谓天下文章一大,看你会不会抄,呵呵。

第一种方法:侧滑菜单SlidingMenu继承HorizontalScrollView,手势滑动。

第二种方法 :侧滑菜单SlidingMenu继承ViewGroup,放入菜单和内容两个子控件,根据手势滑动。

第三种方法:利用ViewDragHelper,可参考:http://blog.csdn.net/developer_jiangqq/article/details/50253925

效果图:


        

下面是第一种方法侧滑菜单主要代码,里面有注释:

<span style="font-size:14px;"><span style="font-size:14px;">package com.gaolei.slidingmenu;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import com.example.zhy_slidingmenu.R;
import com.nineoldandroids.view.ViewHelper;

public class SlidingMenu extends HorizontalScrollView {
	/**
	 * 屏幕宽度
	 */
	private int mScreenWidth;
	/**
	 * dp
	 */
	private int mMenuRightPadding;
	/**
	 * 菜单的宽度
	 */
	private int mMenuWidth;
	private int mHalfMenuWidth;

	public static boolean isOpen;

	private boolean once;

	private ViewGroup mMenu;
	private ViewGroup mContent;

	public SlidingMenu(Context context, AttributeSet attrs) {
		this(context, attrs, 0);

	}

	public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		WindowManager mWM = ((WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE));
		DisplayMetrics mDisplayMetrics = new DisplayMetrics();
		mWM.getDefaultDisplay().getMetrics(mDisplayMetrics);
		mScreenWidth = mDisplayMetrics.widthPixels;

		TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
				R.styleable.SlidingMenu, defStyle, 0);
		int n = a.getIndexCount();
		for (int i = 0; i < n; i++) {
			int attr = a.getIndex(i);
			switch (attr) {
			case R.styleable.SlidingMenu_rightPadding:
				// 默认50
				mMenuRightPadding = a.getDimensionPixelSize(attr,
						(int) TypedValue.applyDimension(
								TypedValue.COMPLEX_UNIT_DIP, 50f,
								getResources().getDisplayMetrics()));// 默认为10DP
				// Log.d("gaolei","mMenuRightPadding-------------"+mMenuRightPadding);
				break;
			}
		}
		a.recycle();
	}

	public SlidingMenu(Context context) {
		this(context, null, 0);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		/**
		 * 显示的设置一个宽度
		 */
		if (!once) {
			LinearLayout wrapper = (LinearLayout) getChildAt(0);
			mMenu = (ViewGroup) wrapper.getChildAt(0);
			mContent = (ViewGroup) wrapper.getChildAt(1);

			mMenuWidth = mScreenWidth - mMenuRightPadding;
			mHalfMenuWidth = mMenuWidth / 2;
			mMenu.getLayoutParams().width = mMenuWidth;
			mContent.getLayoutParams().width = mScreenWidth;

		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if (changed) {
			// 将菜单隐藏
			this.scrollTo(mMenuWidth, 0);
			once = true;
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		int action = ev.getAction();
		switch (action) {
		// Up时,进行判断,如果显示区域大于菜单宽度一半则完全显示,否则隐藏
		case MotionEvent.ACTION_UP:
			int scrollX = getScrollX();
			if (scrollX > mHalfMenuWidth) {
				this.smoothScrollTo(mMenuWidth, 0);
				isOpen = false;
				MainActivity.shadow_layout.setVisibility(View.GONE);
			} else {
				this.smoothScrollTo(0, 0);
				isOpen = true;
				MainActivity.shadow_layout.setVisibility(View.VISIBLE);
			}
			return true;
		case MotionEvent.ACTION_MOVE:

			break;
		}
		return super.onTouchEvent(ev);

	}

	// 这里是拦截菜单布局滑动
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_MOVE:
			final float curX = ev.getX();
			final float curY = ev.getY();
			if (curY > 0 && curY < 500) {
				return false;
			}
			if (isOpen && curX < mMenuWidth) {
				return false;
			}
		}

		return super.onInterceptTouchEvent(ev);
	}

	/**
	 * 打开菜单
	 */
	public void openMenu() {
		if (isOpen)
			return;
		this.smoothScrollTo(0, 0);
		isOpen = true;
		MainActivity.shadow_layout.setVisibility(View.VISIBLE);
	}

	/**
	 * 关闭菜单
	 */
	public void closeMenu() {
		if (isOpen) {
			this.smoothScrollTo(mMenuWidth, 0);
			isOpen = false;
			MainActivity.shadow_layout.setVisibility(View.GONE);
		}
	}

	/**
	 * 切换菜单状态
	 */
	public void toggle() {
		if (isOpen) {
			closeMenu();

		} else {
			openMenu();

		}
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		float scale = l * 1.0f / mMenuWidth; //             这段代码是最重要的调用nineoldandroids.jar 来实现菜单视觉差效果,自己可改动 0.7f 试试
		ViewHelper.setTranslationX(mMenu, mMenuWidth * scale * 0.7f);

	}
}</span>
 

下面是第二种方法SlidingMenu自定义ViewGroup实现方法:

<span style="font-size:14px;">public class SlideLayout extends ViewGroup {
	private static String TAG = "SlideMenuLayout";
	
	private Context mContext;
	private Scroller mScroller;    //Android 提供的滑动辅助类
	private int mTouchSlop = 0 ;    //在被判定为滚动之前用户手指可以移动的最大值
	private VelocityTracker mVelocityTracker;    //用于计算手指滑动的速度
	public static final int SNAP_VELOCITY = 200;    //滚动显示和隐藏左侧布局时,手指滑动需要达到的速度:每秒200个像素点
	private int mMaxScrollX = 0;    //最大滚动距离,等于menu的宽度
	private int menuWidth=0;
	private int scrollX=0;
	private int oldX;
	public static boolean clickMenuToClose=false;
	
	public void setMaxScrollX(int maxScrollX) {
		this.mMaxScrollX = maxScrollX;
	}

	private float mDownX;    //一次按下抬起的动作中,按下时的X坐标,用于和抬起时的X比较,判断移动距离。少于mTouchSlop则判定为原地点击
	private float mLastX;    //记录滑动过程中的X坐标
	
	private boolean isMenuOpen = false;    //菜单界面是否被打开,只有完全打开才为true
	public boolean isMenuOpen() {
		return isMenuOpen;
	}

	private boolean isTouchFinished = true;
	
	private View mContent;
	
	public SlideLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		init();
	}
	
	private void init() {
		Log.v(TAG, "init start");
		mScroller = new Scroller(mContext);
		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			scrollX=mScroller.getCurrX();
//			这里是判断打开菜单时即向右滑动菜单向右滑动 实现视觉差滑动效果,scrollX就是监听startScroll开始滚动程X的值,因为getScrollX()只是监听手在屏幕上滑动的过程,手离开后就要在这里监听,0.7是可以根据需求改动
			if(scrollX<oldX){
//			Log.d("gaolei", "scrollX2--------------------------------|"+scrollX);
			ViewHelper.setTranslationX(getChildAt(0), (menuWidth+scrollX)* 0.7f);
			}
//			这里是判断点击菜单按钮关闭时,对菜单做处理,已达到视觉差效果,不然的话没有
			if(clickMenuToClose){
				ViewHelper.setTranslationX(getChildAt(0), (menuWidth+scrollX)* 0.7f);
			}
			oldX=scrollX;
			postInvalidate();
			
		}
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int width = MeasureSpec.getSize(widthMeasureSpec);
		int height = MeasureSpec.getSize(heightMeasureSpec);
		setMeasuredDimension(width, height);

		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			View child = getChildAt(i);
			child.measure(widthMeasureSpec, heightMeasureSpec);
		}
	}
	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		mContent = getChildAt(1);
		if (mContent != null) {
			mContent.layout(l, t, l + mContent.getMeasuredWidth(), t + mContent.getMeasuredHeight());
		}
		
		 menuWidth = 0;
		View menu = getChildAt(0);
		if (menu != null) {
			ViewGroup.LayoutParams layoutParams = menu.getLayoutParams();
			menuWidth = layoutParams.width;
			menu.layout(l - menuWidth, t, l, t + menu.getMeasuredHeight());
		}
	
		
	}
	
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		createVelocityTracker(event);
		int curScrollX = getScrollX();
		// 检查触摸点是否在滑动布局(内容content)中,如果不是则返回false,即本View不处理该事件
		if (mContent != null && isTouchFinished) {
			Rect rect = new Rect();
			mContent.getHitRect(rect);
			if (!rect.contains((int)event.getX() + curScrollX, (int)event.getY())) {
				return false;
			}
		}

		float x = event.getX();    //取得本次event的X坐标
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mDownX = x;
			mLastX = x;
			isTouchFinished = false;
			break;
		case MotionEvent.ACTION_MOVE:
			int deltaX = (int)(mLastX - x);
			if((curScrollX + deltaX) < -mMaxScrollX) {
				deltaX = -mMaxScrollX - curScrollX;
			}
			if((curScrollX + deltaX) > 0){
				deltaX = -curScrollX;
			}
//			这里是判断打手势滑动开菜单时即向右滑动菜单向右滑动 实现视觉差滑动效果,menuWidth+getScrollX()是菜单滑动时X的变化,手离开后就要在这里监听,0.7是可以根据需求改动
		
			
			if (deltaX != 0) {
				scrollBy(deltaX, 0);
			}
			//这句话一定要放到scrollBy后面 不然你快速滑动左边会有空白闪现<pre code_snippet_id="1564370" snippet_file_name="blog_20160125_2_2220112" name="code" class="html">                       ViewHelper.setTranslationX(getChildAt(0), (menuWidth+getScrollX())* 0.7f);</span>

 

下面要讲一下,手势滑动Activity边缘销毁Activity,主要难点是:里面子控件会拦截手势事件使你监听不到,那么你也就不能触发销毁Activity,看看哥是咋弄的

<span style="font-size:14px;"><span style="font-size:14px;">/**
 * 自定义RelativeLayout 拦截ListView监听事件
 */
public class CustomRelativeLayout extends RelativeLayout {

    private FinishActivityListener finishActivityListener;
    private int downX;

    public void setFinishActivityListener(FinishActivityListener finishActivityListener) {
        this.finishActivityListener = finishActivityListener;
    }

    public CustomRelativeLayout(Context context) {
        super(context);
    }

    public CustomRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
//onTouchEvent()是获取不到手势事件的,因为被ListView消耗了;只有在这判断手势监听事件 ,才能提前ListView获得
    public boolean onInterceptTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        //说明将onTouch拦截在此控件,进而执行此控件的onTouchEvent
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getRawX();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getRawX();
                Log.d("gaolei", "moveX-----------------" + moveX);
                Log.d("gaolei", "downX-----------------" + downX);
                //这里是判断最小滑动5,然后滑动边缘0~50彩触发销毁Activity,滑动屏幕中间不销毁
                if (moveX - downX > 5 && downX < 50) {
                	
                    finishActivityListener.onFinishActivity();
                }
                break;
        }
        return super.onInterceptTouchEvent(event);
    }
}
</span></span>


1、视觉差视觉差侧滑菜单SlidingMenu还可以参考其它侧滑菜单实现方式http://blog.csdn.net/developer_jiangqq/article/details/50253925

2、自定义 View实现类似系统DrawerLayout覆盖型菜单的几种方式,代码地址:https://github.com/gaoleiandroid1201/DrawerLayout




             源码地址,点击下载......




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值