2017-01-18 6 views
0

Hiho、javafx内の個々のListViewアイテムをリフレッシュする

ListViewを「きれいに」更新するのに問題があります。それはプレイリストの項目の束を持つプレイリストです。基本的には、アイテムのスタイルやコンテンツが変更されたときに、ListViewで変更する必要があります。現在、リスト全体がリフレッシュされていますが、それは私の思うように機能しますが、私にとって本当に貧しい(不明瞭な)解決策のように思えます(そして、それはちらつきます)。特定のアイテムを更新/再描画する方法はありますか?私は何かを見つけることができませんでした。参考のため

次のことが発生したときに、各項目を更新する必要があります:

  • 項目に関連したファイルを読み込み、それが失敗例えばされています。またはメタデータが取り出される。
  • または、ユーザ入力から、例えば現在の曲を変更するときなどである。

何とかリスナーを使用できますか?私はバインディングなどを見てきましたが、私が探しているものが見つからないようです。すべてのヘルプは大幅に再び

編集を感謝:

以下の作業のコードをリストを初期化:工場で作成された

protected Playlist(List<PlaylistItem> list){ 
    ... 

    // initialise the items 
    backup = FXCollections.observableArrayList(list); 
    setItems(backup); 

    // Use a custom CellFactory 
    setCellFactory(new Callback<ListView<PlaylistItem>, ListCell<PlaylistItem>>() { 
     @Override 
     public ListCell<PlaylistItem> call(ListView<PlaylistItem> list) { 
      return new PlaylistCell(); 
     } 
    }); 

    ... 
} 

細胞:

private class PlaylistCell extends ListCell<PlaylistItem> { 
    private PlaylistItem lastItem = null; 
    private final BooleanProperty booleanProperty = new SimpleBooleanProperty(false); 

    /** Add a listener to the boolean property upon construction */ 
    private PlaylistCell() { 
     booleanProperty.addListener(new ChangeListener<Boolean>() { 
      @Override 
      public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 
       if (newValue) { updateItem(lastItem, lastItem == null); }; 
      } 
     }); 
    } 

    @Override 
    public void updateItem(PlaylistItem item, boolean empty) { 
     super.updateItem(item, empty); 
     booleanProperty.set(false); 

     if (lastItem != item){ 
      // remove the pointer if we change item 
      if (lastItem != null && booleanProperty == lastItem.getBooleanProperty()) 
       lastItem.setBooleanProperty(null); 
      // and attach it to the new item 
      if (item != null) 
       item.setBooleanProperty(booleanProperty); 
     } 

     // redraw the cell 
     if (!empty && item != null) { 
      lastItem = item; 

      // current song in bold 
      if (item.equals(current)) { 
       setId("current-item"); 
      } else{ 
       setId(null); 
      } 

      // mark queued songs & update text 
      if (queue.contains(item)) { 
       int i = queue.indexOf(item); 
       super.setText(item.toString() + "\t (" + (i + 1) + (i != queue.lastIndexOf(item) ? ", ...)": ')')); 
      } else { 
       super.setText(item.toString()); 
      } 
     } 

     // draw an empty cell 
     else { 
      lastItem = null; 
      setText(null); 
      setId(null); 
     } 

    } 

} 

そして私例えばダブル現在のアイテムをクリックするか、アイテムをキューに入れると、BooleanPropertyをtrue(つまり変更されている)に設定して、更新呼び出しをトリガーします。 queueを想定し

+2

何をしているのかを示すコードを投稿できますか?具体的には、リスト内の項目が[JavaFXプロパティ](http://www.oracle.com/pls/topic/lookup?ctx=javase80&id=JFXBD107)を使用している場合は、[extractor] (http://stackoverflow.com/questions/23822550/listview-is-not-reflecting-changes)。 –

+0

James_D:いくつかのコードを追加しました。私はStringPropertyをすばやく使ってみましたが、わかっている限り、各項目に対して1つのリスナーが必要なので、単純すぎるほど複雑すぎるようでした。私はリストから検索することもできるので、どちらかの方法でエクストラクタを見ていきます。ありがとう – user6502063

+0

あなたがしていることを理解するのに十分なコードはありません。しかし、私が言ったように、あなたはextractorで 'ObservableList'を使うべきです(前のコメントのAPI docsへのリンクを見てください)。 http://stackoverflow.com/questions/23822550/listview-is-not-reflecting-changes –

答えて

0

を再生するキュー内の項目のObservableList<PlaylistItem>あり、そしてcurrentObjectProperty<PlaylistItem>(または類似した何か)で現在再生中のアイテムを表す、私はあなたがちょうど私が「

private class PlaylistCell extends ListCell<PlaylistItem> { 

    public PlaylistCell() { 

     textProperty().bind(Bindings.createStringBinding(this::calculateText, 
      itemProperty(), emptyProperty(), current, queue); 
     idProperty().bind(Bindings 
      .when(current.isEqualTo(itemProperty()) 
      .then("current-item") 
      .otherwise("")); 
    } 

    private String calculateText() { 
     if (isEmpty() || getItem() == null) return null ; 

     PlaylistItem item = getItem(); 

     if (queue.contains(item)) { 
      int index = queue.indexOf(item) ; 
      boolean unique = index == queue.lastIndexOf(item); 
      return String.format("%s (%d%s)", item ,index + 1, (unique ? "" : ", ...")); 
     } else { 
      return item.toString(); 
     } 
    } 
} 

ようなものが必要だと思いますID CSS PseudoClassを使用しての代わりに、操作をお勧めしますdは:

private class PlaylistCell extends ListCell<PlaylistItem> { 

    private final BooleanBinding isCurrent = current.isEqualTo(itemProperty()); 
    private final PseudoClass currentPC = PseudoClass.getPseudoClass("current"); 

    public PlaylistCell() { 

     textProperty().bind(Bindings.createStringBinding(this::calculateText, 
      itemProperty(), emptyProperty(), current, queue); 
     isCurrent.addListener((obs, wasCurrent, isNowCurrent) -> 
      pseudoClassStateChanged(currentPC, isNowCurrent)); 
    } 

    // ... 
} 

をして、セレクタを使用するようにCSSを変更

バインディングを作成する代わりに
.list-cell:current { /* ... */ } 

代わりの

#current-item { /* ... *. } 

、あなたはまた、現在のアイテムを演奏いずれかのとき、通常updateItem()メソッドが呼び出されることになりますどの、extractorを使用してデータリストを作成することができます、またはキュー、変更:

ObservableList<PlaylistItem> items = FXCollections.observableArrayList(
    item -> new Observable[] { current, queue /* , other properties the toString() depends on...*/ }); 

ListView<PlaylistItem> listView = new ListView<>(); 
listView.setItems(items); 

、その後

public class PlaylistCell extends ListCell<PlayListItem> { 

    private final PseudoClass currentPC = PseudoClass.getPseudoClass("current"); 


    @Override 
    protected void updateItem(PlaylistItem item, boolean empty) { 

     pseudoClassStateChanged(currentPC, current.isEqualTo(item)); 

     if (empty) { 
      setText(null); 
     } else { 
      if (queue.contains(item)) { 
       int index = queue.indexOf(item) ; 
       boolean unique = index == queue.lastIndexOf(item); 
       return String.format("%s (%d%s)", item ,index + 1, (unique ? "" : ", ...")); 
      } else { 
       return item.toString(); 
      } 
     } 
    } 
} 
関連する問題