欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?ScrollView實現(xiàn)滾動超過邊界松手回彈

 更新時間:2022年04月19日 08:32:41   作者:qluka  
這篇文章主要為大家詳細(xì)介紹了Android?ScrollView實現(xiàn)滾動超過邊界松手回彈,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

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;

先判斷手指的移動距離,超過了移動的默認(rèn)距離,認(rèn)為是處于mIsBeingDragged狀態(tài),然后調(diào)用overScrollBy()函數(shù),這個方法是實現(xiàn)滾動的關(guān)鍵。并且該方法有個參數(shù)傳遞的是mOverscrollDistance,通過名字可以知道是超過滾動距離,猜測這個是預(yù)留的實現(xiàn)超過滾動邊界的變量。
進(jìn)入該方法看一下

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軸方向的偏移??梢钥吹絥ewScrollY的范圍,top是-maxOverScrollY,bottom是maxOverScrollY + scrollRangeY,其中scrollRangeY是mScrollY的范圍值,maxOverScrollY是超過邊界的范圍值。如果newScrollY的值小于top或者大于bottom,會對該值進(jìn)行調(diào)整。
再進(jìn)入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)。如果沒有滾動動畫,則直接調(diào)用scrollTo到新的滑動到的mScrollY。再經(jīng)過繪制之后,就能看到界面滾動。
再回看overScrollBy()方法中,如果偏移距離到-maxOverScrollY與0之間,則是滑動超過上面邊界;如果偏移在scrollRangeY與maxOverScrollY + scrollRangeY之間,則是滑動超過下面邊界。
通過上面的分析可知,maxOverScrollY參數(shù)是預(yù)留的超過邊界的滑動距離,看一下傳遞過來的實參為成員變量mOverscrollDistance,改動一下該值應(yīng)該就可以實現(xiàn)超過邊界滑動了。但是發(fā)現(xiàn)成員變量為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
? ? }
}

這樣修改可以實現(xiàn)滑動超過邊界,不過有個問題,就是有時候松手了不能彈回,卡在超過邊界那了。需要看看手指抬起的代碼處理,經(jīng)過代碼調(diào)試發(fā)現(xiàn)問題出在手指抬起的下列代碼了

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;

假如現(xiàn)在手指向下滑動超過邊界的時候,計算出來的速度initialVelocity是正數(shù),取個負(fù)然后傳到方法flingWithNestedDispatch()函數(shù)

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,導(dǎo)致后續(xù)的操作都不做了。這個時候,在界面上表現(xiàn)得就是卡在那里不動了。
超過邊界不彈回,這個問題怎么解決?經(jīng)過調(diào)試,找到以下方法,見代碼:

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中最后,手指抬起的時候,加上一道判斷,如果這個時候是超過邊界的狀態(tài),彈回邊界。這樣基本上,可以解決問題。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論