转载请注明出处: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>