我們在開發中經常會碰到view滑動衝突的情況。
滑動衝突的解決辦法就兩種:
1.外部攔截法:是指在點擊事件先經過父容器的攔截處理,如果父容器需要處理此事件就進行攔截,如果不需要此事件就不攔截,這樣就可以解決滑動衝突的問題,外部攔截法需要重寫父容器的onInterceptTouchEvent()方法,在內部做相應攔截處理。
2.內部攔截法:是指點擊事件先經過子View處理,如果子view需要次事件就直接消耗掉,否則就交給父容器進行處理,這樣就可以解決滑動衝突的問題。內部攔截需要配合requestDisallowInterceptTouchEvent()方法,來確定子view是否允許父容器攔截事件。
a.允許父容器進行事件攔截
getParent().requestDisallowInterceptTouchEvent(false)
b.禁止父容器進行事件攔截
getParent().requestDisallowInterceptTouchEvent(true)
以上是解決滑動衝突的方法,下面我們回到我們今天解決的正題ScrollView嵌套viewPager中嵌套listView滑動事件衝突
這裏可以使用第一種解決方案:外部攔截法
只需要自定義一個ScrollView,然後重寫它的onInterceptTouchEvent()即可,代碼如下:
public class VerticalScrollView extends ScrollView { private float xDistance, yDistance, xLast, yLast; public VerticalScrollView(Context context) { super(context); } public VerticalScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public VerticalScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; if (xDistance > yDistance) { return false; } } return super.onInterceptTouchEvent(ev); } }
第二種解決方案如下:不需要自定義控件
1.第一步:允許父容器進行事件攔截
scrollView.setOnScrollToBottomLintener(new BottomScrollView.OnScrollToBottomListener() { @Override public void onScrollBottomListener(boolean isBottom) { if (isBottom) { scrollView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: //解決滑動衝突 scrollView.getParent().requestDisallowInterceptTouchEvent(false); break; } return false; } }); } } });
2.第二步:父元素也要默認攔截除了ACTION_DOWN以外的其他事件,這樣當子元素調用
scrollView.getParent().requestDisallowInterceptTouchEvent(false)
方法時,父元素才能繼續攔截所需的事件。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); return action != MotionEvent.ACTION_DOWN; }
3.爲什麼父容器不能攔截ACTION_DOWN事件呢?
因爲ACTION_DOWN事件並不受FLAG_DISALLOW_INTERCEPT這個標記位的控制,所以一旦父容器攔截ACTION_DOWN事件,那麼所有的事件都無法傳遞到子元素中去,這樣內部攔截就無法起作用了。