转载请注明出处: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); }
重写拦截事件方法,将事件传递给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); } }
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>