Hey deloinfi,
Give this code a try! I hope it helps with your scrolling issue.
It uses a FloatLayout as the root to separate the header from the main list, which avoids the touch propagation conflicts you usually get with nested ScrollViews in V2.3.1.
Best regards,
Pedro
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
class MainRoot(FloatLayout):
def on_touch_move(self, touch):
rv, header = self.ids.list_populaire, self.ids.header_area
if header.collide_point(*touch.pos) and abs(touch.dy) > abs(touch.dx):
rv.scroll_y = max(0, min(1, rv.scroll_y - (touch.dy / 500)))
return True
return super().on_touch_move(touch)
class CardHorizontal(BoxLayout):
title = StringProperty('')
class CardVertical(BoxLayout):
title = StringProperty('')
KV = '''
<CardHorizontal>:
orientation: 'vertical'
size_hint_x: None
width: 140
padding: 10
canvas.before:
Color:
rgba: 0.2, 0.2, 0.2, 1
RoundedRectangle:
size: self.size
pos: self.pos
radius: [10]
Label:
text: root.title
input_transparent: True
<CardVertical>:
size_hint_y: None
height: 80
padding: 10
canvas.before:
Color:
rgba: 0.15, 0.15, 0.15, 1
Rectangle:
size: self.size
pos: self.pos
Label:
text: root.title
MainRoot:
canvas.before:
Color:
rgba: 0.05, 0.05, 0.05, 1
Rectangle:
size: self.size
pos: self.pos
RecycleView:
id: list_populaire
viewclass: 'CardVertical'
on_scroll_y: app.on_scroll(self)
RecycleBoxLayout:
id: rv_layout
default_size: None, 80
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: 5
padding: [10, 590, 10, 10]
BoxLayout:
id: header_area
orientation: 'vertical'
size_hint: 1, None
height: 500
y: root.height - self.height - 80
spacing: 10
padding: 10
canvas.before:
Color:
rgba: 0.05, 0.05, 0.05, 1
Rectangle:
size: self.size
pos: self.pos
Label:
text: "Categories"
size_hint_y: None
height: 30
RecycleView:
id: rv_cats
viewclass: 'CardHorizontal'
size_hint_y: None
height: 40
do_scroll_x: True
do_scroll_y: False
RecycleBoxLayout:
default_size: None, None
default_size_hint: None, 1
size_hint_x: None
width: self.minimum_width
orientation: 'horizontal'
spacing: 10
Label:
text: "Features"
size_hint_y: None
height: 30
RecycleView:
id: rv_features
viewclass: 'CardHorizontal'
size_hint_y: None
height: 160
do_scroll_x: True
do_scroll_y: False
RecycleBoxLayout:
default_size: None, None
default_size_hint: None, 1
size_hint_x: None
width: self.minimum_width
orientation: 'horizontal'
spacing: 10
Label:
text: "Recommended"
size_hint_y: None
height: 30
RecycleView:
id: rv_reco
viewclass: 'CardHorizontal'
size_hint_y: None
height: 100
do_scroll_x: True
do_scroll_y: False
RecycleBoxLayout:
default_size: None, None
default_size_hint: None, 1
size_hint_x: None
width: self.minimum_width
orientation: 'horizontal'
spacing: 10
Label:
text: "Popular (Infinite)"
size_hint_y: None
height: 30
bold: True
Label:
text: "TOUT EST VISIBLE ICI"
size_hint: 1, None
height: 50
pos_hint: {'top': 1}
bold: True
canvas.before:
Color:
rgba: 0.08, 0.08, 0.08, 1
Rectangle:
size: self.size
pos: self.pos
'''
class TestApp(App):
is_loading = False
def build(self): return Builder.load_string(KV)
def on_start(self):
self.root.ids.rv_cats.data = [{'title': f'Cat {i}'} for i in range(15)]
self.root.ids.rv_features.data = [{'title': f'Feature {i}'} for i in range(10)]
self.root.ids.rv_reco.data = [{'title': f'Reco {i}'} for i in range(10)]
self.load_more_items()
def on_scroll(self, rv):
if rv.scroll_y <= 0.1 and not self.is_loading:
self.is_loading = True
self.load_more_items()
header = self.root.ids.header_area
scroll_px = (1.0 - rv.scroll_y) * (rv.children[0].height - rv.height)
header.y = self.root.height - 500 - 80 + min(500, scroll_px)
def load_more_items(self, *args):
rv = self.root.ids.list_populaire
rv.data.extend([{'title': f"Popular #{len(rv.data) + i}"} for i in range(15)])
rv.refresh_from_data()
Clock.schedule_once(lambda dt: setattr(self, 'is_loading', False), 0.5)
if __name__ == '__main__':
TestApp().run()