使用RecycleView无法滚动至底部的技术求助
Kivy RecycleView滚动回弹无法到底部问题排查与修复
问题描述
使用Kivy的RecycleView展示SQL数据库数据时,出现滚动异常:无法正常滚动至列表底部,滚动时会自动回弹至上方位置,仅能通过强制滚动才能查看末尾的第12/13和13/13项,该问题仅在切换为RecycleView后出现。
问题根源分析
从代码逻辑来看,问题集中在RecycleView的高度计算与动态更新逻辑上:
CustomCard中手动计算高度后调用parent.refresh_from_data(),触发RecycleView频繁刷新,导致滚动位置被强制重置RecycleBoxLayout设置了default_size: 0,36,但卡片实际高度远大于该值,导致RecycleView整体高度计算错误- 卡片高度动态更新时未同步到RecycleView的数据中,
key_size指定的cached_size字段未被正确维护
修复方案与代码调整
1. 调整CustomCard类逻辑
移除手动计算高度的冗余代码,通过绑定内部布局自动计算高度,并同步高度到RecycleView数据中:
class MyBoxLayout(BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) self.orientation = "vertical" self.size_hint_y = None self.bind(minimum_height=self.setter('height')) class CustomCard(BoxLayout, RecycleDataViewBehavior): fixed_height = NumericProperty(100) event_id = NumericProperty() widget_count = NumericProperty() total_count = NumericProperty() event_type = StringProperty() reference_number = StringProperty() remarks = StringProperty() image_count = NumericProperty(0) latitude = NumericProperty() longitude = NumericProperty() image_path = StringProperty(defaultvalue='') created_at = StringProperty() sent_at = StringProperty(defaultvalue='') sent_picture = NumericProperty(0) index = 0 def __init__(self, **kwargs): super().__init__(**kwargs) self.size_hint_y = None # 绑定内部布局的最小高度到卡片自身高度 self.ids.inner_box.bind(minimum_height=self.setter('height')) def refresh_view_attrs(self, rv, index, data): self.index = index self.event_id = int(data.get('event_id', 0)) self.widget_count = data.get('widget_count', 0) self.event_type = data.get('event_type', '') self.reference_number = data.get('reference_number', '') self.remarks = data.get('remarks', '') self.image_count = data.get('image_count', 0) self.latitude = float(data.get('latitude', 0.0)) self.longitude = float(data.get('longitude', 0.0)) self.image_path = data.get('image_path', '') self.created_at = data.get('created_at', '') self.sent_at = data.get('sent_at', '') or '' self.sent_picture = int(data.get('sent_picture', 0)) super(CustomCard, self).refresh_view_attrs(rv, index, data) # 延迟同步卡片高度到RecycleView数据 Clock.schedule_once(lambda dt: self.update_cached_size(rv)) def update_cached_size(self, rv): # 将卡片实际高度存入对应数据项,供RecycleView计算总高度 if rv.data and self.index < len(rv.data): rv.data[self.index]['cached_size'] = (0, self.height) rv.refresh_from_data() def on_touch_up(self, touch): if self.collide_point(*touch.pos): if touch.is_mouse_scrolling: return False self.on_release() return True return super().on_touch_up(touch) def on_release(self): app = MDApp.get_running_app() print(f"Event ID on release: {self.event_id}") app.display_event_details(self.event_id)
2. 修正KV文件布局
为内部布局添加ID用于高度绑定,移除RecycleBoxLayout的错误默认高度设置:
<CustomCard>: spacing: dp(8) padding: dp(8) canvas.before: Color: rgba: (228/255, 225/255, 232/255, 1) Rectangle: size: self.size pos: self.pos MDFloatLayout: size_hint: .01, 1 pos_hint: {"center_x": .02, "center_y": .5} md_bg_color: "green" if root.sent_at else ("yellow" if root.latitude and root.longitude else "#EC1F26") MyBoxLayout: id: inner_box # 添加ID用于高度绑定 spacing: dp(1) Label: text: f"[{root.widget_count} of {root.total_count}]: {root.reference_number}" font_size: "15sp" color:"black" bold: True size_hint_y: None height: self.texture_size[1] text_size: self.width, None Label: text: "Remarks:" bold: True font_size:"14sp" color:"#666666" size_hint_y: None height: self.texture_size[1] if root.remarks else 0 text_size: self.width, None opacity: 1 if root.remarks else 0 Label: text: root.remarks if root.remarks else "<None>" font_size:"14sp" color: (0, 0, 0, 1) size_hint_y: None height: self.texture_size[1] if root.remarks else 0 text_size: self.width, None opacity: 1 if root.remarks else 0 MDButton: style: 'text' height: "15dp" if root.image_count > 0 else 0 size_hint_x: .3 opacity: 1 if root.image_count > 0 else 0 MDButtonIcon: icon: 'image' x: 1 theme_icon_color: "Custom" icon_color: "black" MDButtonText: text: f"{root.image_count} Image(s) sent" if root.sent_picture == 1 else (f"{root.image_count} Image(s)" if root.image_count > 0 else "0 Image") theme_text_color: "Custom" text_color: "black" font_style: "Body" role: "medium" Label: text: f"Transmitted successfully: [color=#008000]{root.sent_at}[/color]" if root.sent_at else ("Status: Ready to transmit" if (root.latitude and root.longitude) else "Status: Location not found") bold: True font_size: "12sp" color: "#666666" size_hint_y: None height: self.texture_size[1] text_size: self.width, None markup: True AnchorLayout: anchor_x: 'right' anchor_y: 'top' size_hint: None, None size: 100, 50 pos_hint: {'right': 0.9, 'top': 1} padding: [0, 5] Label: text: root.event_type font_name: "f2icons/status.ttf" color: "#ffff00" font_size: "15sp" MDScreen: on_enter: app.set_bars_colors("#FFF200", "#FFFFFF", "Dark") name: 'main' md_bg_color: "#FFF200" MDNavigationLayout: MDScreenManager: MDScreen: MDBoxLayout: orientation: 'vertical' pos_hint: {"center_y": 0.36} padding: [7, 10, 0, 0] TextInput: id: search_bar hint_text: "Search..." height: '40dp' size_hint_y: None pos_hint: {"center_x": .5, "center_y": .5} on_text: app.filter_cards(self.text) width: '376dp' size_hint_x: None RecycleView: id: card_list pos_hint: {"center_y": 0.3} size_hint_y: 1 viewclass: "CustomCard" do_scroll_x: False RecycleBoxLayout: orientation: 'vertical' padding:dp(15) spacing: dp(15) size_hint_y: None height: self.minimum_height default_size_hint: 1, None key_size: 'cached_size'
3. 关键修复点说明
- 移除手动计算高度的
update_card_height方法,通过绑定内部布局minimum_height自动计算卡片高度,避免计算误差 - 添加
update_cached_size方法,将卡片实际高度同步到RecycleView的数据项中,让RecycleView能准确计算列表总高度 - 修正
refresh_view_attrs中的笔误(原代码中self.created_at = self.remarks = ...的错误赋值) - 删除
RecycleBoxLayout中错误的default_size: 0,36设置,避免与实际卡片高度冲突
内容的提问来源于stack exchange,提问作者nathan
相关产品推荐
相关产品推荐

