Kivy 滚动视图中的背景
测试应用的总结:我正在写一个Kivy应用,这个应用有一个可以滚动的视图(叫做Scroller
),里面有很多个字段(叫做Field
)供查看。这些字段有时候很难区分,所以我决定给每个字段使用交替的背景颜色,以帮助彼此区分。我的测试应用使用了20个独立的字段,每个字段的背景颜色在深灰色和更深的灰色之间交替。
测试过程:启动应用后,程序看起来很不错。交替的背景颜色显示得很好。即使我向下滚动,应用也看起来没问题。然而,当我向上滚动时,应用就变得奇怪了。文本跟着应用一起滚动,但背景却不动。更糟糕的是,文本开始和邻近的背景混在一起,变得模糊不清。这个问题在我再次向下滚动(超过了向上滚动的最高点)时似乎就消失了。
问题简述:在向上滚动时,Field
的“背景颜色”导致了应用出现问题。
附注:我还注意到,滚动太多后,应用变得有点卡顿。我对Kivy的绘制周期不是很熟悉,但背景的处理不应该导致过多的延迟。
测试应用:
import kivy
kivy.require('1.0.7')
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.graphics import Color, Rectangle
class Main(App):
def build(self):
self.root = GridLayout(rows = 1)
self.root.add_widget(Scroller())
return self.root
class Scroller(ScrollView):
def __init__(self):
ScrollView.__init__(self)
self.view = GridLayout(cols = 1, size_hint = (1, None))
self.add_widget(self.view)
self.view.bind(minimum_height = self.view.setter('height'))
for i in range(20):
self.view.add_widget(Field('Test field {}'.format(i),i%2 is 0))
class Field(GridLayout):
def __init__(self, name, bg):
assert isinstance(name, str)
assert isinstance(bg, bool)
self.bg = bg
GridLayout.__init__(self,
rows = 1,
padding = 10,
size = (0, 60),
size_hint = (1, None))
self.add_widget(Label(text = name))
self.add_widget(Button(text = 'Test button',
size = (200, 0),
size_hint = (None, 1)))
self.bind(pos = self.change_background)
self.bind(size = self.change_background)
def change_background(self, *args):
with self.canvas.before:
if self.bg:
Color(0.2, 0.2, 0.2, mode = 'rgb')
else:
Color(0.1, 0.1, 0.1, mode = 'rgb')
Rectangle(pos = self.pos, size = self.size)
if __name__ in ('__main__', '__android__'):
app = Main()
app.run()
1 个回答
3
def change_background(self, *args):
self.canvas.before.clear()#<- clear previous instructions
with self.canvas.before:
if self.bg:
Color(0.2, 0.2, 0.2, mode = 'rgb')
else:
Color(0.1, 0.1, 0.1, mode = 'rgb')
Rectangle(pos = self.pos, size = self.size)
每次当字段的位置或大小变化时,你都在画布上添加新的指令,但没有清除之前的指令。
你还应该考虑使用kv,因为对于超过小片段的代码,这样做能节省你很多时间。你可以像这样将你的代码转换为kv格式:
import kivy
kivy.require('1.0.7')
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.properties import ObjectProperty, BooleanProperty
from kivy.lang import Builder
Builder.load_string('''
<Scroller>
# root is Scroller here
# create a new ObjectProperty in kv that holds the ref to Gridlayout
# so you can access the instance in python code
view: glayout
GridLayout:
id: glayout
cols: 1
size_hint: (1, None)
height: self.minimum_height
<Field>
canvas.before:
Color:
rgba: (0.2, 0.2, 0.2, 1) if self.bg else (0.1, 0.1, 0.1, 1)
Rectangle:
# binding properties is done implicitly and instructions aren't
# piled up while doing that.
pos: self.pos
# self here refers to Field as `self` is supposed to refer to the
# Widget not the drawing instruction
size: self.size
rows: 1
padding: 10
size: (0, 60)
size_hint: (1, None)
Label:
text: root.name
Button:
text: 'test button'
size: (200, 0)
size_hint: (None, 1)
''')
class Main(App):
def build(self):
self.root = GridLayout(rows = 1)
self.root.add_widget(Scroller())
return self.root
class Scroller(ScrollView):
def __init__(self, **kwargs):
super(Scroller, self).__init__(**kwargs)
for i in range(20):
# access self.view that was set in kv
self.view.add_widget(
Field(
name = 'Test field {}'.format(i),
bg = i%2 is 0))
class Field(GridLayout):
# use kivy's Properties so it becomes easier to observe and apply changes
# as a plus these can also be directly used in kv. As a advantage of using this now
# you can change name and bg dynamically and the changes should be reflected on
# screen
name = ObjectProperty('Test field uninitialized')
bg = BooleanProperty(False)
if __name__ in ('__main__', '__android__'):
app = Main()
app.run()