滑滑滑,滑出一个菜单

/ 0评 / 0

打造自己的侧滑菜单

现在大多数的软件中都能看到侧滑菜单的身影,而且这个功能看起来真的很高大上,不是么,滑的一下就出来了一个菜单,真牛逼啊,下面我就来介绍下如何实现这个功能,废话不多说先上图。这里我就使用了两个很简单的布局。

sliding

具体实现

本着能简单就简单的原则,在开始设计这个菜单之前,我们想一下,在android中,有什么视图是可以横向滑动的,答案就是HorizontalScrollView,在我们平常的布局中,我们只要使用HorizontalScrollView包裹一些视图,那么这些视图就可以开始滑动了,那么,我们这里可不可以继承这个类,然后自己修改下他的属性,变成自己需要的侧滑菜单呢,答案是肯定的,不然我这篇博客也不用写了。

上面啰嗦了那么多,现在就来介绍下原理,首先我们自定义一个类继承自HorizontalScrollView,这样我们的布局就拥有了HorizontalScrollView可以横向滚动的特性,由于HorizontalScrollView只能有一个子View,所以在xml中,使用一个线性布局包裹了两个自布局,然后在onMeasure中获取这个线性布局,然后获取它的两个子布局,第一个当做侧滑菜单,第二个当做主布局,多余两个个也很好处理,可以直接在代码中删除掉多余的布局,也可以类似于HorizontalScrollView直接抛出异常,用于提示用户。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	LinearLayout ll = (LinearLayout) this.getChildAt(0);
	measureChildren(widthMeasureSpec, heightMeasureSpec);
	if (ll != null) {
		//获取两个布局
		mSlidingView = ll.getChildAt(0);
		mContentView = ll.getChildAt(1);
		if (mSlidingView != null && mContentView != null) {
			//设置侧滑菜单的宽度:宽度为整个屏幕的宽度 - 和屏幕右边的宽度
			mSlidingView.getLayoutParams().width = mSlidingMenuWidth;
			//设置主布局的宽度 : 为整个屏幕的宽度 
			mContentView.getLayoutParams().width = mScrWidth;
		}
	}
	setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}

隐藏掉侧滑菜单

HorizontalScrollView中有这么一个方法 : scrollTo(int xOffset,int yOffset),这个可以用于移动当前布局的位置,我们如果在一开始的时候就将布局往左边移动侧滑菜单布局的宽度,那不就能隐藏掉侧滑菜单了么?所以我们可以在onLayout中调用这个方法去隐藏(onLayout只调用一次)。

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
	super.onLayout(changed, l, t, r, b);
	this.scrollTo(mSlidingMenuWidth, 0);
}

增加手势判断

在上面一步中,我们也只将侧滑菜单给隐藏掉了,可是用手指滑动以后,滑动到哪里就是哪里了,我们想让滑动以后,如果滑动的距离大于某一个值,自己滑动出来,小于某个值就自己收缩回去,该怎么实现呢?上面我们隐藏的时候调用了 scrollTo(int xOffset,int yOffset)这个方法去移动侧滑菜单,那么我们是不是只需要在手指抬起的时候,判断一下滑动的距离,然后再调用这个函数就可以实现移动了呢?

在下面的代码中我们可以看到,我们在手指抬起的时候通过getScrollX()来判断当前移动的距离,如果大于侧滑菜单的一半,那就调用this.smoothScrollTo()这个方法去完全打开菜单,小于则关闭,这里的smoothScrollTo和上文中的scrollTo类似,不同的是,smoothScrollTo会有移动的动画,平滑的打开和关闭菜单。

@Override
public boolean onTouchEvent(MotionEvent ev) {

	switch (ev.getAction()) {
	case MotionEvent.ACTION_UP:
		if (this.getScrollX() > mHalfMenuWidth) {
			this.smoothScrollTo(mSlidingMenuWidth, 0);
		} else {
			this.smoothScrollTo(0, 0);
		}
		return true;
	}
	return super.onTouchEvent(ev);
}

