如何解决ScrollView抢占触摸事件,实现仅滚轮滚动且TextInput可选文本
解决ScrollView嵌套TextInput时仅允许滚轮滚动的问题
在ScrollView中嵌套TextInput后,希望仅保留鼠标滚轮触发的滚动功能,禁用其他鼠标按钮(如左键)拖动导致的滚动,避免干扰文本选择操作。原代码修改on_touch_down后无法正常选择文本,手动传递触摸事件也未生效。
解决方案
问题出在原代码未完整处理文本选择必需的on_touch_move和on_touch_up事件,且触摸坐标转换与事件传递逻辑不够准确。以下是修正后的完整代码:
from kivy.app import App from kivy.uix.scrollview import ScrollView from kivy.uix.textinput import TextInput class CustomScrollView(ScrollView): def on_touch_down(self, touch): # 仅响应鼠标滚轮事件 if touch.button in ('scrollup', 'scrolldown'): return super().on_touch_down(touch) # 非滚轮事件,先传递给子控件处理 if not self.collide_point(*touch.pos): return False # 将触摸坐标转换为ScrollView本地坐标系 touch.push() touch.apply_transform_2d(self.to_widget) handled = False # 仅将事件传递给触摸点所在的子控件 for child in self.children: if child.collide_point(*touch.pos): if child.dispatch('on_touch_down', touch): handled = True break touch.pop() return handled def on_touch_move(self, touch): # 滚轮事件交给父类处理 if touch.button in ('scrollup', 'scrolldown'): return super().on_touch_move(touch) if not self.collide_point(*touch.pos): return False touch.push() touch.apply_transform_2d(self.to_widget) handled = False for child in self.children: if child.collide_point(*touch.pos): if child.dispatch('on_touch_move', touch): handled = True break touch.pop() return handled def on_touch_up(self, touch): # 滚轮事件交给父类处理 if touch.button in ('scrollup', 'scrolldown'): return super().on_touch_up(touch) if not self.collide_point(*touch.pos): return False touch.push() touch.apply_transform_2d(self.to_widget) handled = False for child in self.children: if child.collide_point(*touch.pos): if child.dispatch('on_touch_up', touch): handled = True break touch.pop() return handled class main(App): def build(self): scroll_view = CustomScrollView(do_scroll_x=False) text_input = TextInput( text="text "*5000, multiline=True, readonly=True, size_hint=(None, None), size=(400, 2000), cursor=(0, 0) ) scroll_view.add_widget(text_input) return scroll_view if __name__ == '__main__': main().run()
逻辑说明
- 针对三个触摸事件方法(
on_touch_down/on_touch_move/on_touch_up)做统一处理:仅让ScrollView响应滚轮事件,其他事件全部传递给子控件。 - 通过坐标转换将全局触摸位置转为ScrollView本地坐标,确保子控件能正确识别触摸点。
- 仅将事件分发到触摸点所在的子控件,避免不必要的事件传递,提升处理效率。
内容的提问来源于stack exchange,提问作者Mango
相关产品推荐
相关产品推荐

