Skip to content

Instantly share code, notes, and snippets.

@tshirtman
Last active April 6, 2021 17:40
Show Gist options
  • Save tshirtman/41e533d077567762b3bd981f718f3cd6 to your computer and use it in GitHub Desktop.
Save tshirtman/41e533d077567762b3bd981f718f3cd6 to your computer and use it in GitHub Desktop.
Example of a RecycleView that keeps the current view position when new data is added, unless we are at the very bottom, in which case we follow the scroll.
from random import sample, randint
from string import ascii_lowercase
from time import asctime
from kivy.app import App
from kivy.factory import Factory
from kivy.lang import Builder
from kivy.properties import NumericProperty, ListProperty
from kivy.clock import Clock
KV = """
BoxLayout:
orientation: 'vertical'
Label:
size_hint_y: None
height: self.texture_size[1]
text:
F'''height: {rv.height}
scrollable_distance: {rv.scrollable_distance}
distance_to_top: {rv.distance_to_top}
scroll_y: {rv.scroll_y}
len items: {len(rv.data)}
'''
ToggleButton:
id: active
state: 'down'
text: 'active'
size_hint_y: None
height: '50dp'
ToggleButton:
id: show
state: 'down'
text: 'active'
size_hint_y: None
height: '50dp'
FixedRecycleView:
id: rv
data: app.data if show.state == 'down' else []
viewclass: 'Label'
scrollable_distance: box.height - self.height
RecycleBoxLayout:
id: box
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
default_size: 0, 48
"""
class FixedRecycleView(Factory.RecycleView):
distance_to_top = NumericProperty()
scrollable_distance = NumericProperty()
def on_scrollable_distance(self, *args):
if self.scroll_y > 0:
self.scroll_y = (self.scrollable_distance - self.distance_to_top) / self.scrollable_distance
def on_scroll_y(self, *args):
self.distance_to_top = (1 - self.scroll_y) * self.scrollable_distance
class Application(App):
pending_data = ListProperty()
data = ListProperty()
def build(self):
Clock.schedule_interval(self.add_log, .01)
Clock.schedule_interval(self.flush_pending_data, .250)
return Builder.load_string(KV)
def flush_pending_data(self, *args):
if self.pending_data:
pending_data, self.pending_data = self.pending_data, []
self.data.extend(pending_data)
def add_log(self, dt):
if self.root.ids.active.state == 'down':
self.pending_data.append({
'text': f"[{asctime()}]: {' '.join(''.join(sample(ascii_lowercase, randint(5, 20))) for i in range(10))}"
})
if __name__ == '__main__':
Application().run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment