彻底解决监听Scrollview滑动暂停问题
2017-11-21 09:33
519 查看
项目中有个功能要监听Scrollview的滑动暂停状态,百度了一下,都是通过handler机制来比较getScrollY()值来实现,这种方式还是有bug的,在滑动中停止为撒手状态下,或者在底部,头部的时候有监听不到的情况。后来我就想着Scrollview内部有没有滑动停止的标志呢。阅读Scrollview源码之后发现还真有!!!
这个滑动事件肯定和onTouchEvent(MotionEvent ev)有关,首先从这个方法开始读源码
通过上面源码的阅读,我们找到了撒手后惯性滑动的方法 flingWithNestedDisp
c099
atch(-initialVelocity);,所以接下来就是分析这个方法
接下来就是要阅读fling(velocityY);
接下来就是阅读 mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,Math.max(0, bottom - height), 0, height/2);就要揭开真相了
再来看看isFinished()
看官方注解就知道了,这个方法就可以判断滑动停止了没有。
接下来就是怎么获取这个方法的返回值了。
isFinished()是OverScroller类里的公共方法,Scrollview里有一个OverScroller私有成员变量,并且没有暴露出isFinished()的调用,就是从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); } }
相关文章推荐
- 监听滑动事件和消除与内嵌recyclerview冲突的Scrollview(惯性消失问题解决)
- 【Android 手势冲突】彻底解决RecyclerView与ScrollView滑动冲突问题,并实现RecyclerView悬停导航栏(附demo)
- 解决ScrollView嵌套RecyclerView 滑动卡顿和嵌套多个RecyclerView 显示不全的问题
- 解决ScrollView嵌套RecyclerView 滑动卡顿和嵌套多个RecyclerView 显示不全的问题
- iOS 解决NSTimer在UITableView滑动中暂停问题
- 解决ScrollView与ReclerView的滑动冲突问题
- 解决VerticalViewPager中嵌套ScrollView滑动问题
- 关于ScrollView中嵌套listview焦点滑动问题 解决
- 关于iOS页面中scrollview中嵌入百度地图滑动冲突问题解决方法
- 解决ScrollView自动滑动到底部的问题
- 解决ListView在ScrollView中滑动冲突的问题
- Android之解决scrollview总是优先滑动,导致在scrollview内的控件不滑动问题
- 解决ScrollView嵌套ViewPager出现的滑动冲突问题
- 【Android界面实现】解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题
- android开发问题(二)解决ScrollView加入后无法执行OnGestureListener的监听函数
- Android Scrollview嵌套RecyclerView导致滑动卡顿问题解决
- Android编程开发ScrollView中ViewPager无法正常滑动问题解决方法
- 对于 imagView 设置监听与 PAPER 左右滑动切换 VIEW 结合 会导致get不到TAB切换问题解决
- Android之 如何解决ScrollView 和ListView滑动冲突的问题如何解决ScrollView can host only one direct child
- 完美解决ScrollView嵌套ViewPager滑动失效和无法正常滑动冲突问题