PullScrollView詳解(六)——延伸拓展(listview中getScrollY()一直等於0、ScrollView中的overScrollBy),listview與scrollview
前言:經常說follow your heart。但等到真到這麼一天的時候,卻很艱難
相關文章:
1、《PullScrollView詳解(一)——自訂控制項屬性》
2、《PullScrollView詳解(二)——Animation、Layout與下拉回彈》
3、《PullScrollView詳解(三)——PullScrollView實現》
4、《PullScrollView詳解(四)——完全使用listview實現下拉回彈(方法一)》
5、《PullScrollView詳解(五)——完全使用listview實現下拉回彈(方法二)》
6、《PullScrollView詳解(六)——延伸拓展(listview中getScrollY()一直等於0、ScrollView中的overScrollBy)》
延伸一:為什麼PullScrollView中getScrollY()有值而ListView中的getScrollY()卻一直為零通過查源碼,你會發現getScrollY()是View的一個方法。那ScrollView為什麼getScrollY()有值呢?
讓我們仔細分析一下源碼:
(1)、先看派生
ScrollView->FrameLayout->ViewGroup->View
ListView->AbsListView->AdapterView->ViewGroup->View
從上面的派生中都可以看出,全部都是派生自View,而且全部都沒有對getScrollY()方法重寫。那就奇怪了,大家都沒有對它進行重寫。那肯定是設定的問題了。
(2)、設定scrollY的區別
在View.java中,有兩個函數能對ScrollY進行設定:
public void setScrollY(int value) { scrollTo(mScrollX, value);}public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { invalidate(true); } }}那分別來看看ScrollView和ListView中都調這兩個函數做了什麼。
(3)、ScrollVIew中的ScrollY的設定
由於ScrollView直接派生自FrameLayout,所以我們直接看ScrollView.java中做了什麼就可以了。
首先,看setScrollY(int value)的用處,木有地方調。
好吧,那我們再來看看ScrollTo(int x,int y)用到的地方。
在OnTouchEvent()中,最關鍵的一句應該在這裡:
@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mIsLayoutDirty = false; // Give a child focus if it needs it if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { scrollToChild(mChildToScrollTo); } mChildToScrollTo = null; // Calling this with the present values causes it to re-clam them scrollTo(mScrollX, mScrollY);}
在每一次重繪時,都會調用scrollTo()重新設定mScrollY的值,所以,當我們在擷取時,是可以擷取到的。
那再來看看Listview中,是如何做的:
木有setScrollY(int value)和ScrollTo(int x,int y)的調用。貌似理解了為什麼getScrollY()沒值的原因了。
但為什麼在上篇中重寫OverScrollBy()時scrollY是有值的呢?那我們再找找overScrollBy()的整個調用流程:
首先在OnTouchEvent()的ACTION_MOVE中:(在AbsListView.java中)
case MotionEvent.ACTION_MOVE: {………… final int y = (int) ev.getY(pointerIndex); switch (mTouchMode) { case TOUCH_MODE_DOWN: case TOUCH_MODE_TAP: case TOUCH_MODE_DONE_WAITING: // Check if we have moved far enough that it looks more like a // scroll than a tap startScrollIfNeeded(y); break; case TOUCH_MODE_SCROLL: case TOUCH_MODE_OVERSCROLL: scrollIfNeeded(y); break; } break;}在MotionEvent.ACTION_MOVE中會走到scrollIfNeeded(y);中(在AbsListView.java中)
private void scrollIfNeeded(int y) { final int rawDeltaY = y - mMotionY; final int deltaY = rawDeltaY - mMotionCorrection; int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; if (mTouchMode == TOUCH_MODE_SCROLL) { if (y != mLastY) { ……………… if (motionView != null) { // Check if the top of the motion view is where it is // supposed to be final int motionViewRealTop = motionView.getTop(); if (atEdge) { // Apply overscroll int overscroll = -incrementalDeltaY - (motionViewRealTop - motionViewPrevTop); overScrollBy(0, overscroll, 0, mScrollY, 0, 0, 0, mOverscrollDistance, true); ………… } mMotionY = y; invalidate(); } mLastY = y; } } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) { if (y != mLastY) { ………… if (overScrollDistance != 0) { overScrollBy(0, overScrollDistance, 0, mScrollY, 0, 0, 0, mOverscrollDistance, true); ………… } }………… } }}從上面的源碼中可以看到,正常滑動和OVERSCROLL時,都會判斷當前是不是已經超出了邊界,進而調用overScrollBy(),然後再看看OverScrollBy()中又做了什麼;
在View.java中:
/*** Scroll the view with standard behavior for scrolling beyond the normal* content boundaries. Views that call this method should override* {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the* results of an over-scroll operation.*/protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { ………… int newScrollX = scrollX + deltaX; // 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;}可以看到overScrollBy()中並沒有做什麼,只是計算出當前最新的newScrollX和newScrollY,然後把結果傳給onOverScrolled;
而View.java中的onOverScrolled()是一個空方法。從overScrollBy()方法上面的那一坨注釋我們也不難看到,需要自己重寫onOverScrolled()方法來實現overScorll的滾動。
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { // Intentionally empty.}那我們再來看看AbsListView.java中,onOverScrolled都做了些什麼吧。
@Overrideprotected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { if (mScrollY != scrollY) { onScrollChanged(mScrollX, scrollY, mScrollX, mScrollY); mScrollY = scrollY; invalidateParentIfNeeded(); awakenScrollBars(); }}看到了吧,在這裡對mScrollY重新進行了賦值。這也就是為什麼在overScrollBy()的時候getScrollY()有值,而其它時候getScrollY()全是零的原因。因為只有在overScrollBy()的時候對mScrollY進行了賦值。其它時間都沒有進行賦值!!!!
延伸二:ScrollView中重寫overScrollBy()實現上、下拉滑動就在上面對比源碼的時候,偶然發現ScrollView竟然也重寫onOverScrolled(),源碼如下:(ScrollView.java中)
@Overrideprotected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { // Treat animating scrolls differently; see #computeScroll() for why. if (!mScroller.isFinished()) { mScrollX = scrollX; mScrollY = scrollY; invalidateParentIfNeeded(); if (clampedY) { mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange()); } } else { super.scrollTo(scrollX, scrollY); } awakenScrollBars();}上面我們說了overScrollBy()中其實什麼都沒做,只是計算出當前最新的移動距離,然後把結果傳給onOverScrolled()而View自己的onOverScrolled()是個空函數,也就是說如果派生自View的控制項要實現OverScrolled的功能,就需要自己重寫onOverScrolled()函數,並在其中處理。所以凡是重寫了onOverScrolled()的控制項都是可以通過重寫overScrollBy()來實現上、下拉滑動的!!!!
下面就舉個栗子來看下ScrollView中實現overScrollBy()的方法吧。
先看看:
效果很明顯,也沒什麼好說的,跟《PullScrollView詳解(四)——完全使用listview實現下拉回彈(方法一)》 效果一樣,其實實現方法也完全一樣。下面就看看代碼吧
1、重寫ScrollView代碼如下,就是重寫overScrollBy()方法,設定裡面的maxOverScrollY的值。
public class OverScrollView extends ScrollView { //定義最大滾動高度 int mContentMaxMoveHeight = 250; public OverScrollView(Context context) { super(context); } public OverScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public OverScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mContentMaxMoveHeight, isTouchEvent); }}2、主布局(main.xml)布局很容易理解,就是在OverScrollView裡,放一個TableLayout來填充資料
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.harvic.OverScrollView.OverScrollView android:id="@+id/scrollview" android:layout_width="match_parent" android:layout_height="match_parent"> <TableLayout android:id="@+id/table_layout" android:layout_width="match_parent" android:layout_height="wrap_content"/> </com.harvic.OverScrollView.OverScrollView></FrameLayout>
3、MainActivity.java資料填充最後就是在MainActivity中對TableLayout進行資料填充,在第二篇和第三篇部落格中都有填充的部分,代碼都一樣,就不再細講了。
public class MainActivity extends Activity { private TableLayout mMainLayout; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mMainLayout = (TableLayout) findViewById(R.id.table_layout); showTable(); } public void showTable() { TableRow.LayoutParams layoutParams = new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.CENTER; layoutParams.leftMargin = 30; layoutParams.bottomMargin = 10; layoutParams.topMargin = 10; for (int i = 0; i < 30; i++) { TableRow tableRow = new TableRow(this); TextView textView = new TextView(this); textView.setText("Test pull down scroll view " + i); textView.setTextSize(20); textView.setPadding(15, 15, 15, 15); tableRow.addView(textView, layoutParams); if (i % 2 != 0) { tableRow.setBackgroundColor(Color.LTGRAY); } else { tableRow.setBackgroundColor(Color.WHITE); } final int n = i; tableRow.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "Click item " + n, Toast.LENGTH_SHORT).show(); } }); mMainLayout.addView(tableRow); } }}好啦 ,到這裡代碼就結束了。這個系列也就結束了,寫了太多內容,一個貌似不複雜的動畫沒想到會牽涉這麼多內容。能努力看完的同學,也都是不容易啊。
源碼在文章底部給出
如果本文有幫到你,記得加關注哦
源碼:http://download.csdn.net/detail/harvic880925/9062283
請大家尊重原創者著作權,轉載請標明出處:http://blog.csdn.net/harvic880925/article/details/48092341 謝謝
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。