外層ScrollView坦辟,內(nèi)嵌ListView伍俘,都是垂直方向。采用內(nèi)部攔截法楣铁,實(shí)現(xiàn)ListView能滾動(dòng)時(shí)則讓ListView處理,當(dāng)ListView滑到頂部或者底部不能滑動(dòng)時(shí)讓ScrollView處理
布局
上面有一段文本更扁,中間是ListView盖腕,下面還有一段文本
<?xml version="1.0" encoding="utf-8"?>
<org.icegeneral.scroll.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="AAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA\nAAAAAAAAAA" />
<org.icegeneral.scroll.MyListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="500dp"
android:background="#888888">
</org.icegeneral.scroll.MyListView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="BBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB\nBBBBBBBBBB" />
</LinearLayout>
</org.icegeneral.scroll.MyScrollView>
MyScrollView
ACTION_DOWN必須讓給ListView赫冬,ListView才能收到ACTION_MOVE,ListView才能判斷自己是否還能滾動(dòng)
第二點(diǎn)要注意的是:因?yàn)锳CTION_DOWN讓給ListView溃列,那么ACTION_DOWN就無法進(jìn)入ScrollView的onTouchEvent劲厌, 但是ScrollView的滾動(dòng)需要在ACTION_DOWN階段做一些準(zhǔn)備,所以主動(dòng)調(diào)用了一次
public class MyScrollView extends ScrollView {
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onTouchEvent(ev);
return false;
}
return true;
}
}
MyListView
public class MyListView extends ListView {
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private float lastY;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
getParent().getParent().requestDisallowInterceptTouchEvent(true);
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
if (lastY > ev.getY()) {
// 如果是向上滑動(dòng)听隐,且不能滑動(dòng)了补鼻,則讓ScrollView處理
if (!canScrollList(1)) {
getParent().getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
} else if (ev.getY() > lastY) {
// 如果是向下滑動(dòng),且不能滑動(dòng)了遵绰,則讓ScrollView處理
if (!canScrollList(-1)) {
getParent().getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
}
}
lastY = ev.getY();
return super.dispatchTouchEvent(ev);
}
}
Activity
protected void onCreate(Bundle savedInstanceState) {
....
scrollView.smoothScrollTo(0, 0);
}
ACTION_DOWN的特殊性
解釋下為什么ListView的ACTION_UP無需調(diào)用
getParent().getParent().requestDisallowInterceptTouchEvent(false)
來看下ViewGroup源碼
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState(); //重點(diǎn)是這句
}
// 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 {
intercepted = true;
}
...
}
...
}
private void resetTouchState() {
clearTouchTargets();
resetCancelNextUpFlag(this);
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; //移除FLAG_DISALLOW_INTERCEPT
mNestedScrollAxes = SCROLL_AXIS_NONE;
}
所以如果是ACTION_DOWN辽幌,會(huì)調(diào)用resetTouchState()增淹,移除FLAG_DISALLOW_INTERCEPT椿访,所以ACTION_DOWN時(shí),parent一定會(huì)進(jìn)入onInterceptTouchEvent
其他
其實(shí)現(xiàn)在都用RecyclerView代替ListView虑润,ScrollView嵌套RecyclerView時(shí)成玫,對于手勢已經(jīng)支持得很好,不必自己處理沖突拳喻。這個(gè)在demo里也有寫哭当,做個(gè)對比