KivyMD RecycleView网格布局中CheckBox点击状态重复触发问题
问题成因
- RecycleView的核心逻辑是视图复用:它只会创建刚好覆盖屏幕可见区域的少量GridTile视图实例,当用户滑动列表时,会把滑出屏幕的旧视图直接复用给新滑入屏幕的条目。你当前的代码没有将CheckBox的选中状态和RecycleView绑定的
data数据集做关联,视图复用时就会把旧视图上的CheckBox状态直接带到新条目上,看起来就是其他条目同步触发了选中/取消操作。 - 你定义的
data列表里只有tile字段,没有为每个条目预留存储选中状态的字段,无法在视图绑定时还原当前条目的正确选中状态,导致状态完全混乱。
修复步骤
- 给
data数据集的每个条目新增is_checked字段,用来持久化存储对应条目的选中状态 - 给
GridTile新增index属性,用来标记当前视图对应data列表的下标 - 调整CheckBox的
active事件绑定逻辑,触发时更新data列表对应下标的is_checked值,同时在视图复用时主动根据data的is_checked值更新CheckBox状态
修复后完整可运行代码
from kivy.lang import Builder from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.recycleview import RecycleView from kivy.properties import StringProperty, ListProperty, NumericProperty, BooleanProperty from kivy.clock import Clock from kivymd.app import MDApp from kivymd.uix.imagelist import SmartTile from kivymd.uix.selectioncontrol import MDCheckbox Builder.load_string(""" <Check>: check_active: root.check_active on_check_active: self.check.active = root.check_active <GridTile>: tile: root.tile index: root.index check_active: root.check_active SmartTile: source: root.tile size_hint_y: None height: '150dp' Check: check_active: root.check_active parent_index: root.index <GridScreen>: name: 'grid_screen' RV: id: rv viewclass: 'GridTile' RecycleGridLayout: cols: 2 size_hint_y: None default_size: 1, dp(150) default_size_hint: 1, None height: self.minimum_height """) class GridTile(Screen): tile = StringProperty('') index = NumericProperty(-1) check_active = BooleanProperty(False) class GridScreen(Screen): pass class RV(RecycleView): data = ListProperty([]) def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) self.cell_data() def cell_data(self): self.data = [{"tile": 'The Beatles', "is_checked": False, "index": i} for i in range(41)] class Check(SmartTile): check_active = BooleanProperty(False) parent_index = NumericProperty(-1) def __init__(self, **kwargs): super().__init__(**kwargs) Clock.schedule_once(self.add_checkbox) def add_checkbox(self, interval): app = MDApp.get_running_app() self.check = MDCheckbox(size_hint=(None, None), size=(48, 48), active=self.check_active) self.check.bind(active=self.on_check_active) self._box_overlay.add_widget(self.check) def on_check_active(self, checkbox, value): app = MDApp.get_running_app() if self.parent_index >= 0: app.root.get_screen('grid_screen').ids.rv.data[self.parent_index]['is_checked'] = value app.on_checkbox_active(checkbox, value, self.parent_index) class ThisApp(MDApp): def __init__(self, **kwargs): super().__init__(**kwargs) def build(self): self.sm = ScreenManager() self.sm.add_widget(GridScreen(name='grid_screen')) return self.sm def on_checkbox_active(self, checkbox, value, index): if value: print(f'第{index}个条目的checkbox', checkbox, 'is active', 'and', checkbox.state, 'state') else: print(f'第{index}个条目的checkbox', checkbox, 'is inactive', 'and', checkbox.state, 'state') if __name__ == "__main__": ThisApp().run()
内容的提问来源于stack exchange,提问作者Henry Thornton
相关产品推荐
相关产品推荐

