I am currently trying to replace some features in an app using the ListView with the RecycleView.
From the documentation I can't figure out how to that though.
The current code looks similar to this:
ListView:
id: x
adapter:
sla.SimpleListAdapter(data=[], cls=label.Label)
x.adapter.data.append(‘frank’)
Are there any resources or tips on how to achieve this?
I am trying to use the recycleview because the ListView seems to be deprecated now.
ListView is removed in Kivy version 1.11.0. Below snippets show the equivalent in RecycleView. There are two examples in Kivy documentation for RecycleView.
The snippets below show the equivalent using RecycleView.
Snippets: kv file
RecycleView:
id: x
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Snippets: py file
x.data.append({'text‘: 'frank’)
Examples
The following three examples illustrate Kivy RecycleView.
How to fetch data from database and show in table
How to add vertical scroll in RecycleView
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
Builder.load_string('''
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
id: rv
# Optional: ScrollView attributes
bar_width: 5
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
effect_cls: "ScrollEffect"
scroll_type: ['bars', 'content']
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
''')
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(100)]
self.data.insert(0, {'text': 'frank'})
self.data.append({'text': 'liam'})
class TestApp(App):
title = 'Kivy RecycleView Demo'
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
Output
Related
What i'm trying to do is find a way to refresh a table made by using recycleview,
If you press one of the label created, my code delete data from table, but not refresh the table on a graphic level
Obviously if you press the same label 2 times, python gives ValueError...
Here's my code:
temp.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import BooleanProperty
from kivy.properties import ListProperty
class Sfondo_tabella(RecycleDataViewBehavior, Label):
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and self.selectable:
x = Screen_1()
x.remove(self.text)
class Screen_1(Screen):
data_items = ListProperty([])
data_items = ['1', '2', '3']
def remove(self, data):
self.data_items.remove(data)
class TempApp(App):
pass
if __name__ == '__main__':
TempApp().run()
temp.kv
ScreenManager:
id: screen_manager
name: "screen_manager"
Screen_1:
id: screen_1
name: "Screen_1"
manager: screen_manager
<Sfondo_tabella>:
# Draw a background to indicate selection
color: 0,0,0,1
font_size: self.height * 0.5
text_size: self.width, None
valign: 'top'
halign: 'center'
canvas.before:
Color:
rgba: (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
canvas:
Color:
rgba:0,0,0,1
Line:
width:0.5
rectangle:(self.x,self.y,self.width,self.height)
<Screen_1>:
BoxLayout:
RecycleView:
viewclass: 'Sfondo_tabella'
data: [{'text': str(x)} for x in root.data_items]
RecycleGridLayout:
cols: 10
size_hint: 1, None
default_size: None, dp(20)
default_size_hint: 1, None
height: self.minimum_height
width: self.minimum_width
In your code you are not updating the RecycleView data. The refresh_view_attrs function should be overloaded to get the index of the label clicked/touched upon and then the data needs to be updated accordingly. Following is the updated code for temp.py:
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import BooleanProperty
from kivy.properties import ListProperty
class Sfondo_tabella(RecycleDataViewBehavior, Label):
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
self.index = index
return super(Sfondo_tabella, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and self.selectable:
x = Screen_1()
x.remove(self.text)
self.parent.parent.data.pop(self.index)
class Screen_1(Screen):
data_items = ListProperty([])
data_items = ['1', '2', '3']
def remove(self, data):
self.data_items.remove(data)
class TempApp(App):
pass
if __name__ == '__main__':
TempApp().run()
This will update properly the data of RecycleView and the GUI accordingly.
I'm trying to better understand how the RecycleView functions. Seems like only practical examples will teach me. Docs aren't helping. Kindly have a look at my current screen in the pic below.
Now following are what I'm trying to achieve.
Add/Remove new rows to/from the list.
The first column sl/no should maintain the order even after deleting from in between.
How to achieve 'sort' function? Is it by taking the data into python and do the sort and then update that to the RV again ?
Please find the both .py and .kv codes below.
rv_main.py
import os
os.environ['KIVY_GL_BACKEND'] = 'gl'
import kivy
kivy.require('1.11.0')
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.properties import ListProperty, BooleanProperty
from kivy.properties import NumericProperty
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
#-----------------------------------------------------------------------
class RecycleViewRow(RecycleDataViewBehavior, BoxLayout):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
slno = StringProperty('')
typ = StringProperty('')
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(RecycleViewRow, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(RecycleViewRow, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected:
pass
else:
pass
def delete_row(self,rv, index, is_selected):
if is_selected:
rv.data.pop(index)
rv.layout_manager.clear_selection()
#-----------------------------------------------------------------------
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
#fetch data from the database
self.data = [{'slno': str(x+1),'typ': 'default'} for x in range(4)]
#-----------------------------------------------------------------------
class DataTable(BoxLayout):
def addRow(self):
pass
def removeRow(self):
pass
#-----------------------------------------------------------------------
class RvMainApp(App):
def build(self):
return DataTable()
if __name__ == '__main__':
RvMainApp().run()
rvmain.kv
#: kivy 1.11.0
<RecycleViewRow>:
id: rv
slno: ""
typ: ""
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0.4, 0.4, 0.4, 1)
Rectangle:
pos: self.pos
size: self.size
orientation: 'horizontal'
size_hint: 1.0, 1.0
Label:
text: root.slno
size_hint_x : 1.0
Label:
text: root.typ
size_hint_x : 1.0
#----------------------------------------------------------------
<RV>:
id : rv
viewclass: 'RecycleViewRow'
SelectableRecycleBoxLayout:
default_size: None, dp(40)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
#----------------------------------------------------------------
<DataTable>:
orientation : 'vertical'
Button:
BoxLayout:
Button:
RV:
Button:
BoxLayout:
Button:
text: "Add"
on_release: rv.data.append({"slno": "?", "typ": 'default'})
Button:
text: "Remove"
on_release:
You edit the data list of the recycleview. The data will be sorted as it is sorted in that list.
So here is an example of the add remove feature:
from kivy.app import App
from kivy.lang import Builder
KV = '''
<Row#BoxLayout>:
ind: 1
Button:
text: str(root.ind)
Button:
text: "default"
BoxLayout:
ind: 1
orientation: "vertical"
Button:
BoxLayout:
Button:
RecycleView:
id: rv
data: [{"text":"first","ind":1}]
viewclass: 'Row'
RecycleBoxLayout:
default_size_hint: 1, None
default_size: None, dp(56)
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Button
BoxLayout:
Button:
text: "Add"
on_release:
root.ind += 1
rv.data.append({"ind": root.ind})
Button:
text: "Remove"
on_release:
root.ind = root.ind - 1 if root.ind > 0 else 0
if len(rv.data): rv.data.pop(-1)
'''
class Test(App):
def build(self):
self.root = Builder.load_string(KV)
return self.root
Test().run()
And here is an example on how to sort the data list by some key. In this case ind.
from kivy.app import App
from kivy.lang import Builder
KV = '''
RecycleView:
viewclass: 'Label'
data: sorted([{"text":"!", "ind":3},{"text":"world", "ind":2},{"text":"hello", "ind":1}], key=lambda k: k["ind"])
RecycleBoxLayout:
id: layout
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
'''
class TestApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
TestApp().run()
Based on the code that is posted on kivy docs about Recycle View, how do I change the data? how to change the size of the selectable labels? and especially if I want to have more widget on the screen, how do set the position of the list to be in the bottom side of the screen?
I've tried to change the position with GridLayout, BoxLayout and nothing happens.
'''
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
Builder.load_string('''
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
''')
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
else:
print("selection removed for {0}".format(rv.data[index]))
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(100)]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
'''
Question 1
How do I change the data?
Answer
Change the data by updating self.data
RecycleView » data
The RecycleView is generatad by processing the data (i.e.
self.data), essentially a list of dicts, and uses these dicts to
generate instances of the viewclass as required.
Question 2
How to change the size of the selectable labels?
Answer
The size especially the height of each selectable widgets can be change in either Python script or kv file.
With reference to this example, the height can be changed by setting default_size: None, dp(30) at SelectableRecycleBoxLayout. As for the width of each selectable widgets, it will change according to the number columns in a row of data, self.data
With a SelectableRecycleGridLayout, one can specify the minimum width for each column by using cols_minimum
Question 3
If I want to have more widget on the screen, how to set the position
of the list to be in the bottom side of the screen?
Answer
Declare a root widget with inheritance of BoxLayout
Snippets
<RootWidget>:
orientation: 'vertical'
BoxLayout:
size_hint: 1, 0.8
BoxLayout:
size_hint: 1, 0.2
RV:
Output
Example
How to add vertical scroll in RecycleView
I've got some programs written in previous versions of Kivy that use ListViews. Using ListViews, it was quite easy to get the selected node via the adapter. However, it is much less clear how to do this with a RecycleView. Now, it is the case that rv.layout_manager.selected_nodes can be used to get the selected value, but there are also times where I'm interested in the actual node. Also, the following snippet can be used to generate the nodes, but apparently, they aren't the actual nodes in the RecycleView.
opts = rv.layout_manager.view_opts
for i in range(len(rv.data)):
s = rv.view_adapter.get_view(i, rv.data[i], opts[i]['viewclass'])
print s.text, s.selected
I'd be interested in finding a way of getting the selected node from the RecycleView.
Full code:
import random
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
#Aside: the code in the string would need to be indented back one tab, but it's like this for SO formatting
Builder.load_string('''
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
key_selection: "True"
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: True
touch_deselect_last: True
''')
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(random.random())} for x in range(20)]
class TestApp(App):
def build(self):
self.rv = RV()
self.rv.layout_manager.bind(selected_nodes=self.selectionChange)
return self.rv
def selectionChange(self, inst, val):
print inst, val
if __name__ == '__main__':
b = TestApp()
b.run()
please show us your code. in the rv.data you can add a parameter 'selected' = 0 when the node is not selected and 1 when it is this is an example:
class TestRecycleView(RecycleView):
def __init__(self, **kwargs):
super(TestRecycleView, self).__init__(**kwargs)
self.data = [{'name': 'test', 'ind':0,'selected': 0}]
class TestBox(RecycleDataBehaviour, BoxLayout):
def action(self, boss):
if boss.data[self.ind]['selected'] == 0:
boss.data[self.ind]['selected'] = 1
else:
boss.data[self.ind]['selected'] = 0
the kv:
<TestRecycleView>:
viewclass: 'TestBox'
RecycleBoxLayout:
default_size: None, 440
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<TestBox>:
Button:
on_press: root.action(root.parent.parent)
if i understand well you want to print the selectedLabel value:
class TestApp(App):
def build(self):
self.rv = RV()
self.rv.layout_manager.bind(selected_nodes=self.selectionChange)
return self.rv
def selectionChange(self, inst, val):
print self.rv.data[val[0]]['text']
proof(image link)
I would like to make a recycleview that has multiple labels in each recycleview row. In my specfic example I would like to have 3 labels in each row: 1 label containing the item index, one label containing an item from one dataset, and another label from another dataset
In this example (taken from the kivy examples) we have a recycleview where each row in the recycleview contains a single label:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
Builder.load_string('''
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
''')
items_1= {'apple', 'banana', 'pear', 'pineapple'}
items_2= {'dog', 'cat', 'rat', 'bat'}
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
else:
print("selection removed for {0}".format(rv.data[index]))
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in items_1]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
I would like each recycleview row to have 3 labels: first label is the index, second label is items_1 and third label is items_2. Like this:
0 apple dog
1 banana cat
2 pear rat
3 pineapple bat
Thank you!
I was looking for a similar solution and could not find it. I do not think PalimPalim has really answered the question since I think the Ben t was looking for multiple label widgets treated as a single line.
In this case you use a custom widget for GridLayout and specify it's structure.
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
label1_text: 'label 1 text'
label2_text: 'label 2 text'
label3_text: 'label 3 text'
pos: self.pos
size: self.size
Label:
id: id_label1
text: root.label1_text
Label:
id: id_label2
text: root.label2_text
Label:
id: id_label3
text: root.label3_text
In applying your data into the RV, you will need to restructure the dictionary to reflect the label layout
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
paired_iter = zip(items_1, items_2)
self.data = []
for i1, i2 in paired_iter:
d = {'label2': {'text': i1}, 'label3': {'text': i2}}
self.data.append(d)
Finally in the refresh_view_attrs, you will specify .label_text which is bound to each label, or you can use label id's.
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
self.label1_text = str(index)
self.label2_text = data['label2']['text']
self.ids['id_label3'].text = data['label3']['text'] # As an alternate method of assignment
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
The entire code is below:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
Builder.load_string('''
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
label1_text: 'label 1 text'
label2_text: 'label 2 text'
label3_text: 'label 3 text'
pos: self.pos
size: self.size
Label:
id: id_label1
text: root.label1_text
Label:
id: id_label2
text: root.label2_text
Label:
id: id_label3
text: root.label3_text
<RV>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
''')
items_1 = {'apple', 'banana', 'pear', 'pineapple'}
items_2 = {'dog', 'cat', 'rat', 'bat'}
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, GridLayout):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
cols = 3
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
self.label1_text = str(index)
self.label2_text = data['label2']['text']
self.ids['id_label3'].text = data['label3']['text'] # As an alternate method of assignment
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
else:
print("selection removed for {0}".format(rv.data[index]))
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
paired_iter = zip(items_1, items_2)
self.data = []
for i1, i2 in paired_iter:
d = {'label2': {'text': i1}, 'label3': {'text': i2}}
self.data.append(d)
# can also be performed in a complicated one liner for those who like it tricky
# self.data = [{'label2': {'text': i1}, 'label3': {'text': i2}} for i1, i2 in zip(items_1, items_2)]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
the easiest way is to change from a RecycleBoxLayout to RecycleGridLayout with
3 columns and using the following list items = [0, "apple", "dog", 1, "banana", "cat", 2, "pear", "rat", 3, "pineapple", "bat"] Obviously, you could stick to you original list data structure and merge them together to form the list above, but I will leave that for you ;).
Another option which should be possible is to add to the RecycleBoxLayout a RecycleBoxLayout with an horizontal orientation per row.
This is all the python code.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
Builder.load_string('''
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
viewclass: 'SelectableLabel'
SelectableRecycleGridLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
cols: 3
''')
items = [0, "apple", "dog", 1, "banana", "cat", 2, "pear", "rat", 3, "pineapple", "bat"]
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
else:
print("selection removed for {0}".format(rv.data[index]))
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in items]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()