首页 > 解决方案 > 当窗口调整大小时,Kivy recycleview 项目会重新排序

问题描述

我使用 Kivy'srecycleview以类似表格的方式显示数据列表。我使用文档中的示例作为实现的基础。

在我的程序中,RecycleDataView 基于 BoxLayout,其子小部件是动态生成的。

这似乎可行,但有时显示项目的顺序会颠倒,并且如果您调整窗口大小,则会不断变化。更糟糕的是,如果你向下滚动,布局会变得非常疯狂。如果我使用简单的标签作为项目类,则不会发生这种情况,所以我想问题出在我的动态创建的小部件逻辑上,但我不明白为什么。

这是一些显示问题的最小代码。

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty

Builder.load_string('''

<RV>:
    viewclass: 'RVItem'
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

''')

class Attribute:
    def __init__(self, name, values):
        self.name = name
        self.values = values


class RVItem(RecycleDataViewBehavior, BoxLayout):
    index = None
    attribute = ObjectProperty()

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        self.create_widgets(data.pop('attribute', None))
        return super(RVItem, self).refresh_view_attrs(
            rv, index, data)

    def create_widgets(self, value: Attribute):
        """Dynamically create the needed Widgets"""

        if value is None:
            return
        self.add_widget(Label(text=value.name, height=self.height, size_hint=(1, None)))

        if not isinstance(value.values, dict):
            self.add_widget(Label(text=value.values, height=self.height, size_hint=(1, None)))
        else:
            for _, v in value.values.items():
                self.add_widget(Label(text=v, height=self.height, size_hint=(1, None)))

        image_button = Button(text='+')
        #image_button.source = 'wm_ui/glyphs/plus.png'
        image_button.size_hint = None, None
        image_button.size = "30sp", "30sp"
        image_button.bind(on_press=self.add_button_pressed)
        self.add_widget(image_button)

    def add_button_pressed(self, s):
        print("Would add a new item to the recycleview if implemented.")


class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'attribute': Attribute(str(x), "test")} for x in range(100)]


class TestApp(App):
    def build(self):
        return RV()

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

为了简单起见,我将一些小部件更改为它们的基类(如 ImageButton 和 Label)

当您运行应用程序时,您应该看到如果项目被颠倒并且出于某种原因以 10 而不是 100 开头的顺序。

启动的应用程序

在窗口的一个角落用鼠标调整窗口大小后,您应该会看到内容不断重新排序。

稍微调整窗口大小后

如果你向下滚动,事情会变得更加疯狂。

向下滚动列表后

不幸的是,我不知道是什么导致了这种行为。我之前开发了一些 Kivy 应用程序,但这是我第一次真正深入研究,不仅仅使用标签和一些输入。

标签: pythonkivy

解决方案


这是对您的代码的修改,似乎可以正常工作:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty

Builder.load_string('''

<RV>:
    viewclass: 'RVItem'
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

''')

class Attribute:
    def __init__(self, name, values):
        self.name = name
        self.values = values


class RVItem(RecycleDataViewBehavior, BoxLayout):
    index = None
    attribute = ObjectProperty()

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        self.create_widgets(data['attribute'])
        return super(RVItem, self).refresh_view_attrs(
            rv, index, data)

    def create_widgets(self, value: Attribute):
        rv = App.get_running_app().root
        rv.cache_widgets(self.children)
        self.clear_widgets()
        label = rv.get_label()
        label.text = value.name
        self.add_widget(label)
        if isinstance(value.values, dict):
            for _,v in value.values.items():
                label = rv.get_label()
                label.text = v
                self.add_widget(label)
        else:
            label = rv.get_label()
            label.text = value.values
            self.add_widget(label)
        image_button = rv.get_button()
        image_button.text = '+'
        image_button.size_hint = None, None
        image_button.size = "30sp", "30sp"
        image_button.bind(on_press=self.add_button_pressed)
        self.add_widget(image_button)


    def add_button_pressed(self, s):
        print("Would add a new item to the recycleview if implemented.")


class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.label_cache = []
        self.button_cache = []
        self.data = [{'attribute': Attribute(str(x), "test")} for x in range(100)]
        for i in range(100):
            if i % 5 == 0:
                self.data[i]['attribute'].values = {'1': 'test1', '2': 'test2', '3': 'test3'}

    def get_button(self):
        if len(self.button_cache) > 0:
            return self.button_cache.pop()
        else:
            return Button()

    def get_label(self):
        if len(self.label_cache) > 0:
            return self.label_cache.pop()
        else:
            return Label()

    def cache_widgets(self, widgets):
        for w in widgets:
            if isinstance(w, Button):
                self.button_cache.append(w)
            else:
                self.label_cache.append(w)


class TestApp(App):
    def build(self):
        return RV()

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

在这个版本中,该refresh_view_attrs方法总是设置RVItem. 线

self.create_widgets(data.pop('attribute', None))

被替换为

self.create_widgets(data['attribute'])

因为pop()实际上删除了数据,我认为您不想这样做。

该类RV现在有一个用于Label小部件的缓存和另一个用于Button小部件的缓存,并且它们被回收(类似于所做的事情RecycleView。该create_widgets方法删除所有子级RVItem并将它们添加到缓存中,然后根据需要回收或创建小部件以填充出RVItem.

values为某些数据添加了额外的项目,以帮助说明这是如何工作的。


推荐阅读