You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kivy中Dropdown箭头旋转动画实现及Spinner关闭事件处理

解决Kivy Dropdown箭头旋转复原及关闭触发问题

核心思路

要实现箭头展开旋转、关闭复原,关键是监听Dropdown的关闭事件(on_dismiss),在事件触发时执行反向动画;同时通过Dropdown和Spinner的关联,确保所有关闭操作(包括点击外部区域)都能触发回调。

修改步骤

1. 更新Python代码

新增箭头还原动画方法,拆分展开/关闭动画逻辑:

from kivy.lang.builder import Builder
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.utils import get_color_from_hex as hex
from kivy.animation import Animation

Builder.load_file("dropdowns_and_btns.kv")

kv = """
#:import hex kivy.utils.get_color_from_hex
#:import FadeTransition kivy.uix.screenmanager.FadeTransition

MyScreenManager:
    transition: FadeTransition(duration=0)
    IntroPage:
    LandingPage:
    ResultPage:

<IntroPage>
    MDScreen:
        md_bg_color: hex("#07074D")

        MDLabel:
            text: "BGF"
            color: hex("#ECE4F5")
            font_size: 36
            pos_hint: {"center": (0.5, 0.7)}
            halign: "center"

        RoundedGradientButton:
            text: "START"
            font_size: 20
            size_hint: 0.3, 0.08
            pos_hint: {"center": (0.5, 0.3)}
            on_release: app.root.current = "landing_page"

<LandingPage>
    name: "landing_page"
    MDScreen:
        md_bg_color: hex("#07074D")

        MDLabel:
            icon_color: 1, 0, 1, 1
            text: "Please select"
            pos_hint: {"center_y": 0.9}
            halign: "center"
            font_size: 32
            color: hex("#ECE4F5")
                                                                        # Dropdown Label
        MDFloatLayout:
            size_hint: 0.75, 0.3
            pos_hint: {"center": (0.5, 0.5)}

            BoxLayout
                size_hint: 0.5, 0.9
                pos_hint: {"center": (0.26, 0.9)}
                MDLabel:
                    text: "Month"
                    color: hex("#ECE4F5")

            BoxLayout
                size_hint: 0.5, 0.9
                pos_hint: {"center": (0.79, 0.9)}
                MDLabel:
                    text: "Year"
                    color: hex("#ECE4F5")
                                                                    # Dropdown Button
            MDGridLayout:
                cols: 2
                size_hint: 1, 0.3
                pos_hint: {"center": (0.5, 0.65)}
                spacing: 30

                DropdownMenu:
                    id: month
                    text: 'Select'
                    values: [month for month, x in app.temp_db.items()]
                    on_text: self.text

                DropdownMenu:
                    id: year
                    text: 'Select'
                    values: [f"{year}" for year in range(18, 46)]
                    on_text: self.text
                                                                    # Find now button
            RoundedGradientButton:
                text: "FIND NOW"
                font_size: 16
                size_hint: 0.25, 0.25
                pos_hint: {"center": (0.5, 0.15)}
<ResultPage>
    name: "result_page"
    MDScreen:
        md_bg_color: hex("#07074D")

    MDLabel:
        id: result_prefix_label
        text: "Congratulations!"
        color: hex("#ECE4F5")
        font_size: 20
        pos_hint: {"center": (0.5, 0.7)}
        halign: "center"

    MDLabel:
        id: result_label
        color: hex("#ECE4F5")
        font_size: 40
        pos_hint: {"center": (0.5, 0.6)}
        halign: "center"

    Button:
        background_normal: ""
        background_color: 0, 0, 0, 0
        pos_hint: {"center": (0.5, 0.3)}
        on_press: app.root.current = "landing_page"
        Image:
            source: "images/reload.png"
            center_x: self.parent.center_x
            center_y: self.parent.center_y
            size_hint_x: None
            height: 50
"""


class IntroPage(Screen):
    pass


class LandingPage(Screen):
    pass


class ResultPage(Screen):
    pass


class MyScreenManager(ScreenManager):
    pass


