Search code examples
kivykivy-languagekivymdkivy-recycleview

Can't scroll to bottom using RecycleView


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 scrolling it just push me back on that screenshot

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.


Solution

  • 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.