最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)文章

  • Android遞歸方式刪除某文件夾下的所有文件(.mp3文件等等)

    Android遞歸方式刪除某文件夾下的所有文件(.mp3文件等等)

    以刪除為例,當(dāng)然,對于遍歷某文件夾下的所有文件均可用這個方法。如搜索.mp3文件等,具體實現(xiàn)如下,感興趣的朋友可以參考下哈
    2013-06-06
  • 淺析Android中的visibility屬性

    淺析Android中的visibility屬性

    以下是對Android中的visibility屬性進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-08-08
  • Android入門之SubMenu的實現(xiàn)詳解

    Android入門之SubMenu的實現(xiàn)詳解

    這篇文章主要為大家詳細(xì)介紹了Android如何實現(xiàn)SubMenu子菜單的效果,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Android有一定的幫助,感興趣的可以了解一下
    2022-11-11
  • Android利用RenderScript實現(xiàn)毛玻璃模糊效果示例

    Android利用RenderScript實現(xiàn)毛玻璃模糊效果示例

    毛玻璃效果(亦稱磨砂效果),近兩年在移動端的UI設(shè)計上越來越流行,下面這篇文章主要介紹了Android利用RenderScript實現(xiàn)毛玻璃模糊效果的相關(guān)資料,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。
    2017-03-03
  • Android常見XML轉(zhuǎn)義字符(總結(jié))

    Android常見XML轉(zhuǎn)義字符(總結(jié))

    下面小編就為大家?guī)硪黄狝ndroid常見XML轉(zhuǎn)義字符(總結(jié))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Android模擬器中安裝apk的方法

    Android模擬器中安裝apk的方法

    這篇文章主要介紹了Android模擬器中安裝apk的方法,結(jié)合圖文描述講解了Android的模擬器中實現(xiàn)安裝apk的具體步驟,簡單實用,需要的朋友可以參考下
    2015-12-12
  • Android自定義鍵盤的實現(xiàn)(數(shù)字鍵盤和字母鍵盤)

    Android自定義鍵盤的實現(xiàn)(數(shù)字鍵盤和字母鍵盤)

    本篇文章主要介紹了Android自定義鍵盤的實現(xiàn)(數(shù)字鍵盤和字母鍵盤),具有一定的參考價值,有興趣的可以了解一下
    2017-08-08
  • Android 微信圖片分享功能

    Android 微信圖片分享功能

    本文主要介紹的是,Anroid 分享本地帶二維碼的圖片給微信好友和朋友圈的功能代碼,具有一定的參考價值,希望能幫助到有需要的朋友
    2016-07-07
  • Android深色模式適配適配過程詳解

    Android深色模式適配適配過程詳解

    這篇文章主要介紹了Android深色模式適配適配過程,深色模式其實就是我們經(jīng)常講到的夜間模式,與白天模式我們需要的屏幕高亮不同,晚上更加需要的是柔和的光,有助于減少手機(jī)對眼睛的傷害
    2023-04-04
  • android實現(xiàn)打地鼠游戲

    android實現(xiàn)打地鼠游戲

    這篇文章主要為大家詳細(xì)介紹了android實現(xiàn)打地鼠游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-04-04

最新評論

绥中县| 巴东县| 错那县| 晋宁县| 满洲里市| 巴塘县| 临夏县| 恩施市| 济宁市| 阿克陶县| 宿迁市| 铅山县| 弋阳县| 于都县| 昌邑市| 余江县| 拉孜县| 乃东县| 始兴县| 肇庆市| 松滋市| 宾阳县| 濉溪县| 巴彦淖尔市| 卓资县| 淮滨县| 淳安县| 平顺县| 神农架林区| 郧西县| 榆中县| 隆回县| 鸡泽县| 怀集县| 尼勒克县| 新余市| 和田县| 泗水县| 琼海市| 伊宁县| 盐城市|