110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
'''How to use Animation with RecycleView items?
|
|
|
|
In case you really want to use the Animation class with RecycleView, you'll
|
|
likely encounter an issue, as widgets are moved around, they are used to
|
|
represent different items, so an animation on a specific item is going to
|
|
affect others, and this will lead to really confusing results.
|
|
|
|
This example works around that by creating a "proxy" widget for the animation,
|
|
and, by putting it in the data, allowing the displayed widget to mimic the
|
|
animation. As the item always refers to its proxy, whichever widget is used to
|
|
display the item will keep in sync with the animation.
|
|
|
|
'''
|
|
from copy import copy
|
|
|
|
from kivy.app import App
|
|
from kivy.clock import triggered
|
|
from kivy.lang import Builder
|
|
from kivy.uix.widget import Widget
|
|
from kivy.animation import Animation
|
|
from kivy.uix.button import Button
|
|
from kivy.properties import (
|
|
ObjectProperty, ListProperty
|
|
)
|
|
|
|
|
|
KV = '''
|
|
<Item>:
|
|
index: None
|
|
animation_proxy: None
|
|
on_release: app.animate_item(self.index)
|
|
|
|
|
|
RecycleView:
|
|
data: app.data
|
|
viewclass: 'Item'
|
|
RecycleBoxLayout:
|
|
orientation: 'vertical'
|
|
size_hint: 1, None
|
|
height: self.minimum_height
|
|
default_size_hint: 1, None
|
|
default_size: 0, dp(40)
|
|
'''
|
|
|
|
|
|
class Item(Button):
|
|
animation_proxy = ObjectProperty(allownone=True)
|
|
_animation_proxy = None
|
|
|
|
def update_opacity(self, proxy, opacity):
|
|
# sync one animated property to the value in the proxy
|
|
self.opacity = opacity
|
|
|
|
def on_animation_proxy(self, *args):
|
|
"""When we create an animation proxy for an item, we need to bind to
|
|
the animated property to update our own.
|
|
"""
|
|
if self._animation_proxy:
|
|
self._animation_proxy.unbind(opacity=self.update_opacity)
|
|
|
|
self._animation_proxy = self.animation_proxy
|
|
if self.animation_proxy:
|
|
# when we are assigned an animation_proxy, sync our properties to
|
|
# the animated version.
|
|
self.opacity = self.animation_proxy.opacity
|
|
self.animation_proxy.bind(opacity=self.update_opacity)
|
|
else:
|
|
# if we lose our animation proxy, we need to reset the animated
|
|
# property to their default values.
|
|
self.opacity = 1
|
|
|
|
|
|
class Application(App):
|
|
data = ListProperty()
|
|
|
|
def build(self):
|
|
self.data = [
|
|
{'index': i, 'text': 'hello {}'.format(i), 'animation_proxy': None}
|
|
for i in range(1000)
|
|
]
|
|
return Builder.load_string(KV)
|
|
|
|
# the triggered decorator allows delaying the animation until after the
|
|
# blue effect on the button is removed, to avoid a flash as widgets gets
|
|
# reordered when that happens
|
|
@triggered(timeout=0.05)
|
|
def animate_item(self, index):
|
|
# the animation we actually want to do on the item, note that any
|
|
# property animated here needs to be synchronized from the proxy to the
|
|
# animated widget (in on_animation_proxy and using methods for each
|
|
# animation)
|
|
proxy = Widget(opacity=1)
|
|
item = copy(self.data[index])
|
|
animation = (
|
|
Animation(opacity=0, d=.1, t='out_quad')
|
|
+ Animation(opacity=1, d=5, t='out_quad')
|
|
)
|
|
animation.bind(on_complete=lambda *x: self.reset_animation(item))
|
|
item['animation_proxy'] = proxy
|
|
self.data[index] = item
|
|
animation.start(proxy)
|
|
|
|
def reset_animation(self, item):
|
|
# animation is complete, widget should be garbage collected
|
|
item['animation_proxy'] = None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
Application().run()
|