You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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()

逻辑说明

  1. 针对三个触摸事件方法(on_touch_down/on_touch_move/on_touch_up)做统一处理:仅让ScrollView响应滚轮事件,其他事件全部传递给子控件。
  2. 通过坐标转换将全局触摸位置转为ScrollView本地坐标,确保子控件能正确识别触摸点。
  3. 仅将事件分发到触摸点所在的子控件,避免不必要的事件传递,提升处理效率。

内容的提问来源于stack exchange,提问作者Mango

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:53:20