解决ViewPager与父ViewGrop的事件冲突
来源:互联网 发布:tim 知乎 编辑:程序博客网 时间:2024/06/03 16:08
在做公司项目的时候,遇到这样一个需求:activity最下层是一个地图控件,可以从下方划出一个layout占据下半个屏幕,也可以下滑收起状态(占1/5屏幕),该layout用于文字显示一条路线,可以左右滑动,切换不同路线(类似画廊效果),展开和收起状态都可以左右滑动,切换后地图上显示对应的路线。
我的思路是自定义一个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,所以感觉很奇怪!
- 解决ViewPager与父ViewGrop的事件冲突
- viewpager与子view的事件冲突解决
- viewpager与子view的事件冲突解决
- [转]viewpager与子view的事件冲突解决
- android 解决ViewPager与Gallery事件冲突
- SwipeRefreshLayout与ViewPager滑动事件冲突解决
- SwipeRefreshLayout与ViewPager滑动事件冲突解决
- ViewPager的事件冲突的解决
- 解决两个ViewPager的冲突事件
- 解决ViewPager和SlidingPaneLayout的事件冲突
- 解决SlideDetailsLayout与ViewPager垂直滚动事件冲突问题
- 解决ListView+ViewPager滑动事件冲突的问题
- ViewFlow嵌套在ViewPager事件冲突的解决
- 解决ListView+ViewPager滑动事件冲突的问题
- 解决viewpager与其他可以左右滑动的事件冲突
- ViewFlow嵌套在ViewPager事件冲突的解决
- 解决ViewPager与HorizontalScrollView 冲突
- ViewPager的touch事件与外面的scrollview冲突
- Multinomial distribution
- Python入门(七)tuple.dictionary
- 自定义共享元素动画(imageView)
- [C++]default constructor默认构造函数
- 蓝桥杯练习系统真题5——错误票据
- 解决ViewPager与父ViewGrop的事件冲突
- Head First 设计模式(三)工厂模式
- 设计一个有getMin功能的栈
- 蓝桥杯练习系统真题6——翻硬币
- GD库常见知识
- 蓝桥杯 带分数 暴力
- 蓝桥杯方格填数
- 云计算相关概念
- 软引用、弱引用和虚引用