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进行设置:

[java]  view plain  copy
  1. public void setScrollY(int value) {  
  2.     scrollTo(mScrollX, value);  
  3. }  
  4. public void scrollTo(int x, int y) {  
  5.     if (mScrollX != x || mScrollY != y) {  
  6.         int oldX = mScrollX;  
  7.         int oldY = mScrollY;  
  8.         mScrollX = x;  
  9.         mScrollY = y;  
  10.         invalidateParentCaches();  
  11.         onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
  12.         if (!awakenScrollBars()) {  
  13.             invalidate(true);  
  14.         }  
  15.     }  
  16. }  
那分别来看看ScrollView和ListView中都调这两个函数做了什么。

(3)、ScrollVIew中的ScrollY的设置

由于ScrollView直接派生自FrameLayout,所以我们直接看ScrollView.java中做了什么就可以了。
首先,看setScrollY(int value)的用处,木有地方调。
好吧,那我们再来看看ScrollTo(int x,int y)用到的地方。
在OnTouchEvent()中,最关键的一句应该在这里:

[java]  view plain  copy
  1. @Override  
  2. protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  3.     super.onLayout(changed, l, t, r, b);  
  4.     mIsLayoutDirty = false;  
  5.     // Give a child focus if it needs it  
  6.     if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {  
  7.         scrollToChild(mChildToScrollTo);  
  8.     }  
  9.     mChildToScrollTo = null;  
  10.   
  11.     // Calling this with the present values causes it to re-clam them  
  12.     scrollTo(mScrollX, mScrollY);  
  13. }  

在每一次重绘时,都会调用scrollTo()重新设置mScrollY的值,所以,当我们在获取时,是可以获取到的。
那再来看看Listview中,是如何做的:
木有setScrollY(int value)和ScrollTo(int x,int y)的调用。貌似理解了为什么getScrollY()没值的原因了。
但为什么在上篇中重写OverScrollBy()时scrollY是有值的呢?那我们再找找overScrollBy()的整个调用流程:
首先在OnTouchEvent()的ACTION_MOVE中:(在AbsListView.java中)

[java]  view plain  copy
  1. case MotionEvent.ACTION_MOVE: {  
  2.     …………  
  3.     final int y = (int) ev.getY(pointerIndex);  
  4.     switch (mTouchMode) {  
  5.     case TOUCH_MODE_DOWN:  
  6.     case TOUCH_MODE_TAP:  
  7.     case TOUCH_MODE_DONE_WAITING:  
  8.         // Check if we have moved far enough that it looks more like a  
  9.         // scroll than a tap  
  10.         startScrollIfNeeded(y);  
  11.         break;  
  12.     case TOUCH_MODE_SCROLL:  
  13.     case TOUCH_MODE_OVERSCROLL:  
  14.         scrollIfNeeded(y);  
  15.         break;  
  16.     }  
  17.     break;  
  18. }  
在MotionEvent.ACTION_MOVE中会走到scrollIfNeeded(y);中(在AbsListView.java中)
[java]  view plain  copy
  1. private void scrollIfNeeded(int y) {  
  2.     final int rawDeltaY = y - mMotionY;  
  3.     final int deltaY = rawDeltaY - mMotionCorrection;  
  4.     int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;  
  5.   
  6.     if (mTouchMode == TOUCH_MODE_SCROLL) {  
  7.         if (y != mLastY) {  
  8.             ………………  
  9.             if (motionView != null) {  
  10.                 // Check if the top of the motion view is where it is  
  11.                 // supposed to be  
  12.                 final int motionViewRealTop = motionView.getTop();  
  13.                 if (atEdge) {  
  14.                     // Apply overscroll  
  15.                     int overscroll = -incrementalDeltaY -  
  16.                             (motionViewRealTop - motionViewPrevTop);  
  17.                     overScrollBy(0, overscroll, 0, mScrollY, 00,  
  18.                             0, mOverscrollDistance, true);  
  19.                     …………  
  20.                 }  
  21.                 mMotionY = y;  
  22.                 invalidate();  
  23.             }  
  24.             mLastY = y;  
  25.         }  
  26.     } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {  
  27.         if (y != mLastY) {  
  28.               
  29.             …………  
  30.             if (overScrollDistance != 0) {  
  31.                 overScrollBy(0, overScrollDistance, 0, mScrollY, 00,  
  32.                         0, mOverscrollDistance, true);  
  33.                 …………  
  34.                 }  
  35.             }  
  36.             …………  
  37.         }  
  38.     }  
  39. }  
