I am using RecycleView for displaying a large number of data in sql. However the problem that i encounter is i can't scroll to the bottom. It will just push back a little bit higher but i can't scroll to the end. Here is the snippet of codes:
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):
# Your properties and methods
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='') # Add this line
sent_picture = NumericProperty(0)
index = 0
def update_card_height(self, *args):
total_height = 0
for child in self.children:
if child.size_hint_y is None:
total_height += child.height
self.height = total_height
# Force the RecycleView to refresh the view
if self.parent and hasattr(self.parent, 'refresh_from_data'):
self.parent.refresh_from_data()
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 = self.remarks = data.get('created_at', '')
self.sent_at = data.get('sent_at', '') or ''
self.sent_picture = int(data.get('event_id', 0))
super(CustomCard, self).refresh_view_attrs(rv, index, data)
Clock.schedule_once(self.update_card_height)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
# Only call on_release if the touch was actually within the card
if touch.is_mouse_scrolling:
return False # Ignore scroll events
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)
app class:
class MainFunction(MDApp):
def add_main_widgets(self):
def fetch_data_thread():
events_data = self.fetch_data_from_db1()
self.update_ui_with_data(events_data)
self.cleanup_expired_events()
Thread(target=fetch_data_thread).start()
@mainthread
def update_ui_with_data(self, events_data):
event_buttons = self.sm.get_screen('main').ids.bottom_nav
card_list = self.sm.get_screen('main').ids.card_list
current_data = {card['event_id']: card for card in card_list.data}
self.original_data = [] # Store original data here
for event in events_data:
event_id = event.id
# Prepare the data for each card
card_data = {
'event_id': event_id,
'widget_count': 0,
'total_count': 0,
'event_type': event.event_type,
'reference_number': event.reference_number,
'remarks': event.remarks,
'image_count': event.image_count,
'latitude': event.latitude,
'longitude': event.longitude,
'created_at': event.created_at,
'sent_at': event.sent_at or '',
'sent_picture': event.sent_picture
}
# Add or update card data
if event_id in self.cards_dict:
self.cards_dict[event_id].update(card_data)
else:
self.cards_dict[event_id] = card_data
card_list.data.insert(0, card_data) # Add to card_list data
# Store the original data as the initial state
self.original_data = list(card_list.data)
# Calculate total count and update each card
total_count = len(card_list.data)
for index, card_data in enumerate(card_list.data):
card_data['widget_count'] = index + 1
card_data['total_count'] = total_count
event_buttons.action_items = [
MDActionBottomAppBarButton(icon="send-circle", on_release=lambda x: self.event_transfer()),
MDActionBottomAppBarButton(icon="image", on_release=lambda x: self.file_transfer()),
]
kv file:
<CustomCard>:
spacing: dp(8)
padding: dp(8)
# Add a canvas for background color
canvas.before:
Color:
rgba: (228/255, 225/255, 232/255, 1) # Set your desired background color here (e.g., white)
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:
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] # Add padding at the top to move the content down
TextInput:
id: search_bar
hint_text: "Search..."
height: '40dp' # Set a fixed height
size_hint_y: None # Disable size_hint_y to use fixed height
pos_hint: {"center_x": .5, "center_y": .5}
on_text: app.filter_cards(self.text)
width: '376dp' # Set a fixed width
size_hint_x: None # Disable size_hint_x to use fixed width
RecycleView:
id: card_list
pos_hint: {"center_y": 0.3}
size_hint_y: 1
viewclass: "CustomCard"
#effect_cls: "ScrollEffect"
do_scroll_x: False
RecycleBoxLayout:
orientation: 'vertical'
padding:dp(15)
spacing: dp(15)
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
default_size: 0, 36
default_size_hint: 1, None
key_size: 'cached_size'
MDBottomAppBar:
id: bottom_nav
#scroll_cls: card_list
#allow_hidden: True
theme_bg_color: "Custom"
MDFabBottomAppBarButton:
icon: "plus"
theme_bg_color: "Custom"
md_bg_color: "#FFF200"
theme_text_color: "Custom"
text_color: "black"
on_release: app.new_event()
I still can't figure out. This only happens when i switch to RecycleView.
I can only see the bottom items when i force to scroll it but in normal scroll it push me back on that screenshot. Can't see the 12 of 13 and 13 of 13 items.
It looks like the MDBoxLayout
is overlapping with the MDBottomAppBar
. An easy fix is to just add one level of indentation to the MDBottomAppBar
, which adds it to the MDBoxLayout
. Otherwise, you need to carefully set things like size_hint
and pos_hint
to avoid overlaps.