实现页面回弹滑动效果

先看下效果图:

然后使用封装好的回弹控件类,直接贴上代码:

  1 public class DragScrollView extends ScrollView {
  2     private static final String TAG = "TEST";
  3 
  4     // 移动因子, 是一个百分比, 比如手指移动了100px, 那么View就只移动40px
  5     // 目的是达到一个延迟的效果
  6     private static final float MOVE_FACTOR = 0.4f;
  7 
  8     // 松开手指后, 界面回到正常位置需要的动画时间
  9     private static final int ANIM_TIME = 300;
 10 
 11     // ScrollView的子View, 也是ScrollView的唯一一个子View
 12     private View contentView;
 13 
 14     // 手指按下时的Y值, 用于在移动时计算移动距离
 15     // 如果按下时不能上拉和下拉, 会在手指移动时更新为当前手指的Y值
 16     private float startY;
 17 
 18     // 用于记录正常的布局位置
 19     private Rect originalRect = new Rect();
 20 
 21     // 手指按下时记录是否可以继续下拉
 22     private boolean canPullDown = false;
 23 
 24     // 手指按下时记录是否可以继续上拉
 25     private boolean canPullUp = false;
 26 
 27     // 在手指滑动的过程中记录是否移动了布局
 28     private boolean isMoved = false;
 29 
 30     public DragScrollView(Context context) {
 31         super(context);
 32     }
 33 
 34     public DragScrollView(Context context, AttributeSet attrs) {
 35         super(context, attrs);
 36     }
 37 
 38     @Override
 39     protected void onFinishInflate() {
 40         super.onFinishInflate();
 41         if (getChildCount() > 0) {
 42             contentView = getChildAt(0);
 43         }
 44     }
 45 
 46     @Override
 47     protected void onLayout(boolean changed, int l, int t, int r, int b) {
 48         super.onLayout(changed, l, t, r, b);
 49 
 50         if (contentView == null)
 51             return;
 52 
 53         // ScrollView中的唯一子控件的位置信息, 这个位置信息在整个控件的生命周期中保持不变
 54         originalRect.set(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());
 55     }
 56 
 57     /**
 58      * 在触摸事件中, 处理上拉和下拉的逻辑
 59      */
 60     @Override
 61     public boolean dispatchTouchEvent(MotionEvent ev) {
 62 
 63         if (contentView == null) {
 64             return super.dispatchTouchEvent(ev);
 65         }
 66 
 67         int action = ev.getAction();
 68 
 69         switch (action) {
 70             case MotionEvent.ACTION_DOWN:
 71 
 72                 // 判断是否可以上拉和下拉
 73                 canPullDown = isCanPullDown();
 74                 canPullUp = isCanPullUp();
 75 
 76                 // 记录按下时的Y值
 77                 startY = ev.getY();
 78                 break;
 79 
 80             case MotionEvent.ACTION_UP:
 81 
 82                 if (!isMoved)
 83                     break; // 如果没有移动布局, 则跳过执行
 84 
 85                 // 开启动画
 86                 TranslateAnimation anim = new TranslateAnimation(0, 0, contentView.getTop(), originalRect.top);
 87                 anim.setDuration(ANIM_TIME);
 88 
 89                 contentView.startAnimation(anim);
 90 
 91                 // 设置回到正常的布局位置
 92                 contentView.layout(originalRect.left, originalRect.top, originalRect.right, originalRect.bottom);
 93 
 94                 // 将标志位设回false
 95                 canPullDown = false;
 96                 canPullUp = false;
 97                 isMoved = false;
 98 
 99                 break;
100             case MotionEvent.ACTION_MOVE:
101 
102                 // 在移动的过程中, 既没有滚动到可以上拉的程度, 也没有滚动到可以下拉的程度
103                 if (!canPullDown && !canPullUp) {
104                     startY = ev.getY();
105                     canPullDown = isCanPullDown();
106                     canPullUp = isCanPullUp();
107 
108                     break;
109                 }
110 
111                 // 计算手指移动的距离
112                 float nowY = ev.getY();
113                 int deltaY = (int) (nowY - startY);
114 
115                 // 是否应该移动布局
116                 boolean shouldMove = (canPullDown && deltaY > 0) // 可以下拉, 并且手指向下移动
117                         || (canPullUp && deltaY < 0) // 可以上拉, 并且手指向上移动
118                         || (canPullUp && canPullDown); // 既可以上拉也可以下拉(这种情况出现在ScrollView包裹的控件比ScrollView还小)
119 
120                 if (shouldMove) {
121                     // 计算偏移量
122                     int offset = (int) (deltaY * MOVE_FACTOR);
123 
124                     // 随着手指的移动而移动布局
125                     contentView.layout(originalRect.left, originalRect.top + offset, originalRect.right,
126                             originalRect.bottom + offset);
127 
128                     isMoved = true; // 记录移动了布局
129                 }
130 
131                 break;
132             default:
133                 break;
134         }
135 
136         return super.dispatchTouchEvent(ev);
137     }
138 
139     /**
140      * 判断是否滚动到顶部
141      */
142     private boolean isCanPullDown() {
143         return getScrollY() == 0 || contentView.getHeight() < getHeight() + getScrollY();
144     }
145 
146     /**
147      * 判断是否滚动到底部
148      */
149     private boolean isCanPullUp() {
150         return contentView.getHeight() <= getHeight() + getScrollY();
151     }
152 }

最后就是使用方法,直接在写好的xml中套上,就跟使用ScrollView的方法一样。

posted @ 2019-10-29 11:49  蒜香小龙虾  阅读(1063)  评论(0编辑  收藏  举报