Wednesday, 3 March 2021

Kivy Android app crashes when changing BoxLayout sizes and scrolling at the same time?

I have developed an Android app using the Python framework Kivy. The layout is divided in two parts using BoxLayouts ("upper_space" and "lower_space"). As seen on the following screenshot (sorry for the huge size):

enter image description here

User Scenario:

The user scrolls in the "lower_space" area and hits the button on the "upper_space" area at the same time (multitouch). The app crashes.

The button changes both BoxLayout's height. How can we prevent the app from crashing in the described user scenario and maybe even without having to change my whole layout?

The content of the "lower_space" area should be scrollable and in this example it is a GridLayout including SmartTiles, but it could also be anything else like a long BoxLayout with multiple Buttons, Labels, Images, etc. The content of the "upper_space" should be something static (non-scrollable) like an image or text with the button that changes both BoxLayout's height.

Here is my code:

from kivymd.app import MDApp
from kivy.lang import Builder

kv = '''
BoxLayout:
    orientation: 'vertical'
    
    BoxLayout:
        orientation: 'vertical'

        BoxLayout:
            id: upper_space
            orientation: 'vertical'
            size_hint_y: 0.3
            
            BoxLayout:
                id: title_bar
                orientation: 'horizontal'
                size_hint_y: 0.3
                    
                MDIconButton:
                    icon: 'delete'
                    size_hint_x: 1
                    on_release:
                        app.size_hint_y_layouts('upper_space', 0, 'lower_space', 1)
                        
            BoxLayout:
                id: preview_widget
                orientation: 'vertical'
                size_hint_y: 0.8
                
                MDLabel:
                    text: 'Some Label'
                    halign: 'center'
        
        BoxLayout:
            id: lower_space
            orientation: 'vertical'
            size_hint_y: 0.7
            
            ScrollView:    
                id: somescroll
                GridLayout:
                    id: somegrid
                    cols: 2
                    row_default_height:
                        (self.width - self.cols*self.spacing[0])/self.cols
                    row_force_default: True
                    size_hint_y: None
                    height: self.minimum_height
                
                    SmartTileWithLabel:
                        box_color: [0.2,0.2,0,1]
                        text: "Tile1"
                        font_style: 'H3'
                        
                    SmartTileWithLabel:
                        box_color: [0.2,0.2,0,1]
                        text: "Tile2"
                        font_style: 'H3'
                
                    SmartTileWithLabel:
                        box_color: [0.2,0.2,0,1]
                        text: "Tile3"
                        font_style: 'H3'
                
                    SmartTileWithLabel:
                        box_color: [0.2,0.2,0,1]
                        text: "Tile4"
                        font_style: 'H3'
                        
                    SmartTileWithLabel:
                        box_color: [0.2,0.2,0,1]
                        text: "Tile5"
                        font_style: 'H3'
'''

class MainApp(MDApp):
    
    def __init__(self, **kwargs):
        super(MainApp, self).__init__(**kwargs)
    
    def build(self):
        screen = Builder.load_string(kv)
        return screen
    
    def size_hint_y_layouts(self, layout1, size_hint_y1, layout2, size_hint_y2):
        self.root.ids[layout1].size_hint_y = size_hint_y1
        self.root.ids[layout2].size_hint_y = size_hint_y2
    
if __name__ == '__main__':
    MainApp().run()

I have thought about multiple options, but maybe you have a better idea?

Idea 1: prevent the usage of multitouch

As multitouch function is not so important in my app, I could disable it completely. Using the following code results into strange behaviour on the Android phone (scrolling not working). I guess this is because the code is for a PC platform. Is there a modified version for smartphone touchscreens?

from kivy.config import Config
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')

Idea 2: Disabling the ScrollView or changing the do_scroll property to False

Both did not solve my problem unfortunately as the app keeps crashing in the described user scenario. Notice that I have to set the do_scroll property to True again after changing the BoxLayout's height and I have to enable the ScrollView again as the "lower_space" area could have a lot of content (it has to be scrollable!). Here is some code snippets that I have tried out (only the button's event has changed, the remaining code is identical):

MDIconButton:
    icon: 'delete'
    size_hint_x: 1
    on_release:
        root.ids['somescroll'].do_scroll = False
        app.size_hint_y_layouts('upper_space', 0, 'lower_space', 1)
        root.ids['somescroll'].do_scroll = True

And here:

MDIconButton:
    icon: 'delete'
    size_hint_x: 1
    on_release:
        root.ids['somescroll'].disabled = True
        app.size_hint_y_layouts('upper_space', 0, 'lower_space', 1)
        root.ids['somescroll'].disabled = False

Thanks in advance for any help!



from Kivy Android app crashes when changing BoxLayout sizes and scrolling at the same time?

No comments:

Post a Comment