ScrollView嵌套Expandable ListView滚动冲突问题求助
我之前也碰到过一模一样的滚动冲突问题,根源就是ScrollView会优先拦截触摸事件,导致ExpandableListView没法正常响应滚动操作——必须按住才能滚动的情况,就是因为ScrollView先把滚动事件抢走了。下面给你几个实用的解决思路,按你需求选就行:
方法一:自定义ExpandableListView,主动控制事件拦截
这种方法最可靠,通过重写ListView的触摸相关方法,让它在被触摸时主动告诉父布局(ScrollView)不要拦截事件:
public class CustomExpandableListView extends ExpandableListView { public CustomExpandableListView(Context context) { super(context); } public CustomExpandableListView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomExpandableListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 触摸开始时,禁止父布局拦截事件 if (ev.getAction() == MotionEvent.ACTION_DOWN) { getParent().requestDisallowInterceptTouchEvent(true); } // 触摸结束时,恢复父布局的拦截权限 else if (ev.getAction() == MotionEvent.ACTION_UP) { getParent().requestDisallowInterceptTouchEvent(false); } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { // 确保滚动过程中父布局不会抢事件 if (ev.getAction() != MotionEvent.ACTION_UP) { getParent().requestDisallowInterceptTouchEvent(true); } else { getParent().requestDisallowInterceptTouchEvent(false); } return super.onTouchEvent(ev); } }
然后把布局里的ExpandableListView替换成这个自定义类:
<com.kitkat.crossroads.CustomExpandableListView android:layout_width="match_parent" android:layout_height="wrap_content" <!-- 其他属性保持不变 --> />
方法二:用NestedScrollView替代ScrollView(API 21+)
如果你的项目最低兼容版本是Android 5.0(API 21)及以上,可以直接用NestedScrollView代替原生的ScrollView,它支持嵌套滚动机制,只需要给ExpandableListView加一行属性:
<androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 原来的ExpandableListView --> <ExpandableListView android:layout_width="match_parent" android:layout_height="wrap_content" android:nestedScrollingEnabled="true" <!-- 其他属性 --> /> </androidx.core.widget.NestedScrollView>
这个方法最省心,不需要写Java代码,不过要注意NestedScrollView的子View高度要设为wrap_content,这样它才能正确计算滚动范围。
方法三:给ExpandableListView设置触摸监听(轻量方案)
如果不想自定义View,也可以直接在Fragment里给ExpandableListView加个触摸监听,手动控制事件拦截:
// 在JobBidsFragment里找到ExpandableListView后设置监听 ExpandableListView expandableListView = view.findViewById(R.id.your_list_id); expandableListView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 触摸开始,禁止父布局拦截事件 v.getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_UP: // 触摸结束,恢复父布局拦截权限 v.getParent().requestDisallowInterceptTouchEvent(false); break; } // 返回false,让ListView自己处理滚动逻辑 return false; } });
额外提醒:尽量避免嵌套滚动容器
虽然上面的方法能解决问题,但不建议在ScrollView里嵌套ExpandableListView——因为如果ExpandableListView的高度设为wrap_content,它会一次性加载所有列表项,完全失去ListView的复用机制,会导致页面卡顿,内存占用过高。
如果你的页面布局允许,最好重构一下:比如把ScrollView里的其他内容和ExpandableListView整合到一个RecyclerView里(用多类型布局),或者把ExpandableListView作为页面的主滚动容器,其他内容放在它的header里,这样既没有滚动冲突,性能也更好。
内容的提问来源于stack exchange,提问作者javacoder123

