
本文介绍如何在 kivy 中构建一个 5 列卡片式网格,每张卡片包含标题按钮和自适应比例图片,并让整个网格容器占据窗口高度的指定百分比(如 75%),同时支持垂直滚动查看超出视口的内容。
本文介绍如何在 kivy 中构建一个 5 列卡片式网格,每张卡片包含标题按钮和自适应比例图片,并让整个网格容器占据窗口高度的指定百分比(如 75%),同时支持垂直滚动查看超出视口的内容。
要在 Kivy 中实现符合需求的动态图片网格(5 列、标题+图片卡片、固定高度占比、可滚动),关键在于正确配置 ScrollView 与内部 GridLayout 的尺寸行为。原代码中 ScrollView 使用了硬编码 size=(Window.width, Window.height),导致其撑满全窗且无法响应父容器约束;而 GridLayout 缺少 size_hint_y: None 和 height: self.minimum_height,使其高度被强制绑定到 ScrollView 视口,失去“内容可溢出”的前提。
✅ 正确做法是:
- 将
ScrollView的size_hint_y设为所需比例(如0.75),使其高度为窗口高度的 75%; - 内部
GridLayout必须设置size_hint_y: None并显式绑定height: self.minimum_height—— 这是 ScrollView 实现滚动的核心机制:只有当子容器高度可动态增长并超过 ScrollView 自身高度时,滚动才生效; - 每个卡片(
BoxLayout)也需设为size_hint_y: None并赋予明确height(例如150dp),否则minimum_height无法准确累加(Kivy 需要确定的子项高度来计算总高); - 图片使用
size_hint_y: 0.75(配合卡片总高)而非size_hint_x=1单独控制,确保在保持宽高比前提下,高度严格按比例分配,避免拉伸或留白异常。
以下为推荐实现(基于 KV 语言 + Python 逻辑分离,更易维护):
ScrollView:
size_hint_y: 0.75 # 占窗口高度的 75%
do_scroll_x: False
do_scroll_y: True
smooth_scroll_end: 15
scroll_wheel_distance: 20
GridLayout:
id: grid
cols: 5
spacing: 8, 12 # 增加间距提升可读性
padding: 10
size_hint: 1, None
height: self.minimum_height # ✅ 关键:允许内容高度自由增长
<card>:
orientation: 'vertical'
size_hint: 1, None
height: 160 # ✅ 卡片固定高度(单位 dp),支撑 minimum_height 计算
Button:
text: root.text
font_size: '14sp'
size_hint_y: 0.3
background_color: 0.9, 0.9, 1, 1
color: 0, 0, 0.3, 1
Image:
texture: root.texture
size_hint_y: 0.7
allow_stretch: True
keep_ratio: True</card>
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
kv = """...""" # 上述 KV 字符串
class Card(BoxLayout):
text = StringProperty('')
texture = ObjectProperty(None)
class MyApp(App):
def build(self):
# 延迟填充确保 UI 已构建完成
Clock.schedule_once(self.fill_grid)
return Builder.load_string(kv)
def fill_grid(self, _dt):
grid = self.root.ids.grid
# 示例:加载本地图片纹理(实际中替换为 image_cache.get_image(...))
try:
sample_img = Image(source='icon.png').texture
except:
sample_img = None
for i in range(42): # 动态生成 42 张卡片
grid.add_widget(Card(
text=f'Item {i+1}',
texture=sample_img
))
⚠️ 注意事项:
-
切勿在
ScrollView中设置size_hint_x/y同时又写死size—— 二者冲突会导致布局失效; -
keep_ratio: True+allow_stretch: True是保持图片原始比例并填满分配区域的黄金组合; - 若图片来源异步(如网络加载),请使用
AsyncImage并监听on_load事件更新 UI,避免空纹理; - 卡片
height建议用dp()转换(如dp(160)),以适配不同屏幕密度(需导入from kivy.metrics import dp); - 当数据量极大(>500 项)时,建议改用虚拟化列表(如
RecycleView)提升性能。
通过上述结构,你将获得一个响应式、可滚动、比例可控且视觉统一的动态图片网格,完美适配桌面与移动场景。










