首页 > 代码库 > ScrollView反弹效果

ScrollView反弹效果

  1 public class BounceScrollView extends ScrollView {  2     private View inner;// 孩子View  3   4     private float y;// 点击时y坐标  5   6     private Rect normal = new Rect();// 矩形(这里只是个形式,只是用于判断是否需要动画.)  7   8     private boolean isCount = false;// 是否开始计算  9  10     public BounceScrollView(Context context, AttributeSet attrs) { 11         super(context, attrs); 12     } 13  14     /*** 15      * 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate 16      * 方法,也应该调用父类的方法,使该方法得以执行. 17      */ 18     @Override 19     protected void onFinishInflate() { 20         if (getChildCount() > 0) { 21             inner = getChildAt(0); 22         } 23     } 24  25     /*** 26      * 监听touch 27      */ 28     @Override 29     public boolean onTouchEvent(MotionEvent ev) { 30         if (inner != null) { 31             commOnTouchEvent(ev); 32         } 33  34         return super.onTouchEvent(ev); 35     } 36  37     /*** 38      * 触摸事件 39      *  40      * @param ev 41      */ 42     public void commOnTouchEvent(MotionEvent ev) { 43         int action = ev.getAction(); 44         switch (action) { 45         case MotionEvent.ACTION_DOWN: 46             break; 47         case MotionEvent.ACTION_UP: 48             // 手指松开. 49             if (isNeedAnimation()) { 50                 animation(); 51                 isCount = false; 52             } 53             break; 54         /*** 55          * 排除出第一次移动计算,因为第一次无法得知y坐标, 在MotionEvent.ACTION_DOWN中获取不到, 56          * 因为此时是MyScrollView的touch事件传递到到了LIstView的孩子item上面.所以从第二次计算开始. 57          * 然而我们也要进行初始化,就是第一次移动的时候让滑动距离归0. 之后记录准确了就正常执行. 58          */ 59         case MotionEvent.ACTION_MOVE: 60             final float preY = y;// 按下时的y坐标 61             float nowY = ev.getY();// 时时y坐标 62             int deltaY = (int) (preY - nowY);// 滑动距离 63             if (!isCount) { 64                 deltaY = 0; // 在这里要归0. 65             } 66  67             y = nowY; 68             // 当滚动到最上或者最下时就不会再滚动,这时移动布局 69             if (isNeedMove()) { 70                 // 初始化头部矩形 71                 if (normal.isEmpty()) { 72                     // 保存正常的布局位置 73                     normal.set(inner.getLeft(), inner.getTop(), 74                             inner.getRight(), inner.getBottom()); 75                 } 76 //                Log.e("jj", "矩形:" + inner.getLeft() + "," + inner.getTop() 77 //                        + "," + inner.getRight() + "," + inner.getBottom()); 78                 // 移动布局 79                 inner.layout(inner.getLeft(), inner.getTop() - deltaY / 2, 80                         inner.getRight(), inner.getBottom() - deltaY / 2); 81             } 82             isCount = true; 83             break; 84  85         default: 86             break; 87         } 88     } 89  90     /*** 91      * 回缩动画 92      */ 93     public void animation() { 94         // 开启移动动画 95         TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(), normal.top); 96         ta.setDuration(200); 97         inner.startAnimation(ta); 98         // 设置回到正常的布局位置 99         inner.layout(normal.left, normal.top, normal.right, normal.bottom);100 101 //        Log.e("jj", "回归:" + normal.left + "," + normal.top + "," + normal.right102 //                + "," + normal.bottom);103 104         normal.setEmpty();105 106     }107 108     // 是否需要开启动画109     public boolean isNeedAnimation() {110         return !normal.isEmpty();111     }112 113     /***114      * 是否需要移动布局 inner.getMeasuredHeight():获取的是控件的总高度115      * 116      * getHeight():获取的是屏幕的高度117      * 118      * @return119      */120     public boolean isNeedMove() {121         int offset = inner.getMeasuredHeight() - getHeight();122         int scrollY = getScrollY();123 //        Log.e("jj", "scrolly=" + scrollY);124         // 0是顶部,后面那个是底部125         if (scrollY == 0 || scrollY == offset) {126             return true;127         }128         return false;129     }130 131 }

 

此View用于,在没有充满父级的时候,下拉会有反弹效果。但是Android自带的ScrollView就不可以。

 

ScrollView反弹效果