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は簡単です。例えば
```yaml
<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を得る方法
を考えないといけません。前者は
```python
class RVItem(Factory.BoxLayout):
@property
def rv(self):
return self.parent.recycleview
```
で解決したので残るは後者なのですが、これまではdataに一意の印を持たせそれを線形探索する事でindexを求めていました。ところが何の事は無く、実は次のようにするだけでindexは求められるのでした。
```python
class RVItem(Factory.BoxLayout):
def get_view_index(self):
return self.parent.get_view_index_at(self.center)
```
これを用いることでDの同期は以下のように実現できました。
```yaml
<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`が押された時に
```yaml
Button:
text: 'delete selection'
on_press: rv.data = [record for record in rv.data if not record['is_checked']]
```
という風にcheckしてある物を取り除いて完成。
[code全体](https://gist.github.com/gottadiveintopython/78ece8aab3fbb2cadfa1e30e1b3cc703)
[Youtube](https://youtu.be/1iB40vjQ-yw)