FormLayoutManager首页,里面有github地址
目录
前言
FormLayoutManager的首篇博客有说到,FormLayoutManager实现的表格,那个RecyclerView是可以全方位滚动的,有点像图片PhotoView那样可以拖来拖去。而以前HorizontalScrollView嵌套Recyclerview来实现表格,它的交互效果是,当你进行一个方向滚动的时候,除非把手指松开,要不不能改变滚动方向。比如你对表格正在进行水平滚动,只要你的手指不松开,即使你上下滑,表格也不会上下滚。
所以这篇文章就是讲解万一你的产品希望你的表格跟以前一样的滚动交互,你要怎么实现。
HVSingleRecylerView
其实非常简单,只要在布局文件的RecyclerView替换成HVSingleRecyclerView,就大功告成了。所以下面主要就是说HVSingleRecyclerView主要做了什么。
思路
思路挺明确,就是截取RecyclerView的触摸事件,当用户滑动的X距离比Y距离大的时候,我们认为他是在做水平滑动,否则认为他在做垂直滑动,这是配置一下FormLayoutManager,让用户只能在对应方向滑动。而当用户松开手指的时候,就把一些状态还原。
代码
看一下HVSingleRecyclerView的源码,你可以发现,只是很普通得获取滑动的X和Y距离。但是我是在onInterceptTouchEvent里面获取ACTION_DOWN事件的x,y坐标。
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
// 最好在这里获取点下的坐标,因为当recyclerview的item加了点击事件的话,onTouchEvent是拿不到ACTION_MOVE的事件
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = e.getX();
mDownY = e.getY();
break;
}
return super.onInterceptTouchEvent(e);
}
那是因为,当我们的adapter的item设置了点击事件的时候,你就会发现ACTION_DOWN事件是不会进到onTouchEvent的,故我就想到在拦截事件的方法那里先拿到点下的x,y坐标。下面再看一下onTouchEvent的代码
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = e.getX() - mDownX;
float dy = e.getY() - mDownY;
if (mScrollType == -1) {
FormLayoutManager layoutManager = (FormLayoutManager) getLayoutManager();
if (Math.abs(dx) > Math.abs(dy)) {
mScrollType = RecyclerView.HORIZONTAL;
mPreScrollType = RecyclerView.HORIZONTAL;
layoutManager.setCanScrollV(false);
}
else {
mScrollType = RecyclerView.VERTICAL;
mPreScrollType = RecyclerView.VERTICAL;
layoutManager.setCanScrollH(false);
}
}
break;
case MotionEvent.ACTION_UP:
mScrollType = -1;
FormLayoutManager layoutManager1 = (FormLayoutManager) getLayoutManager();
layoutManager1.setCanScrollV(true);
layoutManager1.setCanScrollH(true);
break;
}
return super.onTouchEvent(e);
}
我们是怎样让用户只能水平滚和垂直滚的呢?主要就是setCanScrollH和setCanScrollV方法。好,再看一下FormLayoutManager的这两个方法是干什么。
// 设置能否垂直滚动
public void setCanScrollV(boolean canScrollV) {
mIsCanScrollV = canScrollV;
}
// 设置能否水平滚动
public void setCanScrollH(boolean canScrollH) {
mIsCanScrollH = canScrollH;
}
@Override
public boolean canScrollVertically() {
return mIsCanScrollV;
}
@Override
public boolean canScrollHorizontally() {
return mIsCanScrollH;
}
没错,其实只是改了一个变量而已。因为LayoutManager本来就自带一个canScrollVertically和canScrollHorizontally,这两个方法返回的值就是决定我们的RecyclerView能怎样滚。
目光再回到onTouchEvent,在设置LayoutManager所能滚动的方向同时,我们设置了两个值mScrollType和mPreScrollType,过了英语四级的你,应该明白它们分别是当前的滚动类型和上一次的滚动类型。
mScrollType的作用是当它赋了值不等于-1的时候, ACTION_MOVE事件的代码就不会执行了,为的就是用户手指不松开的时候,不会再去改变canScrollVertically和canScrollHorizontally的值。而ACTION_UP事件,当用户松开手指,就把mScrollType还原回-1。
那么为啥还有一个mPreScrollType,那是因为滑动还有一个惯性的fling操作。fling的时候,我们的手指已经是松开了,mScrollType已经还原为-1,我们要根据mPreScrollType来禁调水平的fling还是垂直的fling。
@Override
public boolean fling(int velocityX, int velocityY) {
switch (mPreScrollType){
case RecyclerView.HORIZONTAL:
velocityY = 0;
break;
case RecyclerView.VERTICAL:
velocityX = 0;
break;
}
return super.fling(velocityX, velocityY);
}
我们怎么禁掉对应的fling呢,代码很明显了,只要把对应的速度设置为0就OK了。