以前的博客《下拉刷新、上拉加載更多控件實現原理及解析》中實現了一個通用的刷新控件,可是使用中發現了問題,當包含的列表中有嵌套滾動(滾動控件中還包含滾動控件)時,會出現滾動衝突。java
由於咱們直接攔截了「dispatchTouchEvent」,這個回調是不能被子控件控制的,因此當子控件須要父控件讓出Touch事件時(好比調用父控件的「requestDisallowInterceptTouchEvent(true)」方法),咱們如今的實現是達不到的,咱們須要換一個回調接口,去實現咱們的功能。ide
可以攔截Touch事件的函數,除了咱們前面用到的「dispatchTouchEvent」,還有就是「onInterceptTouchEvent」,這個函數是須要和「onTouchEvent」配合使用的。函數
「onInterceptTouchEvent」攔截觸摸事件的意思,只要你在該函數中一直返回false(ViewGroup的默認實現是返回false的),後續的Touch事件會一直先給這個函數;一旦你在該函數中返回true,那該函數就再也收不到後續的Touch事件,而是傳給它的「onTouchEvent」函數,「onTouchEvent」返回false表示不消耗Touch事件,返回true則消耗Touch事件。優化
「requestDisallowInterceptTouchEvent」函數用來子控件不想父控件打斷它的Touch事件。spa
結合前面的分析,咱們把相關代碼修改以下:code
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if ((!mPullRefreshEnable && !mPullLoadEnable) || isRefreshing) { return super.onInterceptTouchEvent(ev); } switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: { if (refreshLayoutController != null) { mPullRefreshEnable = refreshLayoutController.isPullRefreshEnable(); mPullLoadEnable = refreshLayoutController.isPullLoadEnable(); } preY = ev.getY(); preX = ev.getX(); actionDetermined = false; return super.onInterceptTouchEvent(ev); } case MotionEvent.ACTION_MOVE: { float currentY = ev.getY(); float currentX = ev.getX(); float dy = currentY - preY; float dx = currentX - preX; preY = currentY; preX = currentX; if (!actionDetermined) { actionDetermined = true; //判斷是下拉刷新仍是上拉加載更多 if (dy > 0 && !canChildScrollUp() && mPullRefreshEnable) { mCurrentAction = ACTION_PULL_DOWN_REFRESH; } else if (dy < 0 && !canChildScrollDown() && mPullLoadEnable) { mCurrentAction = ACTION_PULL_UP_LOAD_MORE; } else { mCurrentAction = -1; } } if (mCurrentAction != -1) { return true; } else { return super.onInterceptTouchEvent(ev); } } default: { return super.onInterceptTouchEvent(ev); } } }
@Override public boolean onTouchEvent(MotionEvent event) { if ((!mPullRefreshEnable && !mPullLoadEnable) || isRefreshing) { return false; } switch (event.getActionMasked()) { case MotionEvent.ACTION_MOVE: { float currentY = event.getY(); float currentX = event.getX(); float dy = currentY - preY; float dx = currentX - preX; preY = currentY; preX = currentX; handleScroll(dy); observerArriveBottom(); return true; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { return releaseTouch(); } default: { return super.onTouchEvent(event); } } }
當須要嵌套滾動時,須要複寫你子控件的「onTouch」函數:server
@Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { ((ViewGroup) getParent()).requestDisallowInterceptTouchEvent(true); } else if (ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { ((ViewGroup) getParent()).requestDisallowInterceptTouchEvent(false); } return super.onTouchEvent(ev); }