自定义ScrollView 实现上拉下拉的回弹效果--并且子控件中有Viewpager的情况
程序员文章站
2022-06-30 12:16:09
onInterceptTouchEvent就是对子控件中Viewpager的处理:左右滑动应该让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 }