具有可变小部件大小的回收视图不起作用(Kivy)

Recycle view with variable widget size not working (Kivy)

所以我有一个程序可以从服务器接收字符串并在将其添加到回收视图使用的列表之前对其进行格式化。当列表中的一个小部件的纹理大小发生变化时,会调用一个函数,因为它们的大小可能因内容而异。但是,每当添加更多项目时,回收视图就会完全出错并且所有项目都会重叠。

行为差异很大,所以我无法包含问题的每个示例,但我已经从我的程序中取出相关代码并制作了一个小示例程序,我将在下面包含它。我还添加了一些问题的图片。

我一直无法让它工作,也找不到任何有类似类似问题的人。任何帮助或链接将不胜感激,提前致谢

main.py

from kivy.app import App
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout

example_string = 'test)£++£(0)+££+(test)£++£(0)+££+(test)£++£(0)+££+(test)£++£(0)+££+(test)£++£(0'

class MASTER(BoxLayout):
    def example_button(self, Button): # Demo of list updating
        global example_string # I know
        example_string += ')+££+(test)£++£(0'
        temp = []
        id_num = 0
        for post in example_string.split(')+££+('): # The string is an example of the input data
            temp.append({'message_id':id_num, 'text':('[font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\n' + post.split(')£++£(')[0]), '_size':[0,0], '_group':str(id_num), '_score':int(post.split(')£++£(')[1])})
            id_num = id_num + 1
        App.get_running_app().posts = temp

class DemoApp(App):
    # One post format = {'message_id':0, 'text':'post_test_here','_size':[0,0], '_group':str(0), '_score':20}
    # Text fromat string = [font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\n
    posts = ListProperty([{'message_id':0, 'text':'[font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\nHello, this is a test of a post jhghjgjhgh','_size':[0,0], '_group':str(0), '_score':20}, {'message_id':1, 'text':'[font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\nHello, this is a test of a post hghghghghghghg', '_size':[0,0], '_group':str(1), '_score':100}, {'message_id':2, 'text':'post_test_her\n\ne','_size':[0,0], '_group':str(2), '_score':20}])

    def update_message_size(self, message_id, texture_size):
        self.posts[message_id] = {**self.posts[message_id], '_size':[1, texture_size[1]]}
        print('Message ID = ' + str(message_id))
        print('Texture size = ' + str(texture_size))

    def up_vote(self, button, mode): # Not part of the problem
        if button.state == 'down':
            if mode == 'all':
                print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
            else:
                print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')
    
    def down_vote(self, button, mode): # Not part of the problem
        if button.state == 'down':
            if mode == 'all':
                print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
            else:
                print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')





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

demo.kv

MASTER:

<MASTER>:
    Button:
        text: 'Add items'
        on_press: root.example_button(self)
    RecycleView:
        viewclass: 'PostGrid'
        scroll_y: 1
        id: rv
        data: app.posts
        canvas.before:
            Color:
                rgba: 0, 0, 0, 1
            Rectangle:
                pos: self.pos
                size: self.size
        RecycleBoxLayout:
            id: box
            default_size_hint: 1, None
            size_hint_y: None
            padding: ["10dp", "16dp"]
            spacing: "20dp"
            height: self.minimum_height
            orientation: 'vertical'
            key_size: '_size'

<PostGrid@BoxLayout>:
    message_id: -1
    orientation: "horizontal"
    text: ''
    _group: ''
    _score: 0
    spacing: "6dp"
    text_size: None, None
    BoxLayout:
        id: voting_menu
        orientation: "vertical"
        spacing: "2dp"
        size_hint: .2, 1
        size: self.size
        ToggleButton:
            id: button_up
            on_state: app.up_vote(self, 'all')
            group: str(root._group)
            text: "UP"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: .2,.2,.2,1
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
        Label:
            id: vote_count
            text: str(root._score)
            size_hint: 1, .4
            multiline: False

        ToggleButton:
            id: button_down
            on_state: app.down_vote(self, 'all')
            group: str(root._group)
            text: "DOWN"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: (.2,.2,.2,1)
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
    Label:
        text: root.text
        padding: "10dp", "12dp"
        size_hint: .9, 1
        height: self.texture_size[1]
        font_size: "12dp"
        text_size: self.width, None
        color: 0,0,0,1
        multiline: True
        markup: True
        
        on_texture_size: app.update_message_size(root.message_id, self.texture_size)

        pos: self.pos

        canvas.before:
            Color:
                rgba: (0.8, 0.8, 0.8, 1)
            RoundedRectangle:
                size: self.texture_size
                radius: [5, 5, 5, 5]
                pos: self.x, self.y
        canvas:
            Color:
                rgba:0.8,0,0,1
            Line:
                width: 1.4
                rounded_rectangle:(self.x,self.y,self.width,self.height, 5)

意外行为图片 不同意外行为的图片 问题视频 https://youtu.be/F_2TLh-cFYA

备注 我会鼓励任何人 运行 此代码以完全理解问题,因为代码实际上在我第一次更改存储在列表 'posts'

中的内容时按预期运行

