我们知道在Google在support的库中为我们提供了DrawerLayout和SlidingPanelLayout两个布局来实现侧滑栏的效果,这两个布局让我们实现侧滑的功能变的非常容易,其实这两个布局在内部,都有一个ViewDragHelper类,通过ViewDragHelper类基本可以实现各种不同的滑动,拖放需求:
本文主要是讲述这个类,通过实现一个简单的例子来讲解,当滑动距离大于某个值的时候,就打开侧滑栏,否则不打开,回到原状态。
实现上图的一个效果,ViewDragHelper的使用有以下几步:
- 初始化ViewDragHelper
- 拦截事件
- computeScroll
- Callback
- 加载xml
初始化ViewDragHelper
首先我们自定义一个DragViewGroup继承自FrameLayout.然后在构造方法中初始我们的ViewDragHelper.通常是通过其静态工厂方法进行初始化。
helper = ViewDragHelper.create(this, callback);
注意:第一个参数是要监听的View,通常为viewGroup,第二个一个callback回调,这个回调就是ViewDragHelper的核心。
拦截事件
接下来,要重写拦截事件,将事件传递给ViewDragHelper进行处理,代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return helper.shouldInterceptTouchEvent(ev);
}
int lastX;
@Override
public boolean onTouchEvent(MotionEvent event) {
int x= (int) event.getX();
// 如果是往左滑动,则不可以滑动
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX=x;
break;
case MotionEvent.ACTION_MOVE:
if (lastX>x){
return true;
}
break;
}
helper.processTouchEvent(event);
//将触摸事件传递給ViewDragHelper,此操作必不可少。
return true;
}
注意:这其中最重要的就是三个地方,第一个就是helper.shouldInterceptTouchEvent(ev);,第二个 helper.processTouchEvent(event);第三个必须返回true。
computeScroll()
使用ViewDragHelper和Scroller一样都需要重写computeScroll方法,因为ViewDragHelper内部也是通过S roller来实现平滑移动的。这里的代码模版通常如下:
@Override
public void computeScroll() {
super.computeScroll();
if (helper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
Callback
下面就是最关键的Callback实现,通过如下所示代码来创建一个ViewDragHelepr.Callback,首先会自动重写一个tryCaptureView方法,例如在我们的小例子中,一个为MainView,一个为MenuView,那么这个方法就是指定哪一个View可以被拖动。我们这里是让MainView可以被移动
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//何时开始检测触摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView时开始检测
return mMainView == child;
}
}
然后重写clampViewPositionHorizontal()和clampViewPositionVertical这两个方法,分别对应水平方向和垂直方向的滑动。当返回0的时候,就是不可以滑动。
**注意:**clampViewPositionHorizontal(View child, int left, int dx)方法中,第一个参数就是要滑动的子View,这里是MainView,第二个参数就是水平方向的移动距离,dx是相对上一次移动的距离。
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//何时开始检测触摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView时开始检测
return mMainView == child;
}
//处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
//处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
}
最后callback需要重写 onViewReleased方法。这个方法实现手指离开屏幕后实现的操作
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//何时开始检测触摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView时开始检测
return mMainView == child;
}
//处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
//处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
//拖动结束后调用
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起后缓缓移动到指定位置
if (mMainView.getLeft() < 500) {
//关闭菜单
//相当于Scroller的startScroll方法
helper.smoothSlideViewTo(mMainView, 0, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
} else {
//打开菜单
helper.smoothSlideViewTo(mMainView, 300, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
加载xml
实现完上面的部分后,还需要重写两个方法onFinishInflate()和onSizeChanged(),初始化布局
//加载完xml布局调用。
@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
public class DragViewGroup extends FrameLayout {
private ViewDragHelper helper;
private View mMainView, mMenuView;
//侧滑栏的宽
private int mWidth;
public DragViewGroup(Context context) {
super(context);
init();
}
public DragViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
//加载完xml布局调用。
@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 helper.shouldInterceptTouchEvent(ev);
}
int lastX;
@Override
public boolean onTouchEvent(MotionEvent event) {
int x= (int) event.getX();
// 如果是往左滑动,则不可以滑动
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX=x;
break;
case MotionEvent.ACTION_MOVE:
if (lastX>x){
return true;
}
break;
}
helper.processTouchEvent(event);
//将触摸事件传递給ViewDragHelper,此操作必不可少。
return true;
}
/**
* 初始化ViewDragHelper
*/
public void init() {
helper = ViewDragHelper.create(this, callback);
}
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//何时开始检测触摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView时开始检测
return mMainView == child;
}
//处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
//处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
//拖动结束后调用
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起后缓缓移动到指定位置
if (mMainView.getLeft() < 500) {
//关闭菜单
//相当于Scroller的startScroll方法
helper.smoothSlideViewTo(mMainView, 0, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
} else {
//打开菜单
helper.smoothSlideViewTo(mMainView, 300, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
@Override
public void computeScroll() {
super.computeScroll();
if (helper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<com.aofei.myview.view8_viewdragholder.DragViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_green_dark">
<LinearLayout
android:layout_width="300dp"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="30sp"
android:background="@android:color/holo_green_dark"
android:text="侧滑栏"/>
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:scaleType="fitXY"
android:src="@mipmap/you"/>
</com.aofei.myview.view8_viewdragholder.DragViewGroup>
</FrameLayout>
activity
public class DragViewActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_help);
}
}
通过以上内容,对于滑动事件会有一个更深的了解