Last active
April 6, 2021 17:40
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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