• Android 自定义ScrollView 支持惯性滑动,惯性回弹效果。支持上拉加载更多


    先讲下原理:

    ScrollView的子View 主要分为3部分:head头部,滚动内容,fooder底部

    我们实现惯性滑动,以及回弹,都是靠超过head或者fooder 就重新滚动到  ,内容的顶部或者底部。

    之前看了Pulltorefresh 他是通过不断改变 head或者 fooder的 pading 值来实现 上拉或者 下拉的效果。感觉有点不流畅,而且层次嵌套得比较多。当然他的好处是扩展性好。

    因工作需求,需要层次嵌套少,对性能要求非常高。因此重新自定义了ViewGroup实现。

    直接上代码:

    1. package com.example.administrator.customscrollview;  
    2.   
    3. import android.content.Context;  
    4. import android.content.res.TypedArray;  
    5. import android.util.AttributeSet;  
    6. import android.util.Log;  
    7. import android.view.Gravity;  
    8. import android.view.MotionEvent;  
    9. import android.view.VelocityTracker;  
    10. import android.view.View;  
    11. import android.view.ViewConfiguration;  
    12. import android.view.ViewGroup;  
    13. import android.widget.OverScroller;  
    14.   
    15. /** 
    16.  * 自定义 pulltorefresh Layout 
    17.  * TODO: ferris 2015年9月11日 18:52:40 
    18.  */  
    19. public class PullTorefreshScrollView extends ViewGroup {  
    20.   
    21.     private FoodeLayout fooder_layout;// top and buttom  
    22.     private View top_layout;  
    23.   
    24.     private int desireWidth, desireHeight;  
    25.     private VelocityTracker velocityTracker;  
    26.     private int mPointerId;  
    27.     private float x, y;  
    28.     private OverScroller mScroller;  
    29.     private int maxFlingVelocity, minFlingVelocity;  
    30.     private int mTouchSlop;  
    31.     protected Boolean isMove = false;  
    32.     protected float downX = 0, downY = 0;  
    33.     private int top_hight = 0;  
    34.     private int scrollYButtom = 0;  
    35.     private int nScrollYButtom = 0;  
    36.   
    37.     private int pullDownMin = 0;  
    38.     private Boolean isEnablePullDown = true;  
    39.   
    40.     private Boolean isFirst=true;  
    41.   
    42.     public void setEnablePullDown(Boolean isEnablePullDown) {  
    43.         this.isEnablePullDown = isEnablePullDown;  
    44.     }  
    45.   
    46.     public PullTorefreshScrollView(Context context) {  
    47.         super(context);  
    48.         init(null, 0);  
    49.     }  
    50.   
    51.     public PullTorefreshScrollView(Context context, AttributeSet attrs) {  
    52.         super(context, attrs);  
    53.         init(attrs, 0);  
    54.     }  
    55.   
    56.     public PullTorefreshScrollView(Context context, AttributeSet attrs, int defStyle) {  
    57.         super(context, attrs, defStyle);  
    58.         init(attrs, defStyle);  
    59.     }  
    60.   
    61.     private void init(AttributeSet attrs, int defStyle) {  
    62.         // Load attributes  
    63. //        final TypedArray a = getContext().obtainStyledAttributes(  
    64. //                attrs, R.styleable.PullTorefreshScrollView, defStyle, 0);  
    65. //  
    66. //  
    67. //        a.recycle();  
    68.         mScroller = new OverScroller(getContext());  
    69.         maxFlingVelocity = ViewConfiguration.get(getContext()).getScaledMaximumFlingVelocity();  
    70.         minFlingVelocity = ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();  
    71.         mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();  
    72.     }  
    73.   
    74.     @Override  
    75.     protected void onFinishInflate() {  
    76.         super.onFinishInflate();  
    77.         fooder_layout = (FoodeLayout) findViewById(R.id.fooder_layout);  
    78.         top_layout = findViewById(R.id.top_layout);  
    79.   
    80.   
    81.         if (isEnablePullDown) {  
    82.             fooder_layout.showFooderPull();  
    83.         } else {  
    84.             fooder_layout.hideFooder();  
    85.         }  
    86.     }  
    87.   
    88.   
    89.     public int getScrollYTop() {  
    90.         return top_hight;  
    91.     }  
    92.   
    93.     public int getScrollYButtom() {  
    94.         return scrollYButtom;  
    95.     }  
    96.   
    97.     public int getNScrollYTop() {  
    98.         return 0;  
    99.     }  
    100.   
    101.     public int getNScrollYButtom() {  
    102.         return nScrollYButtom;  
    103.     }  
    104.   
    105.     public int measureWidth(int widthMeasureSpec) {  
    106.         int result = 0;  
    107.         int measureMode = MeasureSpec.getMode(widthMeasureSpec);  
    108.         int width = MeasureSpec.getSize(widthMeasureSpec);  
    109.         switch (measureMode) {  
    110.             case MeasureSpec.AT_MOST:  
    111.             case MeasureSpec.EXACTLY:  
    112.                 result = width;  
    113.                 break;  
    114.             default:  
    115.                 break;  
    116.         }  
    117.         return result;  
    118.     }  
    119.   
    120.     public int measureHeight(int heightMeasureSpec) {  
    121.         int result = 0;  
    122.         int measureMode = MeasureSpec.getMode(heightMeasureSpec);  
    123.         int height = MeasureSpec.getSize(heightMeasureSpec);  
    124.         switch (measureMode) {  
    125.             case MeasureSpec.AT_MOST:  
    126.             case MeasureSpec.EXACTLY:  
    127.                 result = height;  
    128.                 break;  
    129.             default:  
    130.                 break;  
    131.         }  
    132.         return result;  
    133.     }  
    134.   
    135.     @Override  
    136.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    137.         // 计算所有child view 要占用的空间  
    138.         int width = measureWidth(widthMeasureSpec);  
    139.         int height = measureHeight(heightMeasureSpec);  
    140.   
    141.         desireWidth = 0;  
    142.         desireHeight = 0;  
    143.         int count = getChildCount();  
    144.         for (int i = 0; i < count; ++i) {  
    145.             View v = getChildAt(i);  
    146.   
    147.             if (v.getVisibility() != View.GONE) {  
    148.   
    149.                 LayoutParams lp = (LayoutParams) v.getLayoutParams();  
    150.                 measureChildWithMargins(v, widthMeasureSpec, 0,  
    151.                         heightMeasureSpec, 0);  
    152.   
    153.                 //只是在这里增加了垂直或者水平方向的判断  
    154.                 if (v.getId() == R.id.top_layout) {  
    155.                     top_hight = v.getMeasuredHeight();  
    156.                 }  
    157.                 desireWidth = Math.max(desireWidth, v.getMeasuredWidth()  
    158.                         + lp.leftMargin + lp.rightMargin);  
    159.                 desireHeight += v.getMeasuredHeight() + lp.topMargin  
    160.                         + lp.bottomMargin;  
    161.   
    162.             }  
    163.         }  
    164.   
    165.         // count with padding  
    166.         desireWidth += getPaddingLeft() + getPaddingRight();  
    167.         desireHeight += getPaddingTop() + getPaddingBottom();  
    168.   
    169.         // see if the size is big enough  
    170.         desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());  
    171.         desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());  
    172.   
    173.   
    174.         //处理内容比较少的时候,就添加一定的高度  
    175.         int scrollHight = height + top_hight * 2;  
    176.         if (scrollHight > desireWidth) {  
    177.             int offset = scrollHight - desireHeight;  
    178.             View view = new View(getContext());  
    179.             view.setBackgroundResource(R.color.top_layout_color);  
    180.             LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, offset);  
    181.             addView(view, getChildCount() - 1, lp);  
    182.             desireWidth = scrollHight;  
    183.         }  
    184.   
    185.   
    186.         setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),  
    187.                 resolveSize(desireHeight, heightMeasureSpec));  
    188.   
    189.         scrollYButtom = desireHeight - getMeasuredHeight() - top_hight;  
    190.         nScrollYButtom = desireHeight - getMeasuredHeight();  
    191.         //如果上啦拖出一半的高度,就代表将要执行上啦  
    192.         pullDownMin = nScrollYButtom - top_hight / 2;  
    193.         if(isFirst){  
    194.             scrollTo(0, top_hight);  
    195.             isFirst=false;  
    196.         }  
    197.   
    198.     }  
    199.   
    200.     @Override  
    201.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
    202.         final int parentLeft = getPaddingLeft();  
    203.         final int parentRight = r - l - getPaddingRight();  
    204.         final int parentTop = getPaddingTop();  
    205.         final int parentBottom = b - t - getPaddingBottom();  
    206.   
    207.         if (BuildConfig.DEBUG)  
    208.             Log.d("onlayout", "parentleft: " + parentLeft + "   parenttop: "  
    209.                     + parentTop + "   parentright: " + parentRight  
    210.                     + "   parentbottom: " + parentBottom);  
    211.   
    212.         int left = parentLeft;  
    213.         int top = parentTop;  
    214.   
    215.         int count = getChildCount();  
    216.         for (int i = 0; i < count; ++i) {  
    217.             View v = getChildAt(i);  
    218.             if (v.getVisibility() != View.GONE) {  
    219.                 LayoutParams lp = (LayoutParams) v.getLayoutParams();  
    220.                 final int childWidth = v.getMeasuredWidth();  
    221.                 final int childHeight = v.getMeasuredHeight();  
    222.                 final int gravity = lp.gravity;  
    223.                 final int horizontalGravity = gravity  
    224.                         & Gravity.HORIZONTAL_GRAVITY_MASK;  
    225.                 final int verticalGravity = gravity  
    226.                         & Gravity.VERTICAL_GRAVITY_MASK;  
    227.   
    228.   
    229.                 // layout vertical, and only consider horizontal gravity  
    230.   
    231.                 left = parentLeft;  
    232.                 top += lp.topMargin;  
    233.                 switch (horizontalGravity) {  
    234.                     case Gravity.LEFT:  
    235.                         break;  
    236.                     case Gravity.CENTER_HORIZONTAL:  
    237.                         left = parentLeft  
    238.                                 + (parentRight - parentLeft - childWidth) / 2  
    239.                                 + lp.leftMargin - lp.rightMargin;  
    240.                         break;  
    241.                     case Gravity.RIGHT:  
    242.                         left = parentRight - childWidth - lp.rightMargin;  
    243.                         break;  
    244.                 }  
    245.                 v.layout(left, top, left + childWidth, top + childHeight);  
    246.                 top += childHeight + lp.bottomMargin;  
    247.             }  
    248.   
    249.         }  
    250.   
    251.   
    252.     }  
    253.   
    254.     @Override  
    255.     protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams() {  
    256.         return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,  
    257.                 ViewGroup.LayoutParams.MATCH_PARENT);  
    258.     }  
    259.   
    260.     @Override  
    261.     public android.view.ViewGroup.LayoutParams generateLayoutParams(  
    262.             AttributeSet attrs) {  
    263.         return new LayoutParams(getContext(), attrs);  
    264.     }  
    265.   
    266.     @Override  
    267.     protected android.view.ViewGroup.LayoutParams generateLayoutParams(  
    268.             android.view.ViewGroup.LayoutParams p) {  
    269.         return new LayoutParams(p);  
    270.     }  
    271.   
    272.     public static class LayoutParams extends MarginLayoutParams {  
    273.         public int gravity = -1;  
    274.   
    275.         public LayoutParams(Context c, AttributeSet attrs) {  
    276.             super(c, attrs);  
    277.   
    278.             TypedArray ta = c.obtainStyledAttributes(attrs,  
    279.                     R.styleable.SlideGroup);  
    280.   
    281.             gravity = ta.getInt(R.styleable.SlideGroup_layout_gravity, -1);  
    282.   
    283.             ta.recycle();  
    284.         }  
    285.   
    286.         public LayoutParams(int width, int height) {  
    287.             this(width, height, -1);  
    288.         }  
    289.   
    290.         public LayoutParams(int width, int height, int gravity) {  
    291.             super(width, height);  
    292.             this.gravity = gravity;  
    293.         }  
    294.   
    295.         public LayoutParams(android.view.ViewGroup.LayoutParams source) {  
    296.             super(source);  
    297.         }  
    298.   
    299.         public LayoutParams(MarginLayoutParams source) {  
    300.             super(source);  
    301.         }  
    302.     }  
    303.   
    304.   
    305.     /** 
    306.      * onInterceptTouchEvent()用来询问是否要拦截处理。 onTouchEvent()是用来进行处理。 
    307.      * <p/> 
    308.      * 例如:parentLayout----childLayout----childView 事件的分发流程: 
    309.      * parentLayout::onInterceptTouchEvent()---false?---> 
    310.      * childLayout::onInterceptTouchEvent()---false?---> 
    311.      * childView::onTouchEvent()---false?---> 
    312.      * childLayout::onTouchEvent()---false?---> parentLayout::onTouchEvent() 
    313.      * <p/> 
    314.      * <p/> 
    315.      * <p/> 
    316.      * 如果onInterceptTouchEvent()返回false,且分发的子View的onTouchEvent()中返回true, 
    317.      * 那么onInterceptTouchEvent()将收到所有的后续事件。 
    318.      * <p/> 
    319.      * 如果onInterceptTouchEvent()返回true,原本的target将收到ACTION_CANCEL,该事件 
    320.      * 将会发送给我们自己的onTouchEvent()。 
    321.      */  
    322.     @Override  
    323.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
    324.         final int action = ev.getActionMasked();  
    325.         if (BuildConfig.DEBUG)  
    326.             Log.d("onInterceptTouchEvent", "action: " + action);  
    327.   
    328.         if (action == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {  
    329.             // 该事件可能不是我们的  
    330.             return false;  
    331.         }  
    332.   
    333.         boolean isIntercept = false;  
    334.         switch (action) {  
    335.             case MotionEvent.ACTION_DOWN:  
    336.                 // 如果动画还未结束,则将此事件交给onTouchEvet()处理,  
    337.                 // 否则,先分发给子View  
    338.                 isIntercept = !mScroller.isFinished();  
    339.                 // 如果此时不拦截ACTION_DOWN时间,应该记录下触摸地址及手指id,当我们决定拦截ACTION_MOVE的event时,  
    340.                 // 将会需要这些初始信息(因为我们的onTouchEvent将可能接收不到ACTION_DOWN事件)  
    341.                 mPointerId = ev.getPointerId(0);  
    342. //          if (!isIntercept) {  
    343.                 downX = x = ev.getX();  
    344.                 downY = y = ev.getY();  
    345. //          }  
    346.                 break;  
    347.             case MotionEvent.ACTION_MOVE:  
    348.                 int pointerIndex = ev.findPointerIndex(mPointerId);  
    349.                 if (BuildConfig.DEBUG)  
    350.                     Log.d("onInterceptTouchEvent", "pointerIndex: " + pointerIndex  
    351.                             + ", pointerId: " + mPointerId);  
    352.                 float mx = ev.getX(pointerIndex);  
    353.                 float my = ev.getY(pointerIndex);  
    354.   
    355.                 if (BuildConfig.DEBUG)  
    356.                     Log.d("onInterceptTouchEvent", "action_move [touchSlop: "  
    357.                             + mTouchSlop + ", deltaX: " + (x - mx) + ", deltaY: "  
    358.                             + (y - my) + "]");  
    359.   
    360.                 // 根据方向进行拦截,(其实这样,如果我们的方向是水平的,里面有一个ScrollView,那么我们是支持嵌套的)  
    361.   
    362.                 if (Math.abs(y - my) >= mTouchSlop) {  
    363.                     isIntercept = true;  
    364.                 }  
    365.   
    366.   
    367.                 //如果不拦截的话,我们不会更新位置,这样可以通过累积小的移动距离来判断是否达到可以认为是Move的阈值。  
    368.                 //这里当产生拦截的话,会更新位置(这样相当于损失了mTouchSlop的移动距离,如果不更新,可能会有一点点跳的感觉)  
    369.                 if (isIntercept) {  
    370.                     x = mx;  
    371.                     y = my;  
    372.                 }  
    373.                 break;  
    374.             case MotionEvent.ACTION_CANCEL:  
    375.             case MotionEvent.ACTION_UP:  
    376.                 // 这是触摸的最后一个事件,无论如何都不会拦截  
    377.                 if (velocityTracker != null) {  
    378.                     velocityTracker.recycle();  
    379.                     velocityTracker = null;  
    380.                 }  
    381.                 break;  
    382.             case MotionEvent.ACTION_POINTER_UP:  
    383.                 solvePointerUp(ev);  
    384.   
    385.                 break;  
    386.         }  
    387.         return isIntercept;  
    388.     }  
    389.   
    390.     private void solvePointerUp(MotionEvent event) {  
    391.         // 获取离开屏幕的手指的索引  
    392.         int pointerIndexLeave = event.getActionIndex();  
    393.         int pointerIdLeave = event.getPointerId(pointerIndexLeave);  
    394.         if (mPointerId == pointerIdLeave) {  
    395.             // 离开屏幕的正是目前的有效手指,此处需要重新调整,并且需要重置VelocityTracker  
    396.             int reIndex = pointerIndexLeave == 0 ? 1 : 0;  
    397.             mPointerId = event.getPointerId(reIndex);  
    398.             // 调整触摸位置,防止出现跳动  
    399.             x = event.getX(reIndex);  
    400.             y = event.getY(reIndex);  
    401.             if (velocityTracker != null)  
    402.                 velocityTracker.clear();  
    403.         }  
    404.     }  
    405.   
    406.     @Override  
    407.     public boolean onTouchEvent(MotionEvent event) {  
    408.   
    409.         final int action = event.getActionMasked();  
    410.   
    411.         if (velocityTracker == null) {  
    412.             velocityTracker = VelocityTracker.obtain();  
    413.         }  
    414.         velocityTracker.addMovement(event);  
    415.   
    416.         switch (action) {  
    417.             case MotionEvent.ACTION_DOWN:  
    418.                 // 获取索引为0的手指id  
    419.   
    420.   
    421.                 isMove = false;  
    422.                 mPointerId = event.getPointerId(0);  
    423.                 x = event.getX();  
    424.                 y = event.getY();  
    425.                 if (!mScroller.isFinished())  
    426.                     mScroller.abortAnimation();  
    427.                 break;  
    428.             case MotionEvent.ACTION_MOVE:  
    429.                 isMove = true;  
    430.                 // 获取当前手指id所对应的索引,虽然在ACTION_DOWN的时候,我们默认选取索引为0  
    431.                 // 的手指,但当有第二个手指触摸,并且先前有效的手指up之后,我们会调整有效手指  
    432.   
    433.                 // 屏幕上可能有多个手指,我们需要保证使用的是同一个手指的移动轨迹,  
    434.                 // 因此此处不能使用event.getActionIndex()来获得索引  
    435.                 final int pointerIndex = event.findPointerIndex(mPointerId);  
    436.                 float mx = event.getX(pointerIndex);  
    437.                 float my = event.getY(pointerIndex);  
    438.   
    439.                 moveBy((int) (x - mx), (int) (y - my));  
    440.   
    441.                 x = mx;  
    442.                 y = my;  
    443.                 break;  
    444.             case MotionEvent.ACTION_UP:  
    445.                 isMove = false;  
    446.                 velocityTracker.computeCurrentVelocity(1000, maxFlingVelocity);  
    447.                 float velocityX = velocityTracker.getXVelocity(mPointerId);  
    448.                 float velocityY = velocityTracker.getYVelocity(mPointerId);  
    449.   
    450.                 completeMove(-velocityX, -velocityY);  
    451.                 if (velocityTracker != null) {  
    452.                     velocityTracker.recycle();  
    453.                     velocityTracker = null;  
    454.                 }  
    455.                 break;  
    456.   
    457.             case MotionEvent.ACTION_POINTER_UP:  
    458.                 // 获取离开屏幕的手指的索引  
    459.                 isMove = false;  
    460.                 int pointerIndexLeave = event.getActionIndex();  
    461.                 int pointerIdLeave = event.getPointerId(pointerIndexLeave);  
    462.                 if (mPointerId == pointerIdLeave) {  
    463.                     // 离开屏幕的正是目前的有效手指,此处需要重新调整,并且需要重置VelocityTracker  
    464.                     int reIndex = pointerIndexLeave == 0 ? 1 : 0;  
    465.                     mPointerId = event.getPointerId(reIndex);  
    466.                     // 调整触摸位置,防止出现跳动  
    467.                     x = event.getX(reIndex);  
    468.                     y = event.getY(reIndex);  
    469.                     if (velocityTracker != null)  
    470.                         velocityTracker.clear();  
    471.                 }  
    472.                 break;  
    473.             case MotionEvent.ACTION_CANCEL:  
    474.                 isMove = false;  
    475.                 break;  
    476.         }  
    477.         return true;  
    478.     }  
    479.   
    480.     private Boolean isPull = false;  
    481.   
    482.     //此处的moveBy是根据水平或是垂直排放的方向,  
    483. //来选择是水平移动还是垂直移动  
    484.     public void moveBy(int deltaX, int deltaY) {  
    485.         if (BuildConfig.DEBUG)  
    486.             Log.d("moveBy", "deltaX: " + deltaX + "    deltaY: " + deltaY);  
    487.         if (Math.abs(deltaY) >= Math.abs(deltaX)) {  
    488.             int mScrollY = getScrollY();  
    489.             if (mScrollY <= 0) {  
    490.                 scrollTo(0, 0);  
    491.             } else if (mScrollY >= getNScrollYButtom()) {  
    492.                 scrollTo(0, getNScrollYButtom());  
    493.   
    494.   
    495.             } else {  
    496.                 scrollBy(0, deltaY);  
    497.             }  
    498.   
    499.             if (isEnablePullDown && deltaY > 0 && mScrollY >= pullDownMin) {  
    500.                 isPull = true;  
    501.                 Log.d("onlayout", "isPull: true");  
    502.             }  
    503.         }  
    504.   
    505.   
    506.     }  
    507.   
    508.     private void completeMove(float velocityX, float velocityY) {  
    509.   
    510.         int mScrollY = getScrollY();  
    511.         int maxY = getScrollYButtom();  
    512.         int minY = getScrollYTop();  
    513.   
    514.   
    515.         if (mScrollY >= maxY) {//如果滚动,超过了 下边界,就回弹到下边界  
    516.   
    517.             if (isPull) {//滚动到最底部  
    518.                 mScroller.startScroll(0, mScrollY, 0, getNScrollYButtom() - mScrollY, 300);  
    519.                 invalidate();  
    520.   
    521.                 //显示雷达  
    522.                 fooder_layout.showFooderRadar();  
    523.                 if (pullDownListem != null) {  
    524.                     pullDownListem.onPullDown();  
    525.                 }  
    526.                 Log.d("onlayout", "isPull: true 滚动到最底部,显示出雷达");  
    527.   
    528.             } else {  
    529.                 mScroller.startScroll(0, mScrollY, 0, maxY - mScrollY);  
    530.                 invalidate();  
    531.                 Log.d("onlayout", "isPull: true");  
    532.             }  
    533.   
    534.         } else if (mScrollY <= minY) {//如果滚动,超过了上边界,就回弹到上边界  
    535.             // 超出了上边界,弹回  
    536.             mScroller.startScroll(0, mScrollY, 0, minY - mScrollY);  
    537.             invalidate();  
    538.         } else if (Math.abs(velocityY) >= minFlingVelocity && maxY > 0) {//大于1页的时候  
    539. //            mScroller.fling(0, mScrollY, 0, (int) (velocityY * 1.2f), 0, 0, minY, maxY);  
    540.             mScroller.fling(0, mScrollY, 0, (int) (velocityY * 2f), 0, 0, getNScrollYTop(), getNScrollYButtom());  
    541.             invalidate();  
    542.         }  
    543.   
    544.   
    545.     }  
    546.   
    547.   
    548.     public void ifNeedScrollBack() {  
    549.         int mScrollY = getScrollY();  
    550.         int maxY = getScrollYButtom();  
    551.         int minY = getScrollYTop();  
    552.         if (mScrollY > maxY) {  
    553.             // 超出了下边界,弹回  
    554.             mScroller.startScroll(0, mScrollY, 0, maxY - mScrollY);  
    555.   
    556.             invalidate();  
    557.   
    558.         } else if (mScrollY < minY) {  
    559.             // 超出了上边界,弹回  
    560.             mScroller.startScroll(0, mScrollY, 0, minY - mScrollY);  
    561.             invalidate();  
    562.         }  
    563.     }  
    564.   
    565.     @Override  
    566.     protected void onScrollChanged(int l, int t, int oldl, int oldt) {  
    567.         super.onScrollChanged(l, t, oldl, oldt);  
    568.     }  
    569.   
    570.     @Override  
    571.     public void computeScroll() {  
    572.         if (mScroller.computeScrollOffset()) {  
    573.   
    574.             scrollTo(0, mScroller.getCurrY());  
    575.   
    576.             postInvalidate();  
    577.   
    578.         } else {  
    579.             Log.d("onlayout", "computeScroll,isMove:"+isMove+",isPull:"+isPull);  
    580.             if (!isMove && !isPull) {  
    581.                 ifNeedScrollBack();  
    582.             }  
    583.   
    584.         }  
    585.     }  
    586.   
    587.   
    588.     public void onPullSuccess() {  
    589.   
    590.         soomToBack();  
    591.     }  
    592.   
    593.     public void soomToBack() {  
    594.         int mScrollY = getScrollY();  
    595.         int maxY = getScrollYButtom();  
    596.         Log.d("onlayout", "soomToBack: (maxY - mScrollY)="+(maxY - mScrollY)+",maxY="+maxY+",mScrollY="+mScrollY);  
    597.         // 超出了下边界,弹回  
    598.         mScroller.startScroll(0, mScrollY, 0, maxY - mScrollY, 300);  
    599.         invalidate();  
    600.         postDelayed(new Runnable() {  
    601.             @Override  
    602.             public void run() {  
    603.                 fooder_layout.showFooderPull();  
    604.                 isPull = false;  
    605.             }  
    606.         }, 310);  
    607.   
    608.   
    609.     }  
    610.   
    611.     private PullDownListem pullDownListem;  
    612.   
    613.     public void setPullDownListem(PullDownListem pullDownListem) {  
    614.         this.pullDownListem = pullDownListem;  
    615.     }  
    616.   
    617.     public interface PullDownListem {  
    618.   
    619.         public void onPullDown();  
    620.   
    621.     }  
  • 相关阅读:
    Springboot使用PlatformTransactionManager接口的事务处理
    js 正则替换html标签
    【转】mysql查询时,查询结果按where in数组排序
    js输出字幕数字a-zA-Z0-9
    tcpdump使用教程
    rsync安装使用教程
    vim配置修改教程
    XD刷机报错bad CRC
    使用docker搭建seafile服务器
    案例:使用sqlplus登录报ORA-12547错误
  • 原文地址:https://www.cnblogs.com/zhoug2020/p/6076194.html
Copyright © 2020-2023  润新知