从上面的源码中可以看到,正常滑动和OVERSCROLL时,都会判断当前是不是已经超出了边界,进而调用overScrollBy(),然后再看看OverScrollBy()中又做了什么;
在View.java中:
[java]  view plain  copy
  1. /** 
  2. * Scroll the view with standard behavior for scrolling beyond the normal 
  3. * content boundaries. Views that call this method should override 
  4. * {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the 
  5. * results of an over-scroll operation. 
  6. */  
  7. protected boolean overScrollBy(int deltaX, int deltaY,  
  8.        int scrollX, int scrollY,  
  9.        int scrollRangeX, int scrollRangeY,  
  10.        int maxOverScrollX, int maxOverScrollY,  
  11.        boolean isTouchEvent) {  
  12.      
  13.    …………  
  14.    int newScrollX = scrollX + deltaX;  
  15.    // Clamp values if at the limits and record  
  16.    final int left = -maxOverScrollX;  
  17.    final int right = maxOverScrollX + scrollRangeX;  
  18.    final int top = -maxOverScrollY;  
  19.    final int bottom = maxOverScrollY + scrollRangeY;  
  20.   
  21.    boolean clampedX = false;  
  22.    if (newScrollX > right) {  
  23.        newScrollX = right;  
  24.        clampedX = true;  
  25.    } else if (newScrollX < left) {  
  26.        newScrollX = left;  
  27.        clampedX = true;  
  28.    }  
  29.   
  30.    boolean clampedY = false;  
  31.    if (newScrollY > bottom) {  
  32.        newScrollY = bottom;  
  33.        clampedY = true;  
  34.    } else if (newScrollY < top) {  
  35.        newScrollY = top;  
  36.        clampedY = true;  
  37.    }  
  38.   
  39.    onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);  
  40.   
  41.    return clampedX || clampedY;  
  42. }  
可以看到overScrollBy()中并没有做什么,只是计算出当前最新的newScrollX和newScrollY,然后把结果传给onOverScrolled;
而View.java中的onOverScrolled()是一个空方法。从overScrollBy()方法上面的那一坨注释我们也不难看到,需要自己重写onOverScrolled()方法来实现overScorll的滚动。
[java]  view plain  copy
  1. protected void onOverScrolled(int scrollX, int scrollY,  
  2.         boolean clampedX, boolean clampedY) {  
  3.     // Intentionally empty.  
  4. }  
那我们再来看看AbsListView.java中,onOverScrolled都做了些什么吧。
[java]  view plain  copy
  1. @Override  
  2. protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {  
  3.     if (mScrollY != scrollY) {  
  4.         onScrollChanged(mScrollX, scrollY, mScrollX, mScrollY);  
  5.         mScrollY = scrollY;  
  6.         invalidateParentIfNeeded();  
  7.   
  8.         awakenScrollBars();  
  9.     }  
  10. }  
看到了吧,在这里对mScrollY重新进行了赋值。这也就是为什么在overScrollBy()的时候getScrollY()有值,而其它时候getScrollY()全是零的原因。因为只有在overScrollBy()的时候对mScrollY进行了赋值。其它时间都没有进行赋值!!!!

延伸二:ScrollView中重写overScrollBy()实现上、下拉滑动

就在上面对比源码的时候,偶然发现ScrollView竟然也重写onOverScrolled(),源码如下:(ScrollView.java中)
[java]  view plain  copy
  1. @Override  
  2. protected void onOverScrolled(int scrollX, int scrollY,  
  3.         boolean clampedX, boolean clampedY) {  
  4.     // Treat animating scrolls differently; see #computeScroll() for why.  
  5.     if (!mScroller.isFinished()) {  
  6.         mScrollX = scrollX;  
  7.         mScrollY = scrollY;  
  8.         invalidateParentIfNeeded();  
  9.         if (clampedY) {  
  10.             mScroller.springBack(mScrollX, mScrollY, 000, getScrollRange());  
  11.         }  
  12.     } else {  
  13.         super.scrollTo(scrollX, scrollY);  
  14.     }  
  15.     awakenScrollBars();  
  16. }  
上面我们说了overScrollBy()中其实什么都没做,只是计算出当前最新的移动距离,然后把结果传给onOverScrolled()而View自己的onOverScrolled()是个空函数,也就是说如果派生自View的控件要实现OverScrolled的功能,就需要自己重写onOverScrolled()函数,并在其中处理。所以凡是重写了onOverScrolled()的控件都是可以通过重写overScrollBy()来实现上、下拉滑动的!!!!
下面就举个栗子来看下ScrollView中实现overScrollBy()的方法吧。
先看看效果图:
PullScrollView详解(六)——延伸拓展(listview中getScrollY()一直等于0、ScrollView中的overScrollBy)_第1张图片
效果很明显,也没什么好说的,跟 《PullScrollView详解(四)——完全使用listview实现下拉回弹(方法一)》    效果一样,其实实现方法也完全一样。下面就看看代码吧

