欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

自定义ScrollView 实现上拉下拉的回弹效果--并且子控件中有Viewpager的情况

程序员文章站 2022-04-04 08:23:47
onInterceptTouchEvent就是对子控件中Viewpager的处理:左右滑动应该让viewpager消费 ......

自定义ScrollView 实现上拉下拉的回弹效果--并且子控件中有Viewpager的情况

onInterceptTouchEvent就是对子控件中Viewpager的处理:左右滑动应该让viewpager消费

  1 public class MyScrollView extends ScrollView {
  2 
  3     private View childView;
  4 
  5     public MyScrollView(Context context) {
  6         super(context);
  7     }
  8 
  9     public MyScrollView(Context context, AttributeSet attrs) {
 10         super(context, attrs);
 11     }
 12 
 13     public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
 14         super(context, attrs, defStyleAttr);
 15     }
 16 
 17 //    @Override
 18 //    protected void onLayout(boolean changed, int l, int t, int r, int b) {
 19 //        super.onLayout(changed, l, t, r, b);
 20 //    }
 21 
 22     //获取子视图
 23     @Override
 24     protected void onFinishInflate() {
 25         super.onFinishInflate();
 26         if (getChildCount() > 0) {
 27             childView = getChildAt(0);
 28         }
 29     }
 30 
 31     private int lastY;//上一次y轴方向操作的坐标位置
 32     private Rect normal = new Rect();//用于记录临界状态的左、上、右、下
 33     private boolean isFinishAnimation = true;//是否动画结束
 34 
 35     private int lastX, downX, downY;
 36 
 37     //拦截:实现父视图对子视图的拦截
 38     //是否拦截成功,取决于方法的返回值。返回值true:拦截成功。反之,拦截失败
 39     @Override
 40     public boolean onInterceptTouchEvent(MotionEvent ev) {
 41         boolean isIntercept = false;
 42         int eventX = (int) ev.getX();
 43         int eventY = (int) ev.getY();
 44         switch (ev.getAction()) {
 45             case MotionEvent.ACTION_DOWN:
 46                 lastX = downX = eventX;
 47                 lastY = downY = eventY;
 48                 break;
 49             case MotionEvent.ACTION_MOVE:
 50                 //获取水平和垂直方向的移动距离
 51                 int absX = Math.abs(eventX - downX);
 52                 int absY = Math.abs(eventY - downY);
 53 
 54                 if(absY > absX && absY >= UIUtils.dp2px(10)){
 55                     isIntercept = true;//执行拦截
 56                 }
 57 
 58                 lastX = eventX;
 59                 lastY = eventY;
 60                 break;
 61         }
 62 
 63         return isIntercept;
 64     }
 65 
 66     @Override
 67     public boolean onTouchEvent(MotionEvent ev) {
 68         if (childView == null || !isFinishAnimation) {
 69             return super.onTouchEvent(ev);
 70         }
 71 
 72         int eventY = (int) ev.getY();//获取当前的y轴坐标
 73         switch (ev.getAction()) {
 74             case MotionEvent.ACTION_DOWN:
 75                 lastY = eventY;
 76                 break;
 77             case MotionEvent.ACTION_MOVE:
 78 
 79                 int dy = eventY - lastY;//微小的移动量
 80 
 81                 if (isNeedMove()) {
 82                     if (normal.isEmpty()) {
 83                         //记录了childView的临界状态的左、上、右、下
 84                         normal.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
 85 
 86                     }
 87                     //重新布局
 88                     childView.layout(childView.getLeft(), childView.getTop() + dy / 2, childView.getRight(), childView.getBottom() + dy / 2);
 89                 }
 90 
 91                 lastY = eventY;//重新赋值
 92                 break;
 93             case MotionEvent.ACTION_UP:
 94                 if (isNeedAnimation()) {
 95                     //使用平移动画
 96                     int translateY = childView.getBottom() - normal.bottom;
 97                     TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, 0, -translateY);
 98                     translateAnimation.setDuration(200);
 99 //                translateAnimation.setFillAfter(true);//停留在最终位置上
100 
101                     translateAnimation.setAnimationListener(new Animation.AnimationListener() {
102                         @Override
103                         public void onAnimationStart(Animation animation) {
104                             isFinishAnimation = false;
105                         }
106 
107                         @Override
108                         public void onAnimationEnd(Animation animation) {
109                             isFinishAnimation = true;
110                             childView.clearAnimation();//清除动画
111                             //重新布局
112                             childView.layout(normal.left, normal.top, normal.right, normal.bottom);
113                             //清除normal的数据
114                             normal.setEmpty();
115                         }
116 
117                         @Override
118                         public void onAnimationRepeat(Animation animation) {
119 
120                         }
121                     });
122 
123                     //启动动画
124                     childView.startAnimation(translateAnimation);
125                 }
126 
127 
128                 break;
129         }
130 
131 
132         return super.onTouchEvent(ev);
133     }
134 
135     //判断是否需要执行平移动画
136     private boolean isNeedAnimation() {
137         return !normal.isEmpty();
138 
139     }
140 
141     private boolean isNeedMove() {
142         int childMeasuredHeight = childView.getMeasuredHeight();//获取子视图的高度
143         int scrollViewMeasuredHeight = this.getMeasuredHeight();//获取布局的高度
144 
145         Log.e("TAG", "childMeasuredHeight = " + childMeasuredHeight);
146         Log.e("TAG", "scrollViewMeasuredHeight = " + scrollViewMeasuredHeight);
147 
148         int dy = childMeasuredHeight - scrollViewMeasuredHeight;//dy >= 0
149 
150         int scrollY = this.getScrollY();//获取用户在y轴方向上的偏移量 (上 + 下 -)
151         if (scrollY <= 0 || scrollY >= dy) {
152             return true;//按照我们自定义的MyScrollView的方式处理
153         }
154         //其他处在临界范围内的,返回false。即表示,仍按照ScrollView的方式处理
155         return false;
156     }
157 }