""" Components/Refresh Layout ========================= Example ------- .. code-block:: python from kivymd.app import MDApp from kivy.clock import Clock from kivy.lang import Builder from kivy.factory import Factory from kivy.properties import StringProperty from kivymd.uix.button import MDIconButton from kivymd.icon_definitions import md_icons from kivymd.uix.list import ILeftBodyTouch, OneLineIconListItem from kivymd.theming import ThemeManager from kivymd.utils import asynckivy Builder.load_string(''' text: root.text IconLeftSampleWidget: icon: root.icon BoxLayout: orientation: 'vertical' MDToolbar: title: app.title md_bg_color: app.theme_cls.primary_color background_palette: 'Primary' elevation: 10 left_action_items: [['menu', lambda x: x]] MDScrollViewRefreshLayout: id: refresh_layout refresh_callback: app.refresh_callback root_layout: root MDGridLayout: id: box adaptive_height: True cols: 1 ''') class IconLeftSampleWidget(ILeftBodyTouch, MDIconButton): pass class ItemForList(OneLineIconListItem): icon = StringProperty() class Example(MDApp): title = 'Example Refresh Layout' screen = None x = 0 y = 15 def build(self): self.screen = Factory.Example() self.set_list() return self.screen def set_list(self): async def set_list(): names_icons_list = list(md_icons.keys())[self.x:self.y] for name_icon in names_icons_list: await asynckivy.sleep(0) self.screen.ids.box.add_widget( ItemForList(icon=name_icon, text=name_icon)) asynckivy.start(set_list()) def refresh_callback(self, *args): '''A method that updates the state of your application while the spinner remains on the screen.''' def refresh_callback(interval): self.screen.ids.box.clear_widgets() if self.x == 0: self.x, self.y = 15, 30 else: self.x, self.y = 0, 15 self.set_list() self.screen.ids.refresh_layout.refresh_done() self.tick = 0 Clock.schedule_once(refresh_callback, 1) Example().run() """ from kivy.animation import Animation from kivy.core.window import Window from kivy.effects.dampedscroll import DampedScrollEffect from kivy.lang import Builder from kivy.metrics import dp from kivy.properties import ListProperty, NumericProperty, ObjectProperty from kivy.uix.floatlayout import FloatLayout from kivy.uix.scrollview import ScrollView from kivymd.theming import ThemableBehavior Builder.load_string( """ #:import Window kivy.core.window.Window AnchorLayout: id: body_spinner size_hint: None, None size: dp(46), dp(46) y: Window.height pos_hint: {'center_x': .5} anchor_x: 'center' anchor_y: 'center' canvas: Clear Color: rgba: root.theme_cls.primary_dark Ellipse: pos: self.pos size: self.size MDSpinner: id: spinner size_hint: None, None size: dp(30), dp(30) color: 1, 1, 1, 1 """ ) class _RefreshScrollEffect(DampedScrollEffect): """This class is simply based on DampedScrollEffect. If you need any documentation please look at kivy.effects.dampedscrolleffect. """ min_scroll_to_reload = NumericProperty("-100dp") """Minimum overscroll value to reload.""" def on_overscroll(self, scrollview, overscroll): if overscroll < self.min_scroll_to_reload: scroll_view = self.target_widget.parent scroll_view._did_overscroll = True return True else: return False class MDScrollViewRefreshLayout(ScrollView): root_layout = ObjectProperty() """The spinner will be attached to this layout.""" def __init__(self, **kargs): super().__init__(**kargs) self.effect_cls = _RefreshScrollEffect self._work_spinnrer = False self._did_overscroll = False self.refresh_spinner = None def on_touch_up(self, *args): if self._did_overscroll and not self._work_spinnrer: if self.refresh_callback: self.refresh_callback() if not self.refresh_spinner: self.refresh_spinner = RefreshSpinner(_refresh_layout=self) self.root_layout.add_widget(self.refresh_spinner) self.refresh_spinner.start_anim_spinner() self._work_spinnrer = True self._did_overscroll = False return True return super().on_touch_up(*args) def refresh_done(self): if self.refresh_spinner: self.refresh_spinner.hide_anim_spinner() class RefreshSpinner(ThemableBehavior, FloatLayout): spinner_color = ListProperty([1, 1, 1, 1]) _refresh_layout = ObjectProperty() """kivymd.refreshlayout.MDScrollViewRefreshLayout object.""" def start_anim_spinner(self): spinner = self.ids.body_spinner Animation( y=spinner.y - self.theme_cls.standard_increment * 2 + dp(10), d=0.8, t="out_elastic", ).start(spinner) def hide_anim_spinner(self): spinner = self.ids.body_spinner anim = Animation(y=Window.height, d=0.8, t="out_elastic") anim.bind(on_complete=self.set_spinner) anim.start(spinner) def set_spinner(self, *args): body_spinner = self.ids.body_spinner body_spinner.size = (dp(46), dp(46)) body_spinner.y = Window.height body_spinner.opacity = 1 spinner = self.ids.spinner spinner.size = (dp(30), dp(30)) spinner.opacity = 1 self._refresh_layout._work_spinnrer = False self._refresh_layout._did_overscroll = False