仿QQ侧滑效果ViewDragHelper


转载请注明出处:http://blog.csdn.net/wei_chong_chong/article/details/50807353

Google在其support库中为我们提供了DrawerLayout和SlidingPaneLayout两个布局来帮助我们开发侧边栏滑动的效果

,在这两个布局背后有一个功能强大的ViewDragHelper类,通过ViewDragHelper基本可以实现各种不同的滑动,拖放的需求,是解决各种滑动问题的终极绝招。

(实现原理:说是滑动菜单的框架,其实就是我们自定义一个布局,在这个自定义布局中实现好滑动菜单的功能,然后只要在Activity的布局文件里面引入我们自定义的布局,这个Activity就拥有了滑动菜单的功能了。)

下面就带大家用这个类实现一个仿QQ侧滑栏的效果

1.初始化ViewDragHelper(稍后在构造方法中调用这个初始化方法可进行初始化操作,注意:每个构造方法都要调用)ViewGroup通常定义在一个ViewGroup的内部,并通过其静态工厂方法进行初始化代码如下

private void initView() {
		mViewDragHelper = ViewDragHelper.create(this, callback);
	}

它的第一个参数是要监听的View,通常需要是一个ViewGroup,即parentView;第二个参数是一个Callback回调,这个回调就是这个ViewDragHelper的逻辑核心,放在下面讲解


2.拦截事件

重写拦截事件方法,将事件传递给ViewDragHelper进行处理,代码如下所示。

@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		return mViewDragHelper.shouldInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		//将触摸事件传递给ViewDragHelper,此操作必不可少
		mViewDragHelper.processTouchEvent(event);
		return true;
	}
这一点的知识点属于Android事件机制部分的,以后会补充。

3.处理computeScroll()

(ViewDragHelper内部也是通过Scroller来实现平滑移动的),通常可以使用下面的模板代码

@Override
	public void computeScroll() {
		if (mViewDragHelper.continueSettling(true)) {
			ViewCompat.postInvalidateOnAnimation(this);
		}
	}


4.处理回调Callback

private ViewDragHelper.Callback callback =
			new ViewDragHelper.Callback() {

		// 何时开始检测触摸事件
		@Override
		public boolean tryCaptureView(View child, int pointerId) {
			//如果当前触摸的child是mMainView时开始检测
			return mMainView == child;
		}


有两个滑动方法(水平,垂直)如果要实现滑动效果,这两个方法必须重写。

因为默认返回值是0,不滑动,这里我们只需要水平滑动,所以垂直滑动的方法让他返回0(不重写也行默认是0嘛)

// 处理垂直滑动
		@Override
		public int clampViewPositionVertical(View child, int top, int dy) {
			return 0;
		}

		// 处理水平滑动
		@Override
		public int clampViewPositionHorizontal(View child, int left, int dx) {
			return left;
		}
clampViewPositionVertical(View child, int top, int dy)中的参数top,代表在垂直方向上child移动的距离,而dy则表示比较前一次的增量。同理垂直方法参数含义类似。通常情况下,只需要返回top和left即可,但当需要更加精确地计算padding等属性的时候,就需要对left进行一些处理,并返回合适的大小的值。


仅仅是通过重写上面的这三个方法就可以实现最基本的滑动效果了,代码如下:

<pre name="code" class="java">private ViewDragHelper.Callback callback =
			new ViewDragHelper.Callback() {

		// 何时开始检测触摸事件
		@Override
		public boolean tryCaptureView(View child, int pointerId) {
			//如果当前触摸的child是mMainView时开始检测
			return mMainView == child;
		}

		
		// 处理垂直滑动
		@Override
		public int clampViewPositionVertical(View child, int top, int dy) {
			return 0;
		}

		// 处理水平滑动
		@Override
		public int clampViewPositionHorizontal(View child, int left, int dx) {
			return left;
		}

};

 

重写onViewReleased(),通过重写这个方法,可以非常简单地实现当手指离开屏幕后实现的操作,当然这个方法内部是通过Scroller类实现的部分代码如下

// 拖动结束后调用
		@Override
		public void onViewReleased(View releasedChild, float xvel, float yvel) {
			super.onViewReleased(releasedChild, xvel, yvel);
			//手指抬起后缓慢移动到指定位置
			if (mMainView.getLeft() < 500) {
				//关闭菜单
				//相当于Scroller的startScroll方法
				mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
				ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
			} else {
				//打开菜单
				mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
				ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
			}
		}
	};

