2017-05-07 101 views
1

我正在開發一個Kivy媒體播放器並使用RecycleView顯示我的媒體列表。選擇下一個/上一個Kivy RecycleView按鈕上的行按

我試圖實現的是將現有的行選擇從按鈕選擇更改爲RecycleView列表中的下一行或上一行。

我可以看到LayoutSelectionBehavior類看起來像合適的方法,但我正在努力尋找一種方法來使用它。

下面是從kivy.uix.recycleview例子

# -*- coding: utf-8 -*- 
from kivy.app import App 
from kivy.lang import Builder 
from kivy.uix.boxlayout import BoxLayout 
from kivy.uix.recycleview import RecycleView 
from kivy.uix.recycleview.views import RecycleDataViewBehavior 
from kivy.uix.label import Label 
from kivy.properties import BooleanProperty, StringProperty, NumericProperty 
from kivy.uix.recycleboxlayout import RecycleBoxLayout 
from kivy.uix.behaviors import FocusBehavior 
from kivy.uix.recycleview.layout import LayoutSelectionBehavior 


kv = """ 

<SelectableLabel>: 
    # Draw a background to indicate selection 
    canvas.before: 
     Color: 
      rgba: (0.4, 0.4, 0.4, 1) if self.selected else (0.5, 0.5, 0.5, 1) 
     Rectangle: 
      pos: self.pos 
      size: self.size 

<KivyPlayer>: 
    canvas: 
     Color: 
      rgba: 0.3, 0.3, 0.3, 1 
     Rectangle: 
      size: self.size 
      pos: self.pos 
    orientation: 'vertical' 
    BoxLayout: 
     orientation: 'vertical' 
     BoxLayout: 
      Button: 
       id: next_track 
       text: "Next Track" 
      Button: 
       id: previous_track 
       text: "Previous Track" 
     BoxLayout: 
      RecycleView: 
       id: media_list 
       viewclass: 'SelectableLabel' 
       scroll_type: ['bars', 'content'] 
       scroll_wheel_distance: dp(114) 
       bar_width: dp(10) 
       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 
        spacing: dp(2) 


""" 

Builder.load_string(kv) 

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 KivyPlayer(BoxLayout): 
    ''' Main Kivy class for creating the initial BoxLayout ''' 

    def __init__(self, **kwargs): 
     super(KivyPlayer, self).__init__(**kwargs) 

     # Set media_list data 
     self.ids.media_list.data = [{'text': str(x)} for x in range(100)] 


class KivyApp(App): 
    def build(self): 
     return KivyPlayer() 


if __name__ == '__main__': 
    KivyApp().run() 

任何建議或協助,將不勝感激修改RecycleView的簡化示例。

回答

2

我想如果我們添加一個get_next/previous_node,或許將來會更好。無論如何,這就是你所做的。

修改按鈕,以便:

BoxLayout: 
    Button: 
     id: next_track 
     text: "Next Track" 
     on_release: controller.select_next() 
    Button: 
     id: previous_track 
     text: "Previous Track" 
     on_release: controller.select_previous() 

而在KV編輯SelectableRecycleBoxLayout如下:

SelectableRecycleBoxLayout: 
    id: controller 
    key_selection: 'selectable' 

最後,這裏的SelectableRecycleBoxLayout應該是什麼樣子:

class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, 
           RecycleBoxLayout): 
    ''' Adds selection and focus behaviour to the view. ''' 

    def get_nodes(self): 
     nodes = self.get_selectable_nodes() 
     if self.nodes_order_reversed: 
      nodes = nodes[::-1] 
     if not nodes: 
      return None, None 

     selected = self.selected_nodes 
     if not selected: # nothing selected, select the first 
      self.select_node(nodes[0]) 
      return None, None 

     if len(nodes) == 1: # the only selectable node is selected already 
      return None, None 

     last = nodes.index(selected[-1]) 
     self.clear_selection() 
     return last, nodes 

    def select_next(self): 
     last, nodes = self.get_nodes() 
     if not nodes: 
      return 

     if last == len(nodes) - 1: 
      self.select_node(nodes[0]) 
     else: 
      self.select_node(nodes[last + 1]) 

    def select_previous(self): 
     last, nodes = self.get_nodes() 
     if not nodes: 
      return 

     if not last: 
      self.select_node(nodes[-1]) 
     else: 
      self.select_node(nodes[last - 1]) 

也需要在數據中指定應該如何選擇:self.ids.media_list.data = [{'text': str(x), 'selectable': True} for x in range(100)](請注意,在您的示例中您忘記了ids部分)。

+0

感謝您花時間閱讀本文並提供@matt的詳細回覆。您的解決方案運作完美。我會閱讀使用'ids',因爲我以前沒有見過。 SO主持人,如果我應該發表一個新問題,不能確定在回覆中詢問其他問題的政策。 @matt是否有一種從KivyPlayer類的init方法中選擇第一個/下一個節點的簡單方法?如果你在那裏處理,很樂意在#kivy IRC中討論? – campervancoder

+0

我發現了一個hacky的方法,它是使用ButtonBehavior類和trigger_action方法'self.next_track.trigger_action(duration = 0.1)'我懷疑有更清晰的方法:) – campervancoder

+0

您確實需要使用這個時鐘。但是像'Clock.schedule_once(lambda * x:self.ids.controller.select_node(0),0.1)'。但是你應該詢問IRC是否不起作用(我也是'matham')。 – Matt

相關問題