1、重写ScrollView

代码如下,就是重写overScrollBy()方法,设置里面的maxOverScrollY的值。

[java]  view plain  copy
  1. public class OverScrollView extends ScrollView {  
  2.     //定义最大滚动高度  
  3.     int mContentMaxMoveHeight = 250;  
  4.   
  5.     public OverScrollView(Context context) {  
  6.         super(context);  
  7.     }  
  8.   
  9.     public OverScrollView(Context context, AttributeSet attrs) {  
  10.         super(context, attrs);  
  11.     }  
  12.   
  13.     public OverScrollView(Context context, AttributeSet attrs, int defStyle) {  
  14.         super(context, attrs, defStyle);  
  15.     }  
  16.   
  17.     @Override  
  18.     protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {  
  19.         return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mContentMaxMoveHeight, isTouchEvent);  
  20.     }  
  21. }"color:#660000;">  
  22.   

2、主布局(main.xml)

布局很容易理解,就是在OverScrollView里,放一个TableLayout来填充数据
[html]  view plain  copy
  1. xml version="1.0" encoding="utf-8"?>  
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.              android:layout_width="fill_parent"  
  4.              android:layout_height="fill_parent">  
  5.   
  6.     <com.harvic.OverScrollView.OverScrollView  
  7.             android:id="@+id/scrollview"  
  8.             android:layout_width="match_parent"  
  9.             android:layout_height="match_parent">  
  10.   
  11.         <TableLayout  
  12.                 android:id="@+id/table_layout"  
  13.                 android:layout_width="match_parent"  
  14.                 android:layout_height="wrap_content"/>  
  15.     com.harvic.OverScrollView.OverScrollView>  
  16. FrameLayout><span style="color:#660000;">  
  17. span>  

3、MainActivity.java数据填充

最后就是在MainActivity中对TableLayout进行数据填充,在第二篇和第三篇博客中都有填充的部分,代码都一样,就不再细讲了。
[java]  view plain  copy
  1. public class MainActivity extends Activity {  
  2.   
  3.     private TableLayout mMainLayout;  
  4.   
  5.     @Override  
  6.     public void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.main);  
  9.         mMainLayout = (TableLayout) findViewById(R.id.table_layout);  
  10.         showTable();  
  11.   
  12.     }  
  13.   
  14.     public void showTable() {  
  15.         TableRow.LayoutParams layoutParams = new TableRow.LayoutParams(  
  16.                 TableRow.LayoutParams.MATCH_PARENT,  
  17.                 TableRow.LayoutParams.WRAP_CONTENT);  
  18.         layoutParams.gravity = Gravity.CENTER;  
  19.         layoutParams.leftMargin = 30;  
  20.         layoutParams.bottomMargin = 10;  
  21.         layoutParams.topMargin = 10;  
  22.   
  23.         for (int i = 0; i < 30; i++) {  
  24.             TableRow tableRow = new TableRow(this);  
  25.             TextView textView = new TextView(this);  
  26.             textView.setText("Test pull down scroll view " + i);  
  27.             textView.setTextSize(20);  
  28.             textView.setPadding(15151515);  
  29.   
  30.             tableRow.addView(textView, layoutParams);  
  31.             if (i % 2 != 0) {  
  32.                 tableRow.setBackgroundColor(Color.LTGRAY);  
  33.             } else {  
  34.                 tableRow.setBackgroundColor(Color.WHITE);  
  35.             }  
  36.             final int n = i;  
  37.             tableRow.setOnClickListener(new View.OnClickListener() {  
  38.                 @Override  
  39.                 public void onClick(View v) {  
  40.                     Toast.makeText(getApplicationContext(), "Click item " + n, Toast.LENGTH_SHORT).show();  
  41.                 }  
  42.             });  
  43.   
  44.   
  45.             mMainLayout.addView(tableRow);  
  46.         }  
  47.     }  
  48. }  
好啦 ,到这里代码就结束了。这个系列也就结束了,写了太多内容,一个貌似不复杂的动画没想到会牵涉这么多内容。能努力看完的同学,也都是不容易啊。

你可能感兴趣的:(android,PullScrollView)