<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
ScrollView捲動超過邊界,鬆手回彈
Android原生的ScrollView滑動到邊界之後,就不能再滑動了,感覺很生硬。不及再多滑動一段距離,鬆手後回彈這種效果順滑一些。
先檢視下捲動裡面程式碼的處理
case MotionEvent.ACTION_MOVE: final int activePointerIndex = ev.findPointerIndex(mActivePointerId); if (activePointerIndex == -1) { Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); break; } final int y = (int) ev.getY(activePointerIndex); int deltaY = mLastMotionY - y; ……………………………… if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } mIsBeingDragged = true; if (deltaY > 0) { deltaY -= mTouchSlop; } else { deltaY += mTouchSlop; } } if (mIsBeingDragged) { // Scroll to follow the motion event mLastMotionY = y - mScrollOffset[1]; final int oldY = mScrollY; final int range = getScrollRange(); final int overscrollMode = getOverScrollMode(); boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); // Calling overScrollBy will call onOverScrolled, which // calls onScrollChanged if applicable. if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true) && !hasNestedScrollingParent()) { // Break our velocity if we hit a scroll barrier. mVelocityTracker.clear(); } ……………………………… } break;
先判斷手指的移動距離,超過了移動的預設距離,認為是處於mIsBeingDragged狀態,然後呼叫overScrollBy()函數,這個方法是實現捲動的關鍵。並且該方法有個引數傳遞的是mOverscrollDistance,通過名字可以知道是超過捲動距離,猜測這個是預留的實現超過捲動邊界的變數。
進入該方法看一下
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { final int overScrollMode = mOverScrollMode; final boolean canScrollHorizontal = computeHorizontalScrollRange() > computeHorizontalScrollExtent(); final boolean canScrollVertical = computeVerticalScrollRange() > computeVerticalScrollExtent(); final boolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS || (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal); final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS || (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical); int newScrollX = scrollX + deltaX; if (!overScrollHorizontal) { maxOverScrollX = 0; } int newScrollY = scrollY + deltaY; if (!overScrollVertical) { maxOverScrollY = 0; } // Clamp values if at the limits and record final int left = -maxOverScrollX; final int right = maxOverScrollX + scrollRangeX; final int top = -maxOverScrollY; final int bottom = maxOverScrollY + scrollRangeY; boolean clampedX = false; if (newScrollX > right) { newScrollX = right; clampedX = true; } else if (newScrollX < left) { newScrollX = left; clampedX = true; } boolean clampedY = false; if (newScrollY > bottom) { newScrollY = bottom; clampedY = true; } else if (newScrollY < top) { newScrollY = top; clampedY = true; } onOverScrolled(newScrollX, newScrollY, clampedX, clampedY); return clampedX || clampedY; }
ScrollView主要是豎直方向的捲動,主要看其Y軸方向的偏移。可以看到newScrollY的範圍,top是-maxOverScrollY,bottom是maxOverScrollY + scrollRangeY,其中scrollRangeY是mScrollY的範圍值,maxOverScrollY是超過邊界的範圍值。如果newScrollY的值小於top或者大於bottom,會對該值進行調整。
再進入onOverScrolled()方法看看,
@Override protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { // Treat animating scrolls differently; see #computeScroll() for why. if (!mScroller.isFinished()) { final int oldX = mScrollX; final int oldY = mScrollY; mScrollX = scrollX; mScrollY = scrollY; invalidateParentIfNeeded(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (clampedY) { mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange()); } } else { super.scrollTo(scrollX, scrollY); } awakenScrollBars(); }
如果mScroller.isFinished()為false,說明正在捲動動畫中(包括fling和springBack)。如果沒有捲動動畫,則直接呼叫scrollTo到新的滑動到的mScrollY。再經過繪製之後,就能看到介面捲動。
再回看overScrollBy()方法中,如果偏移距離到-maxOverScrollY與0之間,則是滑動超過上面邊界;如果偏移在scrollRangeY與maxOverScrollY + scrollRangeY之間,則是滑動超過下面邊界。
通過上面的分析可知,maxOverScrollY引數是預留的超過邊界的滑動距離,看一下傳遞過來的實參為成員變數mOverscrollDistance,改動一下該值應該就可以實現超過邊界滑動了。但是發現成員變數為private,並且也沒提供修改的方法,所以改變該變數的值可以通過反射修改。
下面為修改
class OverScrollDisScrollView(cont: Context, attrs: AttributeSet?): ScrollView(cont, attrs) { val tag = "OverScrollDisScrollView" private val overScrollDistance = 500 constructor(cont: Context): this(cont, null) init { val sClass = ScrollView::class.java var field: Field? = null try { field = sClass.getDeclaredField("mOverscrollDistance") field.isAccessible = true field.set(this, overScrollDistance) } catch (e: NoSuchFieldException) { e.printStackTrace() } catch (e: IllegalAccessException) { e.printStackTrace() } overScrollMode = OVER_SCROLL_ALWAYS } }
這樣修改可以實現滑動超過邊界,不過有個問題,就是有時候鬆手了不能彈回,卡在超過邊界那了。需要看看手指擡起的程式碼處理,經過程式碼偵錯發現問題出在手指擡起的下列程式碼了
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;
假如現在手指向下滑動超過邊界的時候,計算出來的速度initialVelocity是正數,取個負然後傳到方法flingWithNestedDispatch()函數
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); } } }
這個時候mScrollY小於0,velocityY小於0,所以canFling為false,導致後續的操作都不做了。這個時候,在介面上表現得就是卡在那裡不動了。
超過邊界不彈回,這個問題怎麼解決?經過偵錯,找到以下方法,見程式碼:
class OverScrollDisScrollView(cont: Context, attrs: AttributeSet?): ScrollView(cont, attrs) { val tag = "OverScrollDisScrollView" private val overScrollDistance = 500 constructor(cont: Context): this(cont, null) init { val sClass = ScrollView::class.java var field: Field? = null try { field = sClass.getDeclaredField("mOverscrollDistance") field.isAccessible = true field.set(this, overScrollDistance) } catch (e: NoSuchFieldException) { e.printStackTrace() } catch (e: IllegalAccessException) { e.printStackTrace() } overScrollMode = OVER_SCROLL_ALWAYS } // override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) { // super.onOverScrolled(scrollX, scrollY, clampedX, clampedY) // } override fun onTouchEvent(ev: MotionEvent?): Boolean { super.onTouchEvent(ev) if (ev != null) { when(ev.action) { MotionEvent.ACTION_UP -> { val yDown = getYDownScrollRange() //解決超過邊界鬆手不回彈得問題 if (mScrollY < 0) { scrollTo(0, 0) // onOverScrolled(0, 0, false, false) } else if (mScrollY > yDown) { scrollTo(0, yDown) // onOverScrolled(0, yDown, false, false) } } } } return true } private fun getYDownScrollRange(): Int { var scrollRange = 0 if (childCount > 0) { val child = getChildAt(0) scrollRange = Math.max( 0, child.height - (height - mPaddingBottom - mPaddingTop) ) } return scrollRange } }
在onTouchEvent中最後,手指擡起的時候,加上一道判斷,如果這個時候是超過邊界的狀態,彈回邊界。這樣基本上,可以解決問題。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45