RecycleView
はdataを表示するだけならそんなに苦にならないですが、user側の操作によってdataを編集しようとした時にこれまでは色々と苦労していました。ただ最近になって簡単な方法に気付いたのでここに記録します。
例えば次のようにLabel
とCheckBox
を並べて
"delete selection"Button
を押すことで選択されている行を消せるようにしたいとします。この場合widget階層は次のような感じになると思います。
KV_CODE = '''
<RVItem>:
Label:
text: root.text
CheckBox:
active: root.is_checked
BoxLayout:
orientation: 'vertical'
BoxLayout:
Button:
text: 'add data'
Button:
text: 'delete selection'
RecycleView:
id: rv
viewclass: 'RVItem'
RecycleBoxLayout:
orientation: 'vertical'
'''
class RVItem(Factory.BoxLayout):
text = StringProperty()
is_checked = BooleanProperty()
すると三者がCheckBox
の状態を持つ事になります。
この三者を適切に同期しないといけないのですが、どうするかが悩みの種でした。AとBの同期は既にできています。AはRecycleView
自体がやってくれててBはKv言語の以下の行でできています。
<RVItem>:
Label:
text: root.text
CheckBox:
active: root.is_checked # B
userがCheckBox
を押した時にはCheckBox.active
が変わるのでCとDの同期も行わないといけないのですが、Cは簡単です。例えば
<RVItem>:
Label:
text: root.text
CheckBox:
active: root.is_checked
on_press: root.is_checked = self.active # C
とするだけなので。問題はDで
-
RVItem
又はCheckBox
がrv
への参照を得る方法と -
RVItem
が自身が表示しているdataのindexを得る方法
を考えないといけません。前者は
class RVItem(Factory.BoxLayout):
@property
def rv(self):
return self.parent.recycleview
で解決したので残るは後者なのですが、これまではdataに一意の印を持たせそれを線形探索する事でindexを求めていました。ところが何の事は無く、実は次のようにするだけでindexは求められるのでした。
class RVItem(Factory.BoxLayout):
def get_view_index(self):
return self.parent.get_view_index_at(self.center)
これを用いることでDの同期は以下のように実現できました。
<RVItem>:
Label:
text: root.text
CheckBox:
active: root.is_checked
on_press:
active = self.active
root.is_checked = active # C
root.rv.data[root.get_view_index()]['is_checked'] = active # D
後は"delete selection"Button
が押された時に
Button:
text: 'delete selection'
on_press: rv.data = [record for record in rv.data if not record['is_checked']]
という風にcheckしてある物を取り除いて完成。