概述
项目中有个功能要监听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;
}
通过上面源码的阅读,我们找到了撒手后惯性滑动的方法 flingWithNestedDispatch(-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);
}
}
最后
以上就是务实画笔为你收集整理的彻底解决监听Scrollview滑动暂停问题的全部内容,希望文章能够帮你解决彻底解决监听Scrollview滑动暂停问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复