您的位置:首页 > 其它

彻底解决监听Scrollview滑动暂停问题

2017-11-21 09:33 519 查看
项目中有个功能要监听Scrollview的滑动暂停状态,百度了一下,都是通过handler机制来比较getScrollY()值来实现,这种方式还是有bug的,在滑动中停止为撒手状态下,或者在底部,头部的时候有监听不到的情况。后来我就想着Scrollview内部有没有滑动停止的标志呢。阅读Scrollview源码之后发现还真有!!!

这个滑动事件肯定和onTouchEvent(MotionEvent ev)有关,首先从这个方法开始读源码

@Override
public boolean onTouchEvent(MotionEvent ev) {
initVelocityTrackerIfNotExists();

MotionEvent vtev = MotionEvent.obtain(ev);

final int actionMasked = ev.getActionMasked();

if (actionMasked == MotionEvent.ACTION_DOWN) {
mNestedYOffset = 0;
}
vtev.offsetLocation(0, mNestedYOffset);

switch (actionMasked) {
....//省略无关代码
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
//计算撒手时的速度
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
//如果上面计算的速度大于设备能识别的最先速度,执行撒手后的惯性滑动
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
//这里就是执行惯性滑动的方法,接下来就是阅读他的源码了
flingWithNestedDispatch(-initialVelocity);
} else if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0,
getScrollRange())) {
postInvalidateOnAnimation();
}

mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
postInvalidateOnAnimation();
}
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
mLastMotionY = (int) ev.getY(index);
mActivePointerId = ev.getPointerId(index);
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}

if (mVelocityTracker != null) {
mVelocityTracker.addMovement(vtev);
}
vtev.recycle();
return true;
}


通过上面源码的阅读,我们找到了撒手后惯性滑动的方法 flingWithNestedDisp
c099
atch(-initialVelocity);
,所以接下来就是分析这个方法

private void flingWithNestedDispatch(int velocityY) {
final boolean canFling = (mScrollY > 0 || velocityY > 0) &&
(mScrollY < getScrollRange() || velocityY < 0);
if (!dispatchNestedPreFling(0, velocityY)) {
dispatchNestedFling(0, velocityY, canFling);
//如果可以惯性滑动,那么就根据上面算出的撒手时的速度进行惯性滑动
if (canFling) {
fling(velocityY);
}
}
}


接下来就是要阅读fling(velocityY);

public void fling(int velocityY) {
if (getChildCount() > 0) {
int height = getHeight() - mPaddingBottom - mPaddingTop;
int bottom = getChildAt(0).getHeight();
//在这里滑动操作又交给了mScroller去执行
mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,
Math.max(0, bottom - height), 0, height/2);

if (mFlingStrictSpan == null) {
mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling");
}

postInvalidateOnAnimation();
}
}


接下来就是阅读 mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,Math.max(0, bottom - height), 0, height/2);就要揭开真相了

public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY, int overX, int overY) {
//当时看到这个isFinished()方法时,就想着滑动停止标识就是你了
if (mFlywheel && !isFinished()) {
float oldVelocityX = mScrollerX.mCurrVelocity;
float oldVelocityY = mScrollerY.mCurrVelocity;
if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
Math.signum(velocityY) == Math.signum(oldVelocityY)) {
velocityX += oldVelocityX;
velocityY += oldVelocityY;
}
}

mMode = FLING_MODE;
mScrollerX.fling(startX, velocityX, minX, maxX, overX);
mScrollerY.fling(startY, velocityY, minY, maxY, overY);
}


再来看看isFinished()

/**
*
* Returns whether the scroller has finished scrolling.
*
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
return mScrollerX.mFinished && mScrollerY.mFinished;
}


看官方注解就知道了,这个方法就可以判断滑动停止了没有。

接下来就是怎么获取这个方法的返回值了。

public class OverScroller {

public final boolean isFinished() {
return mScrollerX.mFinished && mScrollerY.mFinished;
}
}


isFinished()是OverScroller类里的公共方法,Scrollview里有一个OverScroller私有成员变量,并且没有暴露出isFinished()的调用,就是从Scrollview里没有直接获得滑动停止标志的方法,那么只能用反射来获取了。

public class MyScrollView extends ScrollView {

public boolean isfinishScroll() {
boolean isfinish=false;
Class scrollview=ScrollView.class;
try {
//获取Scrollview里的OverScroller这个字段
Field scrollField=scrollview.getDeclaredField("mScroller");
scrollField.setAccessible(true);
//获取到Scrollview里OverScroller的成员变量值
Object scroller=scrollField.get(this);
//获取scroller的类类型
Class overscroller= scrollField.getType();
//获取到OverScroller中isFinished()方法
Method finishField=overscroller.getMethod("isFinished");
finishField.setAccessible(true);
//调用isFinished()方法
isfinish= (boolean) finishField.invoke(scroller);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {

} catch (InvocationTargetException e) {
e.printStackTrace();
}
return isfinish;
}
}


最后贴出源码吧

public class MyScrollView extends ScrollView {

public MyScrollView(Context context) {
super(context);
}

public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);

}

@Override
public void computeScroll() {
super.computeScroll();
Log.v("scrollveiwj",""+isfinishScroll());
}

public boolean isfinishScroll() {

boolean isfinish=false;
Class scrollview=ScrollView.class;
try {
Field scrollField=scrollview.getDeclaredField("mScroller");
scrollField.setAccessible(true);
Object scroller=scrollField.get(this);
Class overscroller= scrollField.getType();
Method finishField=overscroller.getMethod("isFinished");
finishField.setAccessible(true);
isfinish= (boolean) finishField.invoke(scroller);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {

} catch (InvocationTargetException e) {
e.printStackTrace();
}

return isfinish;

}

@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(ev);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