LoginSignup
2
1

More than 1 year has passed since last update.

RecycleViewのviewclassが自身が表示しているdataのindexを知る方法

Last updated at Posted at 2020-04-26

RecycleViewはdataを表示するだけならそんなに苦にならないですが、user側の操作によってdataを編集しようとした時にこれまでは色々と苦労していました。ただ最近になって簡単な方法に気付いたのでここに記録します。

例えば次のようにLabelCheckBoxを並べて

Screenshot at 2020-04-26 16-04-36.png

"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の状態を持つ事になります。

Untitled Diagram(3).png

この三者を適切に同期しないといけないのですが、どうするかが悩みの種でした。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又はCheckBoxrvへの参照を得る方法と
  • 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してある物を取り除いて完成。

code全体
Youtube

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1