class BabyGenderFinder(MDApp):
    temp_db = {"January": 1, "February": 2, "March": 3, "April": 4, "May": 5}

    def build(self):
        return Builder.load_string(kv)

    def dropdown_expand_animation(self, wid):
        ani = Animation(angle=-90, duration=0.2)
        ani.start(wid)

    def dropdown_collapse_animation(self, wid):
        ani = Animation(angle=0, duration=0.2)
        ani.start(wid)


BabyGenderFinder().run()

2. 更新KV文件

绑定Dropdown关闭事件,关联Spinner与Dropdown的引用,同步颜色与动画:

#:import hex kivy.utils.get_color_from_hex
#:import Factory kivy.factory.Factory

                                                                    # Drop down button setting
<DropdownMenu@Spinner>
    dropdown_cls: Factory.CustomDropdown
    option_cls: Factory.DropdownOptions

    color: hex("#87828C")
    font_name: "fonts/Poppins-Medium.ttf"
    background_normal: ""
    background_color: 0, 0, 0, 0

    # 展开时触发动画和颜色变化,同时绑定Spinner引用到Dropdown
    on_release: 
        self.color = hex("#ECE4F5")
        app.dropdown_expand_animation(self.ids.dropdown_sign)
        if not self._dropdown:
            self._dropdown = self.dropdown_cls()
            self._dropdown.spinner_ref = self

    canvas.before:
        Color:
            rgba: hex("#111140")
        RoundedRectangle:
            size: self.size
            pos: self.pos
            radius: [6]
        Color: 
            rgba: hex("#1F1F73")
        Line:
            width: 1.25
            rounded_rectangle: self.x, self.y, self.width, self.height, 6, 6, 6, 6, 100
            
    MDIconButton:
        id: dropdown_sign
        center_x: self.parent.center_x + 110
        center_y: self.parent.center_y
        angle: 0
        canvas.before:
            PushMatrix
            Rotate:
                angle: self.angle
                axis: 0, 0, 1
                origin: self.center
        canvas.after:
            PopMatrix
        Image:            
            source: "images/dropdown.png"
            size_hint: None, None
            size: 20, 20

                                                                    # Drop down options setting
<DropdownOptions@SpinnerOption>
    color: hex("#ECE4F5")
    font_name: "fonts/Poppins-Regular.ttf"
    background_normal: ""
    background_color: 0, 0, 0, 0
                                                                    # Drop down options separator setting
    canvas:
        Color:
            rgba: hex("#3D3D99")
        Line:
            width: 1
            points: self.x+10, self.y, self.width-10, self.y

<CustomDropdown@DropDown>:
    spinner_ref: None  # 存储对应的Spinner引用
    canvas.before:
        Color:
            rgba: hex("#1F1F73")
        RoundedRectangle:
            size: self.size
            pos: self.pos
            radius: [0, 0, 6, 6]
    max_height: 200

    # 关闭Dropdown时触发还原动画和颜色恢复
    on_dismiss:
        if self.spinner_ref:
            app.dropdown_collapse_animation(self.spinner_ref.ids.dropdown_sign)
            self.spinner_ref.color = hex("#87828C")

                                                                    # Gradient button settings
<RoundedGradientButton@Button>
    background_normal: ""
    background_color: 0,0,0,0
    font_name: "fonts/Poppins-Medium.ttf"
    on_press: self.size_hint = self.size_hint_x/1.05, self.size_hint_y/1.05
    on_release: self.size_hint = self.size_hint_x*1.05, self.size_hint_y*1.05
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        RoundedRectangle:
            source: "button_bg.png"
            size: self.size
            pos: self.pos
            radius: [self.size[1]/1.92]

关键说明

  • 全局关闭监听:CustomDropdown的on_dismiss事件会覆盖所有关闭场景(点击选项、点击外部区域),确保箭头总能复原。
  • 组件关联:通过spinner_ref将Dropdown与对应的Spinner绑定,避免操作错误的箭头组件。
  • 动画优化:将动画时长调整为0.2秒,让旋转过渡更平滑自然。

内容的提问来源于stack exchange,提问作者StevenHaven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 09:06:17