Search code examples
pythonkivykivymd

Spinner at bottom of recyleview in kivy and kivymd


I want to user to see the spinner when they reach the bottom of the Recycleview.. In this bellow code the spinner is visible in the viewport this is the issue so there is some code below..

from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.scrollview import ScrollView
from kivy.uix.screenmanager import Screen

Builder.load_string('''
<MyScreen>:
    ScrollView:
        BoxLayout:
            orientation:"vertical"
            size_hint_y:None
            height:root.height
            RV:
                size_hint_y:None
                height:root.height
                viewclass: 'Button'
                RecycleBoxLayout:
                    default_size: None, dp(56)
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'
                    size_hint_y:None
                    height:self.minimum_height
            MDSpinner:
                size_hint:None,None
                width:dp(40)
                height:dp(20)
                pos_hint:{"center_x":.5}
                
    

''')

class MyScreen(Screen):
    pass


class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'text': str(x)} for x in range(25)]


class TestApp(MDApp):
    def build(self):
        return MyScreen()

if __name__ == '__main__':
    TestApp().run()

Thanks in advance..


Solution

  • If you want the last item in the RecycleView to be a spinner, you cannot do that with a viewclass of Button (unless you can make a Button look like a Spinner). However, you can design your own viewclass that can look like either a Button or a Spinner. So defining a custom viewclass like this:

    class MyViewClass(RelativeLayout):
        text = StringProperty('')  # text for the Button
        spinner = BooleanProperty(False)  # if True, show a Spinner instead
    

    Then add a rule for it in your kv:

    <MyViewClass>:
        Button:
            text: root.text
            opacity: 0 if root.spinner else 1
        MDSpinner:
            pos_hint:{"center_x":.5}
            opacity: 1 if root.spinner else 0
            active: True if root.spinner else False
            size_hint: None, None
            height: root.height
            width: root.height
    

    The above rule uses opacity to determine whether the Button or the Spinner is visible. And the active attribute of the MDSpinner is used to avoid having a spinning MDSpinner running in the items where it is not visible.

    Then just specify MyViewClass as the viewclass:

    viewclass: 'MyViewClass'
    

    And remove the old MDSpinner from your kv.

    The last thing to do is to adjust your data:

    class RV(RecycleView):
        def __init__(self, **kwargs):
            super(RV, self).__init__(**kwargs)
            self.data = [{'text': str(x), 'spinner': False} for x in range(25)]
            self.data.append({'text': '', 'spinner': True})
    

    The data now sets spinner to False for all the entries, and adds an extra entry with spinner set to True.