如何从内部设置标签 size 而不是使用 key_size 从外部设置标签。

您可以使用 minimum_height 保持固定高度的 viewclass 小部件,并通过标签的纹理高度控制其子小部件。

因此,您修改后的 PostGridkvlang 中的相关代码将如下所示,

        RecycleBoxLayout:
            id: box
            default_size_hint: 1, None
            default_size: None, dp(50) #
            size_hint_y: None
            padding: ["10dp", "16dp"]
            spacing: "20dp"
            height: self.minimum_height
            orientation: 'vertical'
#            key_size: '_size'
# Remove the associated prop. from python (i.e. from method 'example_button' etc.) as well.

<PostGrid@BoxLayout>:
    message_id: -1
    orientation: "horizontal"
    text: ''
    _group: ''
    _score: 0
    spacing: "6dp"
    text_size: None, None
    size_hint_y: None
    height: self.minimum_height
    BoxLayout:
        id: voting_menu
        orientation: "vertical"
        spacing: "2dp"
        size_hint: .2, None
        height: label.height # This binding will force voting_menu to resize.
#        size: self.size # I don't think it has any effect.
        ToggleButton:
            id: button_up
            on_state: app.up_vote(self, 'all')
            group: str(root._group)
            text: "UP"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: .2,.2,.2,1
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
        Label:
            id: vote_count
            text: str(root._score)
            size_hint: 1, .4
            multiline: False

        ToggleButton:
            id: button_down
            on_state: app.down_vote(self, 'all')
            group: str(root._group)
            text: "DOWN"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: (.2,.2,.2,1)
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
    Label:
        id: label # For reference.
        text: root.text
        padding: "10dp", "12dp"
        size_hint: .9, None
        height: self.texture_size[1]
        font_size: "12dp"
        text_size: self.width, None
        color: 0,0,0,1
        multiline: True
        markup: True
        
#        on_texture_size: app.update_message_size(root.message_id, self.texture_size)

#        pos: self.pos

        canvas.before:
            Color:
                rgba: (0.8, 0.8, 0.8, 1)
            RoundedRectangle:
                size: self.texture_size
                radius: [5, 5, 5, 5]
                pos: self.x, self.y
        canvas:
            Color:
                rgba:0.8,0,0,1
            Line:
                width: 1.4
                rounded_rectangle:(self.x,self.y,self.width,self.height, 5)

或者#您可以使用ScrollView滚动扩展标签。但是这里每个 viewclass 小部件都将具有相同的高度。此外,由于内部小部件中存在 ScrollView,您必须从一侧滚动小部件(这很常见)或根据 scroll_y 值自行操作触摸调度事件。

MASTER:

<MASTER>:
    orientation: "vertical"
    Button:
        size_hint_y: 0.15
        text: 'Add items'
        on_press: root.example_button(self)
    RecycleView:
        viewclass: 'PostGrid'
        scroll_y: 1
        id: rv
        data: app.posts
        canvas.before:
            Color:
                rgba: 0, 0, 0, 1
            Rectangle:
                pos: self.pos
                size: self.size
        RecycleBoxLayout:
            id: box
            default_size_hint: 1, None
            default_size: None, dp(64) # Specify it explicitly. Adjust to your need.
            size_hint_y: None
            padding: ["10dp", "16dp"]
            spacing: "20dp"
            height: self.minimum_height
            orientation: 'vertical'


<PostGrid@BoxLayout>:
    message_id: -1
    orientation: "horizontal"
    text: ''
    _group: ''
    _score: 0
    spacing: "6dp"
    text_size: None, None
    size_hint_y: None
    height: dp(100) # Adjust to your need.
    BoxLayout:
        id: voting_menu
        orientation: "vertical"
        spacing: "2dp"
        size_hint_x: 0.2
        ToggleButton:
            id: button_up
            on_state: app.up_vote(self, 'all')
            group: str(root._group)
            text: "UP"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: .2,.2,.2,1
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
        Label:
            id: vote_count
            text: str(root._score)
            size_hint: 1, .4
            multiline: False

        ToggleButton:
            id: button_down
            on_state: app.down_vote(self, 'all')
            group: str(root._group)
            text: "DOWN"
            color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
            font_size: "10dp"
            size_hint: 1, .3
            background_color: .2, .2, .2, 0
            canvas.before:
                Color:
                    rgba: (.1,.1,.1,1)
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [6,]
            canvas:
                Color:
                    rgba: (.2,.2,.2,1)
                Line:
                    width: 1.4
                    rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
    ScrollView:
        effect_cls: "ScrollEffect"
        canvas.before:
            Color:
                rgba: (0.8, 0.8, 0.8, 1)
            RoundedRectangle:
                size: self.size
                radius: [5, 5, 5, 5]
                pos: self.pos
            Color:
                rgba:0.8,0,0,1
            Line:
                width: 1.4
                rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
        
        Label:
            text: root.text
            padding: "10dp", "12dp"
            size_hint_y: None
            height: self.texture_size[1]
            font_size: "12dp"
            text_size: self.width, None
            color: 0,0,0,1
            multiline: True
            markup: True

#这是对这个的回应。