Kivy中使用Draggable在ScrollView间拖拽Widget时方向异常的问题求助
我最近在做一个Kivy项目,用到了kivy-garden的Draggable组件,遇到了一个奇怪的拖拽方向限制问题——只能从右侧的ScrollView往左侧拖,反过来完全不行,想请教下大家怎么解决!
我的环境
- 操作系统:Windows 10 Home
- Python版本:
3.11.5 - Kivy版本:
2.2.1 - kivy-garden-draggable版本:
0.2.0
我的代码
lists.py
from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.uix.scrollview import ScrollView from kivy_garden.draggable import KXDraggableBehavior, KXReorderableBehavior class ListApp(App): def build(self): return MainLayout() class MainLayout(BoxLayout): pass class ReorderableBoxLayout(KXReorderableBehavior, BoxLayout): pass class Card(KXDraggableBehavior, Label): pass ListApp().run()
list.kv
<ReorderableBoxLayout@KXReorderableBehavior+BoxLayout> <MainLayout> canvas.before: Color: rgba: .6, .7, .8, 1 Rectangle: pos: self.pos size: self.size BoxLayout: padding: '10dp' spacing: '10dp' ScrollView: id: left_box height: self.parent.height ReorderableBoxLayout: drag_classes: ['test',] orientation: 'vertical' padding: '5dp' spacing: '10dp' size_hint_y: None on_minimum_height: self.height = self.minimum_height Card: Card: ScrollView: id: center_box height: self.parent.height ReorderableBoxLayout: drag_classes: ['test',] orientation: 'vertical' padding: '5dp' spacing: '10dp' size_hint_y: None on_minimum_height: self.height = self.minimum_height Card: Card: ScrollView: id: right_box height: self.parent.height ReorderableBoxLayout: drag_classes: ['test',] orientation: 'vertical' padding: '5dp' spacing: '10dp' size_hint_y: None on_minimum_height: self.height = self.minimum_height Card: Card: <Card> drag_cls: 'test' drag_timeout: 0 text: 'Card' font_size: '30sp' size_hint_y: None canvas.before: Color: rgba: .5, .8, .5, 1 Rectangle: pos: self.pos size: self.size
问题详情
我设置了三个横向排列的ScrollView,每个里面都有可拖拽的Card组件。现在遇到的情况是:
- 正常工作的拖拽方向:
- 最右侧的ScrollView → 中间/左侧的ScrollView
- 中间的ScrollView → 左侧的ScrollView
- 完全无法工作的拖拽方向:
- 左侧的ScrollView → 中间/右侧的ScrollView
- 中间的ScrollView → 右侧的ScrollView
简单说就是只能从右往左拖,反过来拖的时候,目标ScrollView根本不会接收这个拖拽的Card,完全没反应。有没有大佬知道怎么解决这个问题?万分感谢!
解决思路与方案
这个问题大概率是**Widget的绘制层级(Z轴顺序)**导致的:Kivy中先添加到父布局的Widget会在下层,后添加的在上层。你的三个ScrollView是从左到右依次添加的,所以左侧的在最下层,右侧的在最上层。当你从左往右拖的时候,拖拽的Card其实是在右侧ScrollView的下方,导致右侧的ReorderableBoxLayout无法捕捉到拖拽事件。
分享两种亲测有效的解决方法:
方法1:拖拽开始时将Card移到所在布局的最上层
修改你的Card类,重写on_drag_start方法,在拖拽启动时把自己加到父布局的末尾(也就是最上层):
class Card(KXDraggableBehavior, Label): def on_drag_start(self, touch, ctx): super().on_drag_start(touch, ctx) # 将当前Card移到父布局的最上层 if self.parent: self.parent.add_widget(self)
这样每次拖拽开始时,这个Card都会被放到所在布局的最顶层,就能被右侧的ScrollView里的容器检测到了,左右拖拽都能正常工作。
方法2:给可接收拖拽的容器设置更高的层级
如果你不想修改Python代码,也可以在kv文件里给ReorderableBoxLayout设置zindex属性(Kivy 2.0及以上版本支持),提升它的事件优先级:
<ReorderableBoxLayout@KXReorderableBehavior+BoxLayout> zindex: 10 # 设置一个较高的数值,确保能优先捕捉到拖拽事件
这样每个可接收拖拽的容器都有更高的层级,不管从哪个方向拖拽,都能检测到拖拽的Card。
你可以先试试第一种方法,应该就能快速解决问题啦!
备注:内容来源于stack exchange,提问作者大橋和季

