忽略 ScrollView 中的滚动交互区域

Ignore area in ScrollView for scrolling interactions

假设我有这个 UI 改编自 ScrollView 示例:

from kivy.app import App
from kivy.uix.slider import Slider
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout

class ScrollViewApp(App):
    def build(self):
        layout = GridLayout(cols=1, padding=10, spacing=10,
                size_hint=(None, None), width=500)
        layout.bind(minimum_height=layout.setter('height'))
        for i in range(15):
            blt = BoxLayout(size_hint=(None, None), size=(480, 40))
            blt.add_widget(Label(text="Slider %d" % i))
            btn = Slider(value=i, min=0, max=42, size=(400, 40),
                         size_hint=(None, None))
            blt.add_widget(btn)
            layout.add_widget(blt)
        scroll = ScrollView(size_hint=(None, None), size=(500, 320),
                pos_hint={'center_x': .5, 'center_y': .5}, do_scroll_x=False)
        scroll.add_widget(layout)
        return scroll

if __name__ == '__main__':
    ScrollViewApp().run()

由于 scroll_timeout,与 Slider 的互动被延迟。是否可以在 ScrollView 中定义区域,其中 touch 事件直接传递到 children 而没有延迟(并且没有启动滚动)?

看看Widget touch event bubbling

我从来没有和你做过同样的事情,但也许你可以创建一个自定义 class 继承 ScrollView 并覆盖 on_touch_down 事件,你可以:

  1. 禁用滚动
  2. 致电super.on_touch_down
  3. 启用滚动。

另一种方法可能是创建一个自定义小部件,它继承了用户点击的 Slider class。然后用 return True 重载它的 on_touch_down 方法。 Documentation says:

In order to stop this event bubbling, one of these methods must return True

ScrollView 也会触发 on_scroll_start 事件,所以也许你可以在那里做类似的事情。

我想出了一个简单的覆盖 ScrollViewon_touch_down,到目前为止它似乎满足了我的需要。这个想法是测试触摸事件是否落入 'exclusion zone'(在下面的示例中,只检查了 x 维度,但将其扩展到任意矩形区域是微不足道的)。如果它确实落入该禁区,on_touch_down 事件将被分派给 ScrollView 的 children。如果 child 捕获它,则该事件被吞没。在所有其他情况下,将调用 super.on_touch_down,即启动正常的滚动行为。如果触摸没有落在 Slider 上(在问题示例中),这样做的好处是仍然可以滚动。

class MSV(ScrollView):
    x_exclusion_lower = NumericProperty(None, allownone=True)
    x_exclusion_upper = NumericProperty(None, allownone=True)
    x_exclusion = ReferenceListProperty(x_exclusion_lower, x_exclusion_upper)

    def on_touch_down(self, touch):
        pos_in_sv = self.to_local(*touch.pos)
        if (self.x_exclusion_lower is not None or self.x_exclusion_upper is not None) and (self.x_exclusion_lower is None or self.x_exclusion_lower <= pos_in_sv[0]) and \
                (self.x_exclusion_upper is None or self.x_exclusion_upper >= pos_in_sv[0]):
            touch.push()
            touch.apply_transform_2d(self.to_local)
            if self.dispatch_children('on_touch_down', touch):
                return True
            touch.pop()
        super(MSV, self).on_touch_down(touch)