完整代码

下面我贴出目前为止"完整"的代码。这里之所以打上引号,是因为还有很多优化的地方,这里我们也只是非常基础的给系统的HorizontalScrollView增加了一个手势判断和一开始隐藏的功能而已,是不是觉得很简单呢?不过这样可不够,不信我们看看最后的效果。

package com.jay.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.widget.HorizontalScrollView;
import android.widget.LinearLayout;

public class SlidingMenu extends HorizontalScrollView {

	/**
	 * 屏幕宽
	 */
	private int mScrWidth;

	/**
	 * 侧滑菜单宽
	 */
	private int mSlidingMenuWidth;

	/**
	 * 侧滑view
	 */
	private View mSlidingView;

	/**
	 * 主界面View
	 */
	private View mContentView;

	/**
	 * 滑动的阈值,设置为了一半菜单宽,当大于这个值,打开菜单,小于这个值则关闭
	 */
	private int mHalfMenuWidth;

	/**
	 * 侧滑菜单滑出的最大距离后和屏幕右边的距离,默认为150dp
	 */
	private int mRightPadding = (int) TypedValue.applyDimension(
			TypedValue.COMPLEX_UNIT_DIP, 150, getResources()
					.getDisplayMetrics());

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

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

	public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		TypedArray array = context.obtainStyledAttributes(attrs,
				R.styleable.SlidingMenu, defStyle, 0);

		int count = array.getIndexCount();
		for (int i = 0; i < count; i++) {
			switch (array.getIndex(i)) {
			case R.styleable.SlidingMenu_rightPadding:
				mRightPadding = (int) array.getDimension(i, 50f);
				break;
			}
		}
		array.recycle();
		DisplayMetrics metrics = this.getResources().getDisplayMetrics();
		mScrWidth = metrics.widthPixels;

		mHalfMenuWidth = (mScrWidth - mRightPadding) / 2;
		mSlidingMenuWidth = mScrWidth - mRightPadding;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		LinearLayout ll = (LinearLayout) this.getChildAt(0);
		measureChildren(widthMeasureSpec, heightMeasureSpec);
		if (ll != null) {
			// 获取两个布局
			mSlidingView = ll.getChildAt(0);
			mContentView = ll.getChildAt(1);
			if (mSlidingView != null && mContentView != null) {
				// 设置侧滑菜单的宽度:宽度为整个屏幕的宽度 - 和屏幕右边的宽度
				mSlidingView.getLayoutParams().width = mSlidingMenuWidth;
				// 设置主布局的宽度 : 为整个屏幕的宽度
				mContentView.getLayoutParams().width = mScrWidth;
			}
		}
		setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		this.scrollTo(mSlidingMenuWidth, 0);
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {

		switch (ev.getAction()) {
		case MotionEvent.ACTION_UP:
			if (this.getScrollX() > mHalfMenuWidth) {
				this.smoothScrollTo(mSlidingMenuWidth, 0);
			} else {
				this.smoothScrollTo(0, 0);
			}
			return true;
		}
		return super.onTouchEvent(ev);
	}
}

gif

可以看到,我们的侧滑菜单是跟着主布局一起出来的,而不是“在主布局的下面”,这也是HorizontalScrollView的特性决定的,不过我们可以在滑动的过程中,动态调整侧滑菜单的位置,让他看起来“在主布局的下面”。

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
	super.onScrollChanged(l, t, oldl, oldt);
	mSlidingView.setTranslationX(l);
}

很简单的一句话,当视图移动的时候,当前视图往右边移动了多少,我们就把侧滑菜单的视图跟着往右边移动多少,这样看起来,就可以实现博客一开头的效果了。我们还可以实现跟更多的动画,这里就不在扩展了。具体可以查看下面的源码。

PS:由于本人技术有限,如果有遗漏或者错误,欢迎留言指出。

源码:360云盘下载   访问密码 4e3e

发表评论

您的电子邮箱地址不会被公开。