设置MainView移动后左边距小于500像素的时候,就使用smoothSlidViewTo()方法来将MainView还原到初始状态,即

坐标为(0,0)的点,当左边距大于500的时候,则将MainView移动到(300,0)坐标,即显示MenuView。


最后按顺序将子View分别定义成MenuView和MainView使用getChildAt(0)获取到的布局作为左边布局,使用getChildAt(1)获取到的布局作为右边布局。并在onSizeChanged()方法中获得View宽度;

@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		mMenuView = getChildAt(0);
		mMainView = getChildAt(1);
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mWidth = mMenuView.getMeasuredWidth();
	}



实现过程如下

首先写一个DragViewGroup类继承FrameLayout

public class DragViewGroup extends FrameLayout {

	private ViewDragHelper mViewDragHelper;
	private View mMenuView, mMainView;
	private int mWidth;

	public DragViewGroup(Context context) {
		super(context);
		initView();
	}

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

	public DragViewGroup(Context context,
			AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		initView();
	}

	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		mMenuView = getChildAt(0);
		mMainView = getChildAt(1);
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mWidth = mMenuView.getMeasuredWidth();
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		return mViewDragHelper.shouldInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		//将触摸事件传递给ViewDragHelper,此操作必不可少
		mViewDragHelper.processTouchEvent(event);
		return true;
	}

	private void initView() {
		mViewDragHelper = ViewDragHelper.create(this, callback);
	}

	private ViewDragHelper.Callback callback =
			new ViewDragHelper.Callback() {

		// 何时开始检测触摸事件
		@Override
		public boolean tryCaptureView(View child, int pointerId) {
			//如果当前触摸的child是mMainView时开始检测
			return mMainView == child;
		}

		// 触摸到View后回调
		@Override
		public void onViewCaptured(View capturedChild,
				int activePointerId) {
			super.onViewCaptured(capturedChild, activePointerId);
		}

		// 当拖拽状态改变,比如idle,dragging
		@Override
		public void onViewDragStateChanged(int state) {
			super.onViewDragStateChanged(state);
		}

		// 当位置改变的时候调用,常用与滑动时更改scale等
		@Override
		public void onViewPositionChanged(View changedView,
				int left, int top, int dx, int dy) {
			super.onViewPositionChanged(changedView, left, top, dx, dy);
		}

		// 处理垂直滑动
		@Override
		public int clampViewPositionVertical(View child, int top, int dy) {
			return 0;
		}

		// 处理水平滑动
		@Override
		public int clampViewPositionHorizontal(View child, int left, int dx) {
			return left;
		}

		// 拖动结束后调用
		@Override
		public void onViewReleased(View releasedChild, float xvel, float yvel) {
			super.onViewReleased(releasedChild, xvel, yvel);
			//手指抬起后缓慢移动到指定位置
			if (mMainView.getLeft() < 500) {
				//关闭菜单
				//相当于Scroller的startScroll方法
				mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
				ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
			} else {
				//打开菜单
				mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
				ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
			}
		}
	};

	@Override
	public void computeScroll() {
		if (mViewDragHelper.continueSettling(true)) {
			ViewCompat.postInvalidateOnAnimation(this);
		}
	}
}

然后在布局文件中直接引用即可,使用方法类似于自定义控件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.qqviewdraghelper.MainActivity" >

    <com.example.qqviewdraghelper.DragViewGroup
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/holo_blue_light" >

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Menu" />
        </FrameLayout>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/holo_orange_dark" >

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Main" />
        </FrameLayout>
    </com.example.qqviewdraghelper.DragViewGroup>

</RelativeLayout>




评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值