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

Kivy技术问题:如何通过坐标获取RecycleView项及分辨率适配?

场景

我有一个三列布局的窗口,第二列是包含20个项的RecycleView。当用户将文件拖放到RecycleView上时,我需要获取对应的数据源项。我仅需判断文件被拖放到了RecycleView的哪个项上,类似父级分发on_touch事件时的需求;虽然项可自行处理事件,但on_file_drop绑定在Window类上,因此必须向下查找被命中的RV项。

演示代码

<PageThumb>:
    size_hint: 1, None
    orientation: 'vertical'
    Image:
        id: imgThumbnail
        size_hint: 1, None
        height: 300
    Label:
        id: lblPageNr
        size_hint: 1, None
        height: 20

GridLayout:
    cols: 3
    id: mainGrid
    BoxLayout:
        size_hint: 0.4, 1
        orientation: 'vertical'
        Label:
            text: '1st column'
    RecycleView:
        size_hint: 0.2, 1
        id: rvMain
        viewclass: 'PageThumb'
        RecycleBoxLayout:
            padding: 5
            spacing: 20
            default_size: None, 320
            default_size_hint: 1, None
            size_hint_y: None
            height: self.minimum_height
            orientation: 'vertical'
    BoxLayout:
        size_hint: 0.4, 1
        id: layoutPreview
        Label:
            text: '3rd column'
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty

class PageThumb(BoxLayout):
    page_nr = NumericProperty(None)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
    
    def on_page_nr(self, obj, page_nr):
        obj.ids.lblPageNr.text = str(page_nr+1)

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            # touch is inside this widget
            app = App.get_running_app()
            print(f"touched page_nr={self.page_nr}")

class DemoApp(App):

    def getSystemSizeFromScaledSize(self, win, scaledX, scaledY):
        scaleX = win.size[0] / win.system_size[0]
        scaleY = win.size[1] / win.system_size[1]

        systemX = scaledX * scaleX;
        systemY = scaledY * scaleY;
    
        return (systemX, systemY)

    def on_drop_file(self, win, bFileName, x, y):
  
        fileName = bFileName.decode("utf-8")

        xS, yS = self.getSystemSizeFromScaledSize(win, x, y)

        if self.root.ids.rvMain.collide_point(xS, yS):
            yL = self.root.ids.rvMain.layout_manager.size[1] - yS

            # pos = self.root.ids.rvMain.to_local(xS, yL)
            ndx = self.root.ids.rvMain.layout_manager.get_view_index_at(pos=(xS, yL))

            page = list(self.root.ids.rvMain.layout_manager.view_indices)[ndx]

            print(f"d&d ndx={ndx} page_nr={page.page_nr}")
    
    def build(self):
        from kivy.core.window import Window
        Window.bind(on_drop_file = self.on_drop_file)

        pages = ({"page_nr": i} for i in range(0, 20))
        self.root.ids.rvMain.data = pages

DemoApp().run()

问题

  • 如何通过on_file_drop事件中获取的屏幕x/y坐标,从RV中找到对应的项?
  • 我基于分辨率密度的碰撞检测转换是否正确?目前仅在Windows平台测试,是否仅需在此平台处理?在Kivy文档中未找到相关提示。

我的分析

  • 问题核心是处理on_file_drop事件中的屏幕坐标,由于Windows桌面使用250%缩放,发现必须转换坐标才能让collide_point正确检测,类似on_touch事件的处理逻辑。
  • 认为layout_manager.get_view_index_at(pos)也需应用密度计算,但尝试RecycleView及其layout_manager的多种to_local()/to_widget()组合均未得到正确结果。
  • 尝试操作layout_manager.view_indices和children等属性也未成功。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 20:13:16