from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
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
<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
<HorizontalRV@RecycleView>:
do_scroll_x: True
do_scroll_y: False
size_hint_y: None
height: 150
viewclass: 'CardHorizontal'
scroll_timeout: 100
RecycleBoxLayout:
default_size: None, None
default_size_hint: None, 1
size_hint_x: None
width: self.minimum_width
orientation: 'horizontal'
spacing: 10
padding: 10
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: 0.05, 0.05, 0.05, 1
Rectangle:
size: self.size
pos: self.pos
Label:
text: "TOUT EST VISIBLE ICI"
size_hint_y: None
height: 50
bold: True
# --- LE SCROLL PRINCIPAL ---
ScrollView:
id: main_scroll
on_scroll_y: app.check_infinite_scroll(self)
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
spacing: 20
padding: 10
# 1. Categories
Label:
text: "Catégories"
size_hint_y: None
height: 30
HorizontalRV:
id: rv_cats
height: 60
# 2. Features
Label:
text: "Features"
size_hint_y: None
height: 30
HorizontalRV:
id: rv_features
height: 180
# 3. Recommandés
Label:
text: "Recommandés"
size_hint_y: None
height: 30
HorizontalRV:
id: rv_reco
height: 120
# 4. Populaires (RECYCLEVIEW)
Label:
text: "Populaires (Infini)"
size_hint_y: None
height: 30
RecycleView:
id: list_populaire
viewclass: 'CardVertical'
size_hint_y: None
# TRÈS IMPORTANT : On lie la hauteur à son contenu interne
height: rv_layout.minimum_height
do_scroll_y: False # On laisse le ScrollView principal scroller
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
'''
class TestApp(App):
def build(self):
return Builder.load_string(KV)
def on_start(self):
# Données horizontales
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)]
# Données verticales
self.load_more_items()
def load_more_items(self, *args):
rv = self.root.ids.list_populaire
current_data = rv.data
new_items = [{'title': f"Populaire #{len(current_data) + i}"} for i in range(15)]
rv.data.extend(new_items)
rv.refresh_from_data()
def check_infinite_scroll(self, scrollview):
# On vérifie le scroll du ScrollView principal
if scrollview.scroll_y <= 0.05:
Clock.schedule_once(self.load_more_items, 0.2)
if __name__ == '__main__':
TestApp().run()