我正在尝试实现 kivy Hero 组件的这个 example,但不使用 KV language。
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView
from kivymd.app import MDApp
from kivymd.uix.hero import MDHeroFrom, MDHeroTo
from kivymd.uix.imagelist.imagelist import MDSmartTile, MDSmartTileImage, MDSmartTileOverlayContainer
from kivy.metrics import dp
from kivymd.uix.button import MDButton, MDButtonIcon, MDButtonText
from kivymd.uix.screen import MDScreen
from kivymd.uix.screenmanager import MDScreenManager
from kivymd.uix.label.label import MDLabel
from kivymd.uix.scrollview import StretchOverScrollStencil, MDScrollView
from kivymd.uix.gridlayout import MDGridLayout
from kivy.core.window import Window
class HeroItem(MDHeroFrom):
text = StringProperty()
tag = StringProperty()
manager = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.size_hint_y = None
self.height = "200dp"
self.radius = "24dp"
window_width, window_height = Window.size
def create_tile(self, img_path):
# Create SmartTile
print('type', type(Window.size), type(self.manager), self.manager)
print('parent_size', (self.width, self.height), Window.size, self.manager.size)
print('self.manager', self.manager, self.size_hint)
self.tile = MDSmartTile(id='tile', size_hint=(None, None))#pos_hint={"x": 0, "y": 0.1}, - not worked
self.tile.size = self.manager.size
#size_hint=(None, None) should be None, then only the smooth opening and closing is happening on each tile
#size=(380, 200) is fine but rearrening the window size it is not dynamic
self.tile.bind(on_release=self.on_release)
# Create SmartTileImage
self.image = MDSmartTileImage(id='image', source=img_path, radius="24dp",)
#self.image.size = self.tile.size
self.image.ripple_duration_in_fast = 0.05
self.tile.add_widget(self.image)
# Create Overlay Container
self.overlay = MDSmartTileOverlayContainer(id='overlay',
md_bg_color=(0, 0, 0, .5),
adaptive_height=True,
radius=[0, 0, dp(24), dp(24)],
padding="8dp",
spacing="8dp",
)
#self.overlay.size = self.tile.size
# Create Label
#self.label = Label(text=self.tag, color=(1, 1, 1, 1), size_hint_y=None, height=dp(40))
self.label = MDLabel(text=self.tag+str(Window.size[0]), theme_text_color='Custom', text_color="white", adaptive_height=True)
self.overlay.add_widget(self.label)
self.tile.add_widget(self.overlay)
self.add_widget(self.tile)
def on_transform_in(self, instance_hero_widget, duration):
print('in', duration)
for instance in [
instance_hero_widget,
instance_hero_widget._overlay_container,
instance_hero_widget._image,
]:
Animation(radius=[0, 0, 0, 0], duration=duration).start(instance)
def on_transform_out(self, instance_hero_widget, duration):
print('on', duration)
for instance, radius in {
instance_hero_widget: [dp(24), dp(24), dp(24), dp(24)],
instance_hero_widget._overlay_container: [0, 0, dp(24), dp(24)],
instance_hero_widget._image: [dp(24), dp(24), dp(24), dp(24)],
}.items():
print('radius', radius)
Animation(
radius=radius,
duration=duration,
).start(instance)
def on_release(self, *args):
print('a')
def switch_screen(*args):
#print('self.manager.current_heroes', self.manager.current_heroes)
self.manager.current_heroes = [self.tag]
#print('self.manager.current_heroes', self.manager.current_heroes)
print('?', dir(self.manager.ids))
print('tag', self.tag)
screen_b = self.manager.get_screen("screen B")
screen_b.hero_to.tag = self.tag
self.manager.current = "screen B"
Clock.schedule_once(switch_screen, 0.2)
#class ScreenA(MDScreen):
# pass
class ScreenB(MDScreen):
hero_to = ObjectProperty() # Use ObjectProperty
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hero_to = MDHeroTo(size_hint=(1, None), height="220dp", pos_hint={"top": 1})
self.add_widget(self.hero_to)
self.button = MDButton(MDButtonText(text="Move Hero To Screen A"),
pos_hint={"center_x": .5}, y = "36dp")#size_hint_y=None, height=dp(50)
self.button.bind(on_release=self.switch_to_screen_a)
self.add_widget(self.button)
def switch_to_screen_a(self, *args):
print('b', self.hero_to.tag)
self.manager.current = "screen A"
self.manager.current_heroes = [self.hero_to.tag]
class Example(MDApp):
def build(self):
self.theme_cls.theme_style_switch_animation = True
self.theme_cls.primary_palette = "Orange"
self.theme_cls.theme_style = "Dark" # "Light"
print('self.theme_cls.backgroundColor', self.theme_cls.backgroundColor)
self.manager = MDScreenManager()
self.manager.primary_palette = "Orange"
self.manager.theme_style = "Dark" # "Light"
self.manager.md_bg_color = (1, 1, 1, 1) #self.theme_cls.backgroundColor
screen_a = MDScreen(name="screen A")
screen_b = ScreenB(name="screen B")
# ScrollView
scroll_view = MDScrollView()
grid = MDGridLayout(cols=2, spacing="12dp", padding="12dp", adaptive_height = True,)
#print('grid', dir(grid))
#print('??', grid.width, grid.height, grid.x, grid.y, grid.adaptive_height, grid.adaptive_width, grid.adaptive_size,
# grid.line_width, grid.minimum_height, grid.minimum_width, grid.minimum_size,
# grid.size_hint)
#grid.width - 100
#0 0 0 True False False 1 0 0 [0, 0]
#[1, None]
#grid.bind(minimum_height=grid.setter('height'))
scroll_view.add_widget(grid)
screen_a.add_widget(scroll_view)
self.manager.add_widget(screen_a)
self.manager.add_widget(screen_b)
# Add HeroItems to Grid
for i in range(12):
hero_item = HeroItem(text=f"Item {i + 1}", tag=f"Tag {i}", manager=self.manager)
hero_item.create_tile('3.png')
if not i % 2:
hero_item.md_bg_color = "lightgrey"
grid.add_widget(hero_item)
return self.manager
Example().run()
但是,图像未按正确顺序显示。另外,当我调整窗口大小时,图像不会根据窗口大小自动调整大小:请参阅输出视频 1。
我将
size_hint
的MDSmartTile
参数从(None, None)
修改为(0.5, 1)
,然后图像可以正确显示,并且在调整窗口大小时,图像也会根据窗口大小自动调整大小,但是修改此 size_hint 时出现问题 - 打开图像时动画出现失真:请参阅输出视频 2。
这可能是参数问题吗?
我找到了部分解决方案。在此之前,一些观察。
当您调整窗口大小时,布局也会调整大小,这也负责根据子窗口的 size_hint 调整其子窗口的大小。 MDGridLayout 的子级的 size_hint_x 应该为 1,或者 size_hint 为 (1, None)。
因此,您应该为每个 Hero 小部件提供一个大小提示(1,None),以使其水平延伸
图像顺序看起来不错。我看到具有最大标签 ID 的英雄小部件显示在底部。
MDSmartTile 上的大小提示应为 1,1,因为您希望它占用其英雄父级允许的尽可能多的空间。你应该依赖kivy语言;它的存在是为了让您的生活更轻松。
在您基于代码的演示示例中,图块的大小绑定到 Hero 小部件的大小。每当 Hero 小部件尺寸发生变化时,它会自动更新 Tile 小部件的尺寸。 我尝试使用您的代码手动执行此操作,但没有成功。我不确定为什么,但在这种情况下使用 kivy 语言肯定更好。
以下与动画相关的步骤也有帮助:
还请记住:
涉及的主要更改在 on_transform_in 和 on_transform_out 方法内部。
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import StringProperty, ObjectProperty
from kivymd.app import MDApp
from kivymd.uix.hero import MDHeroFrom
from kivymd.uix.transition import MDSharedAxisTransition
KV = '''
<HeroItem>
size_hint_x: 1
size_hint_y: None
height: "200dp"
radius: "24dp"
MDSmartTile:
id: tile
size_hint: None, None
size: root.size
on_release: root.on_release()
MDSmartTileImage:
id: image
source: 'https://github.com/kivymd/internal/raw/main/logo/kivymd_logo_blue.png'
radius: dp(24)
MDSmartTileOverlayContainer:
id: overlay
md_bg_color: 0, 0, 0, .5
adaptive_height: True
padding: "8dp"
spacing: "8dp"
radius: [0, 0, dp(24), dp(24)]
MDLabel:
text: root.tag
theme_text_color: "Custom"
text_color: "white"
adaptive_height: True
MDScreenManager:
md_bg_color: self.theme_cls.backgroundColor
transition: app.transition
MDScreen:
name: "screen A"
ScrollView:
MDGridLayout:
id: box
cols: 2
spacing: "12dp"
padding: "12dp"
adaptive_height: True
MDScreen:
name: "screen B"
heroes_to: [hero_to]
MDBoxLayout:
orientaion: 'horizontal'
MDHeroTo:
id: hero_to
size_hint: 1, None
height: "220dp"
pos_hint: {"top": 1}
MDButton:
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = [hero_to.tag]
root.current = "screen A"
MDButtonText:
text: "Move Hero To Screen A"
'''
def set_size_hint_one(widget):
widget.ids.tile.size_hint = (1, 1)
def set_size_hint_none(widget):
widget.ids.tile.size_hint = (None, None)
class HeroItem(MDHeroFrom):
text = StringProperty()
manager = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.ids.image.ripple_duration_in_fast = 0.05
def on_transform_in(self, instance_hero_widget, duration):
set_size_hint_none(self)
for instance in [
instance_hero_widget,
instance_hero_widget._overlay_container,
instance_hero_widget._image,
]:
Animation(radius=[0, 0, 0, 0], duration=duration).start(instance)
Clock.schedule_once(lambda x: set_size_hint_one(self), duration+0.1)
def on_transform_out(self, instance_hero_widget, duration):
set_size_hint_none(self)
for instance, radius in {
instance_hero_widget: [dp(24), dp(24), dp(24), dp(24)],
instance_hero_widget._overlay_container: [0, 0, dp(24), dp(24)],
instance_hero_widget._image: [dp(24), dp(24), dp(24), dp(24)],
}.items():
Animation(
radius=radius,
duration=duration,
).start(instance)
Clock.schedule_once(lambda x: set_size_hint_one(self), duration+0.1)
def on_release(self):
def switch_screen(*args):
self.manager.current_heroes = [self.tag]
self.manager.ids.hero_to.tag = self.tag
self.manager.current = "screen B"
Clock.schedule_once(switch_screen, 0.2)
class Example(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.transition = MDSharedAxisTransition()
self.transition.transition_axis = "z"
self.transition.duration = 0.2
def build(self):
return Builder.load_string(KV)
def on_start(self):
for i in range(12):
hero_item = HeroItem(
text=f"Item {i + 1}", tag=f"Tag {i}", manager=self.root
)
if not i % 2:
hero_item.md_bg_color = "lightgrey"
self.root.ids.box.add_widget(hero_item)
Example().run()