(源碼分析)ScrollView嵌套ListView/GridView的滑動事件處理


開發過程中經常會遇到使用scrollview嵌套listview或gridview的情況,這時由於scrollview攔截消費了滑動事件,所以在listview或gridview區域滑動時該區域無法滑動,而是scrollview整體滑動。

正確的處理應該是當焦點在listview或gridview區域該區域滑動,在區域外則scrollview滑動。
想要解決這個問題,加上如下代碼即可:
listView.setOnTouchListener(new View.OnTouchListener() {
  @Override
  public boolean onTouch(View arg0, MotionEvent arg1) {
    scrollView.requestDisallowInterceptTouchEvent(true);
return false;
  }

}



下面我們通過源碼來看看requestDisallowInterceptTouchEvent是怎麼起作用的。
requestDisallowInterceptTouchEvent函數實際上是ListView的父類AbsListView的一個函數,這個函數的源碼如下:
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    if (disallowIntercept) {
        recycleVelocityTracker();
    }
    super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
可以看到是調用了父類的同名函數,即ViewGroup的requestDisallowInterceptTouchEvent函數,源碼如下:
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
        // We're already in this state, assume our ancestors are too
        return;
    }

    if (disallowIntercept) {
        mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
    } else {
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    }

    // Pass it up to our parent
    if (mParent != null) {
        mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
    }
}
其中mGroupFlags 保存的是二進制值,控制了幾個開關。
在這裏如果disallowIntercept爲true的話,則爲mGroupFlags添加FLAG_DISALLOW_INTERCEPT標誌。同時如果有父view,調用父view的同名函數。因爲父view也是ViewGroup,所以會一層層調用直到根佈局。

那麼FLAG_DISALLOW_INTERCEPT這個標誌是如何影響滑動事件的呢?
這個標誌使用在ViewGroup的dispatchTouchEvent函數中,由於代碼較多,我們只截取有關的部分,如下:
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); // restore action in case it was changed
    } else {
        intercepted = false;
    }
} else {
    // There are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
}
mGroupFlags有FLAG_DISALLOW_INTERCEPT這個標誌,則disallowIntercept爲true,這時intercepted爲false。
dispatchTouchEvent函數的後續代碼中intercepted爲false則爲所有的子view分發touch事件。

回到之前的問題中,當listview或gridview的區域touch時,設置requestDisallowInterceptTouchEvent爲true,這樣scrollview的touch就會分發下去,這樣listview或gridview就可以響應滑動事件了。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章