问题
I have several textinputs inside a ScrollView. When a 'normal' TextInput is on focus, I can easily scroll the view without losing the focus on the textinput. However, if the textinput opens a MeasureDropDown
(custom DropDown) widget, scrolling is not possible anymore. The behavior is actually sligthly different on Windows and Android:
- On Windows, I can scroll the view only if the mouse cursor is not above the TextInput;
- On Android, there is no way to scroll the view as long as the textInput has focus (resulting in some of dropdown items not to be selectable if they are "hidden" from the current view).
Below is a minimal code to reproduce the issue (which benefited greatly from help received here):
from kivy.app import App
from kivy.factory import Factory
from kivy.lang import Builder
from kivy.properties import BooleanProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.textinput import TextInput
class DelIngButton(Button): # Button to delete IngredientRow
pass
class DropListButton(Button): # Custom Button for DropDown widget
def __init__(self, **kwargs):
super(DropListButton, self).__init__(**kwargs)
self.bind(on_release=lambda x: self.parent.parent.select(self.text))
class DropListTextInput(TextInput):
# Provides a couple needed behaviors
def on_focus(self, *args):
if self.focus:
self.dropDown.selection_is_DLTI = True
else:
self.dropDown.selection_is_DLTI = False
def on_text_validate(self, *args):
self.dropDown.selection_is_DLTI = False
# put the text from this widget into the TextInput that the DropDown is attached to
self.dropDown.attach_to.text = self.text
# dismiss the DropDown
self.dropDown.dismiss()
class IngredientRow(BoxLayout):
def __init__(self, **kwargs):
super(IngredientRow, self).__init__(**kwargs)
self.dropdown = MeasureDropDown()
def handle_focus(self, ti):
# handle on_focus event for the measure TextInput
if ti.focus:
# open DropDown if the TextInput gets focus
self.dropdown.open(ti)
else:
# ti has lost focus
if self.dropdown.selection_is_DLTI:
# do not dismiss if a DropListTextInput is the selection
return
# dismiss DropDown
self.dropdown.dismiss(ti)
self.dropdown.unbind_all()
self.dropdown.fbind('on_select', lambda self, x: setattr(ti, 'text', x))
class MeasureDropDown(DropDown):
# set to True if the selection is a DropListTextInput
selection_is_DLTI = BooleanProperty(False)
def unbind_all(self):
for callBack in self.get_property_observers('on_select'):
self.funbind('on_select', callBack)
####################################
class AddWindow(Screen):
def addIngredient(self, instance): #adds a new IngredientRow
row = instance.parent
row.remove_widget(row.children[0])
row.add_widget(Factory.DelIngButton(), index=0)
self.ingsGrid.add_widget(Factory.IngredientRow(), index=0)
class WMan(ScreenManager):
def __init__(self, **kwargs):
super(WMan, self).__init__(**kwargs)
# kv = Builder.load_file("ui/layout.kv")
kv = Builder.load_string('''
#:set text_color 0,0,0,.8
#:set row_height '35sp'
#:set main_padding ['10sp', '10sp']
#:set small_padding ['5sp', '5sp']
<DropListButton>: # Button for custom DropDown
color: text_color
background_normal: ''
<DelIngButton>: # Button to delete row
text: '-'
size_hint: None, None
height: row_height
width: row_height
on_release: self.parent.parent.remove_widget(self.parent)
<MeasureDropDown>:
id: dropDown
DropListButton:
size_hint: 1, None
height: row_height
text: "g"
DropListButton:
size_hint: 1, None
height: row_height
text: "Kg"
DropListButton:
size_hint: 1, None
height: row_height
text: "L"
DropListTextInput: # CustomTextInput instead of standard TextInput
dropDown: dropDown # provide easy access to the DropDown
size_hint: 1, None
height: row_height
hint_text: 'new'
multiline: False # needed to trigger on_text_validate
<IngredientRow>:
orientation: 'horizontal'
size_hint: 1, None
height: row_height
spacing: '5sp'
TextInput:
id: ing
hint_text: 'Ingredient'
multiline: False
size_hint: .6, None
height: row_height
TextInput:
id: quant
hint_text: 'Quantity'
multiline: False
size_hint: .2, None
height: row_height
TextInput:
id: measure
hint_text: 'measure'
size_hint: .2, None
height: row_height
on_focus:
root.handle_focus(self)
Button:
id: addIng
text: "+"
size_hint: None, None
height: row_height
width: row_height
on_release: app.root.ids.add.addIngredient(self)
<MainScrollView@ScrollView>:
size_hint: 1, None
scroll_type: ['bars', 'content']
##################
# Windows
##################
WMan:
AddWindow:
id: add
<AddWindow>:
name: 'add'
ingsGrid: ingsGrid
ingredientRow: ingredientRow
MainScrollView:
height: self.parent.size[1]
GridLayout:
cols:1
size_hint: 1, None
pos_hint: {"top": 1}
height: self.minimum_height
padding: main_padding
StackLayout:
id: ingsGrid
size_hint: 1, None
height: self.minimum_height
orientation: 'lr-tb'
padding: small_padding
IngredientRow:
id: ingredientRow
''')
class TryApp(App):
def build(self):
return kv
if __name__ == "__main__":
TryApp().run()
来源:https://stackoverflow.com/questions/62510256/kivy-textinput-with-dropdown-widget-does-not-allow-scrolling