我们在淘宝上购物的时候,在浏览商品页面时,有看到有”继续拖动,查看图文详情”,实则就是纵向拖动的Viewpager。今天我们就要来实现它。先上效果图。
要实现上面的效果,我们今天必须学习两样东西,一个就是ViewDragHelper,另外一个就是GestureDetectorCompat。好了,我们先,来看看View的拖动工具类。ViewDragHelper的基本用法很简单。官方自带的DrawerLayout侧滑菜单,也是用的这个实现的额。下面我们实现一个View的拖动效果。
先自定义一个View在构造方法里面实例化一个ViewDragHelper对象,直接调用静态方法里面有三个参数,第一个Context。第二个精度,第三个拖动回调。这里要说一下,精度给的值越大越敏感。
mDragHelper = ViewDragHelper.create(this, 10f, new DragHelperCallback());
第三个参数通过,继承ViewDragHelper.Callback,你就可以在回调中处理各种拖动行为。当然这里我们自定义的一个类DragHelperCallback来重写里面的方法,简单来讲,我们重写三个方法就可以了,如下
@Override
public boolean tryCaptureView(View arg0, int arg1) {
return true;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dy) {
return left;
}
以上三个方法,tryCaptureView的返回值可以决定一个parentview中哪个子view可以拖动,即可以判断方法参数中的int值,给以具体的返回false或true。返回true表示该子View可以被拖动。英语好的朋友可能已经大概知道接下来两个方法的作用了;对,翻译过来,就是固定View的纵向和横向位置。也就是说给拖动View设置边界。。我们重点来说一下方法里面第二个参数。第二个参数是指当前拖动子view应该到达的x坐标。按照正常逻辑,应该是原值返回,但是我们在使用手机或者其他电子设备的时候是希望在屏幕以内,以外我们看不到,就没有意义了。所以,我们要更改一下,方法里面的内容让其返回一个恰当的值。
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//得到顶部内边距
int topBound = getPaddingTop();
//得到底部内边距
int bottomBound = getHeight() - child.getHeight();
//返回最小值
int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
我们希望子View在ViewGroup内运动。最小Y轴即为topBound。最大即为bottomBound。通过Math.min方法判断最小值,返回。就可以让子View在父View内部运动。不会滑出边界。同样横向方法照样改写。最小横向移动位置在leftBound。最大为整个view最右边位置减去子view最右边位置再减去View的右内边距。
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int leftBound = getPaddingLeft();
int rightBound = getWidth() - child.getWidth();
int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
这样,就实现了上面的效果,很简单吧,但是如果想要更复杂的操作,那就得继续深入。我们还可以重写几个方法用于其他功能。比如
//手指释放的时候回调// 滑动松开后
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel){}
//在边界拖动时回调
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId){}
我们想要view拖动后回到原位,首先拖动前先保存,view的位置坐标,然后在onViewReleased()方法种调用settleCapturedViewAt(int,int)方法里面传入x,y值。最后重绘invalidate();就可以了。边界拖动同样通过调用captureChildView(edgeFlags, pointerId)就可以了。我们就简单的做了一个可拖动子view的viewgroup了。全部代码贴出来如下。
public class MyViewGoup extends LinearLayout {
private ViewDragHelper mDragger;
public MyViewGoup(Context context, AttributeSet attrs) {
super(context, attrs);
mDragger = ViewDragHelper.create(this, 10f, new DragHelperCallback());
}
class DragHelperCallback extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int leftBound = getPaddingLeft();
int rightBound = getWidth() - child.getWidth();
int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//得到顶部内边距
int topBound = getPaddingTop();
//得到底部内边距
int bottomBound = getHeight() - child.getHeight();
//返回最小值
int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragger.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);
return true;
}
}
上面简单的view拖动,我们就完成了,接下来开始我们的上下拖动ViewPager。大概思路也是一样的。先自定一个类,继承自ViewGroup。然后。在xml文件中装入两个子布局(ViewGroup)。然后,通过设置两个子布局的高度来控制view的拖动。同样开始也是构造方法,创建ViewDragHelper对象。设置回调。设置监听,初始化炒作。
public class DragLayout extends ViewGroup {
/**
* 拖动工具类
*/
private final ViewDragHelper mDragHelper;
/**
* 手势处理,Y轴
**/
private GestureDetectorCompat gestureDetectorCompat;
/**
* 上下两个frameLayout,在Activity中注入fragment
*/
private View frameView1, frameView2;
private int viewHeight;
/**
* 滑动速度的阈值,超过这个绝对值认为是上下
*/
private static final int VEL_THRESHOLD = 100;
/**
* 单位是像素,当上下滑动速度不够时,通过这个阈值来判定是应该粘到顶部还是底部