解决ViewPager与父ViewGrop的事件冲突
2017-04-02 21:02
393 查看
在做公司项目的时候,遇到这样一个需求:activity最下层是一个地图控件,可以从下方划出一个layout占据下半个屏幕,也可以下滑收起状态(占1/5屏幕),该layout用于文字显示一条路线,可以左右滑动,切换不同路线(类似画廊效果),展开和收起状态都可以左右滑动,切换后地图上显示对应的路线。
我的思路是自定义一个layout,实现滑动展开和收起,这个简单,不一会就敲了一个demo:
测试了下,非常不错,已经实现了滑动展开和收起的效果,接下来只要在里面放一个ViewPager就可以了~
马上又写了一个放进去,结果发现layout的滑动效果没了,只有ViewPager响应了事件,想到可能是ViewPager抢夺了事件,于是马上自定义一个ViewPager,只消耗水平方向的时间,仍然没有效果。
奇怪了,不应该啊!
断点看了一下VIewPager的onTouchEvent方法,发现如果是竖直方向的事件并没有消耗,确实返回false!又看下SlideLayout的onTouchEvent方法没有执行,说明没有接收到事件。
尼玛!我的事件被谁吃了!按理说子View的onTouchEvent返回false,应该会把事件还给父View,执行父View的onTouchEvent才对啊!
静下几分钟后,既然ViewPager没有把事件还给SlideLayout,那我就在SlideLayout中拦截合适的事件,于是有了下面的改动:
我设定一个范围,只要在范围的事件就会触发layout的滑动,而范围外的事件则分发给ViewPager去消耗。虽然实现业务的需求,但是我始终没有明白为什么ViewPager消耗了它不需要的事件,不知道哪位大神可以告诉我原因?
ps:一个小问题onFling方法是必然执行的吗?(onDown返回了true)我在测试的时候发现,在一个测试机中onFling是必然触发的(无论是多慢地离开手指),而在另一个测试机上,慢慢滑动后手指离开屏幕不会触发onFling,所以感觉很奇怪!
我的思路是自定义一个layout,实现滑动展开和收起,这个简单,不一会就敲了一个demo:
public class SlideLayout extends RelativeLayout implements GestureDetector.OnGestureListener { private Scroller scroller; private GestureDetector detector; private int height;//此layout的高度 private int up;//展开的scrollY值 private int down;//收起的scrollY值 private int middle;//中间scrollY值 public SlideLayout(Context context) { this(context, null); } public SlideLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { scroller = new Scroller(context); detector = new GestureDetector(context, this); getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { getViewTreeObserver().removeOnGlobalLayoutListener(this); height = getHeight(); up = 0; down = -height * 2 / 3; middle = (up + down) / 2; Log.i("tag", "init"); } }); } public int dp2px(Context context, float dp) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } @Override public boolean onTouchEvent(MotionEvent event) { //事件交给GestureDetector处理 return detector.onTouchEvent(event); } @Override public void computeScroll() { if (scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), scroller.getCurrY()); invalidate(); } } @Override public boolean onDown(MotionEvent e) { return e.getY() >= Math.abs(getScrollY()); } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // Log.i("tag", "onScroll:" + distanceY); if (getScrollY() > up || getScrollY() < down) { return false; } scrollBy(0, Math.round(distanceY * 0.5f)); return true; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.i("tag", "onFling:" + velocityY); //确定滑动方向 int target; if (velocityY > 500) { target = down; } else if (velocityY < -500) { target = up; } else { if (e2.getY() > Math.abs(middle)) { target = down; } else { target = up; } } //开始滑动 smoothScrollTo(target); return true; } private void smoothScrollTo(int target) { scroller.startScroll(0, getScrollY(), 0, target - getScrollY()); invalidate(); } }
测试了下,非常不错,已经实现了滑动展开和收起的效果,接下来只要在里面放一个ViewPager就可以了~
马上又写了一个放进去,结果发现layout的滑动效果没了,只有ViewPager响应了事件,想到可能是ViewPager抢夺了事件,于是马上自定义一个ViewPager,只消耗水平方向的时间,仍然没有效果。
奇怪了,不应该啊!
断点看了一下VIewPager的onTouchEvent方法,发现如果是竖直方向的事件并没有消耗,确实返回false!又看下SlideLayout的onTouchEvent方法没有执行,说明没有接收到事件。
尼玛!我的事件被谁吃了!按理说子View的onTouchEvent返回false,应该会把事件还给父View,执行父View的onTouchEvent才对啊!
静下几分钟后,既然ViewPager没有把事件还给SlideLayout,那我就在SlideLayout中拦截合适的事件,于是有了下面的改动:
public class SlideLayout extends RelativeLayout implements GestureDetector.OnGestureListener { private Scroller scroller; private GestureDetector detector; private int height;//此layout的高度 private int up;//展开的scrollY值 private int down;//收起的scrollY值 private int middle;//中间scrollY值 private int slideHead;//可以触发此layout滑动的高度 public SlideLayout(Context context) { this(context, null); } public SlideLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { scroller = new Scroller(context); detector = new GestureDetector(context, this); getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { getViewTreeObserver().removeOnGlobalLayoutListener(this); height = getHeight(); up = 0; down = -height * 2 / 3; middle = (up + down) / 2; slideHead = dp2px(getContext(), 100); Log.i("tag", "init"); } }); } public int dp2px(Context context, float dp) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { int abs = Math.abs(getScrollY()); //如果down的位置在在设定范围内则直接拦截事件 if (ev.getY() < abs + slideHead && ev.getY() > abs) { Log.i("tag", "intercept"); return true; } } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { //事件交给GestureDetector处理 return detector.onTouchEvent(event); } @Override public void computeScroll() { if (scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), scroller.getCurrY()); invalidate(); } } @Override public boolean onDown(MotionEvent e) { return e.getY() >= Math.abs(getScrollY()); } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // Log.i("tag", "onScroll:" + distanceY); if (getScrollY() > up || getScrollY() < down) { return false; } scrollBy(0, Math.round(distanceY * 0.5f)); return true; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.i("tag", "onFling:" + velocityY); //确定滑动方向 int target; if (velocityY > 500) { target = down; } else if (velocityY < -500) { target = up; } else { if (e2.getY() > Math.abs(middle)) { target = down; } else { target = up; } } //开始滑动 smoothScrollTo(target); return true; } private void smoothScrollTo(int target) { scroller.startScroll(0, getScrollY(), 0, target - getScrollY()); invalidate(); } }
我设定一个范围,只要在范围的事件就会触发layout的滑动,而范围外的事件则分发给ViewPager去消耗。虽然实现业务的需求,但是我始终没有明白为什么ViewPager消耗了它不需要的事件,不知道哪位大神可以告诉我原因?
ps:一个小问题onFling方法是必然执行的吗?(onDown返回了true)我在测试的时候发现,在一个测试机中onFling是必然触发的(无论是多慢地离开手指),而在另一个测试机上,慢慢滑动后手指离开屏幕不会触发onFling,所以感觉很奇怪!
相关文章推荐
- 解决ListView+ViewPager滑动事件冲突的问题
- 解决两个ViewPager的冲突事件
- android 解决ViewPager与Gallery事件冲突
- SwipeRefreshLayout与ViewPager滑动事件冲突源码分析及解决办法
- 解决ViewPager和SlidingPaneLayout的事件冲突
- android 图片缩放后划屏拖动图片与Viewpager切换Item冲突(拖动图片的事件相应被屏蔽)问题解决
- Android中解决ListView嵌套Viewpager时,滑动事件冲突的方法
- 解决android中viewpager和内嵌html滑动事件冲突
- ViewFlow嵌套在ViewPager事件冲突的解决
- 学习过程中遇到很好的博客去学习-Handler详解(超棒)-View事件分发机制(解决ViewPager的滑动冲突)
- 解决ScrollView和VerticalViewPager滑动事件冲突问题
- 解决ListView+ViewPager滑动事件冲突的问题
- Android解决ListView头ViewPager事件冲突问题
- 【事件冲突】ScrollView嵌套ViewPager冲突的解决办法
- 解决ViewPager 放在ListView 头位置事件冲突
- ViewPager禁止滑动以及它与内层滑动控件水平方向上事件冲突的解决方法
- 解决ViewPager的左右滑动和点击返回事件的冲突
- 解决ScrollView嵌套viewpager滑动事件冲突问题
- ViewPager的事件冲突的解决
- ViewFlow嵌套在ViewPager事件冲突的解决