Kivy RecycleView可拖拽行位置计算bug求助
RecycleView拖拽行功能适配问题
由于BoxLayout行数存在限制,我尝试在RecycleView中实现可拖拽行功能,参考相关示例完成了基础实现,但适配RecycleView时遇到核心问题:元素位置计算逻辑存在bug,例如列表有20条数据但屏幕仅显示10条时会出现异常。我已禁用RecycleView的拖拽滚动,相关实现代码如下:
from kivy.app import App from kivy.lang import Builder from kivy.uix.boxlayout import BoxLayout from kivy.properties import StringProperty, ListProperty, NumericProperty from kivy.uix.recycleview import RecycleView from kivy.metrics import dp from kivy.uix.behaviors import DragBehavior kv = """ <DraggableRow>: canvas: Color: rgba: root.highlight_color Rectangle: size: self.size pos: self.pos padding: 20, 0 Label: text: root.song Label: text: root.album Label: text: root.artist Button: size_hint_x: None width: 80 text: 'Delete' on_release: root.delete_song() TestBox: orientation: 'vertical' TextInput: size_hint_y: None height: 48 hint_text: 'Enter Song Name' multiline: False on_text: root.highlight_song(self.text) Label: text: 'Song List' size_hint_y: .1 TouchRecycleView: id: rv viewclass: 'DraggableRow' data: root.rv_data_list scroll_type: ['bars'] scroll_distance: 0 bar_inactive_color: 1, 1, 1, 1 bar_width: dp(20) do_scroll_x: False RecycleBoxLayout: id: rbl key_selection: 'song' default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical' """ class DraggableRow(DragBehavior, BoxLayout): song = StringProperty() album = StringProperty() artist = StringProperty() highlight_color = ListProperty() index = NumericProperty() drag_touch = None def __init__(self, **kwargs): super(DraggableRow, self).__init__(**kwargs) self.test_box = None self.original_index = 0 self.drag_touch = None def on_parent(self, instance, value): if value is not None: self.test_box = instance def on_touch_down(self, touch): if self.collide_point(*touch.pos) and not self.drag_touch: self.drag_touch = touch self.original_index = self.parent.children.index(self) return super(DraggableRow, self).on_touch_down(touch) return super(DraggableRow, self).on_touch_down(touch) def on_touch_move(self, touch): if touch == self.drag_touch: self.y = touch.y - self.height / 2 # 找到TestBox父容器 test_box = self.parent while not isinstance(test_box, TestBox): test_box = test_box.parent # 拖拽时更新列表位置 target_index = round(self.y / self.height) target_index = max(0, min(target_index, len(test_box.rv_data_list) - 1)) if self.original_index < target_index: target_index -= 1 #print(f"original_index: {self.original_index}, target_index: {target_index}, y: {self.y}") if target_index != self.original_index: test_box.move_element(self.original_index, target_index) self.original_index = target_index def on_touch_up(self, touch): if touch == self.drag_touch: test_box = self.parent while not isinstance(test_box, TestBox): test_box = test_box.parent test_box.move_element(self.original_index, self.index) self.drag_touch = None return True return super(DraggableRow, self).on_touch_up(touch) def delete_song(self): app = App.get_running_app() rv = app.root.ids.rv del rv.data[self.index] for i, _ in enumerate(rv.data): rv.data[i]['index'] = i rv.refresh_from_data() class TestBox(BoxLayout): elements = [{'song': f'song_{n}', 'album': f'album_{n}', 'artist': f'artist_{n}', 'index': n, 'highlight_color': [0, 0, 0, 1]} for n in range(7)] rv_data_list = ListProperty(elements) def highlight_song(self, song): for track in self.rv_data_list: track['highlight_color'] = (.8, .8, .8, 1) if song == track['song'] else (0, 0, 0, 1) self.ids.rv.refresh_from_data() def move_element(self, original_index, target_index): # 将元素从original_index移动到target_index element = self.rv_data_list.pop(original_index) self.rv_data_list.insert(target_index, element) #print(f"data: {self.rv_data_list}") # 更新所有元素的index值 for i, element in enumerate(self.rv_data_list): element['index'] = i # 刷新RecycleView self.ids.rv.refresh_from_data() class TouchRecycleView(RecycleView): pass class RVTestApp(App): def build(self): return Builder.load_string(kv) RVTestApp().run()
内容的提问来源于stack exchange,提问作者kobiiiiii
相关产品推荐
相关产品推荐

