2013-06-02 60 views
6

JavaFX 2モーダルダイアログウィンドウでListViewコントロールを使用しています。JavaFX 2.2:ListViewの再描画/更新を強制する方法

このListViewは、セル工場で製造されたListCellsであるDXAliasインスタンスを表示します。ファクトリオブジェクトが行う主なことは、ListViewのUserDataプロパティデータを調べ、それをListCellに対応する項目と比較することです。それらが同じ場合、ListCellのコンテンツは赤で表示され、そうでない場合は黒で表示されます。これは、ListView内のどのアイテムが現在「デフォルト」として選択されているかを示すために行います。

private class AliasListCellFactory implements 
    Callback<ListView<DXSynonym>, ListCell<DXSynonym>> { 

@Override 
public ListCell<DXSynonym> call(ListView<DXSynonym> p) { 
    return new ListCell<DXSynonym>() { 

    @Override 
    protected void updateItem(DXSynonym item, boolean empty) { 
     super.updateItem(item, empty); 

     if (item != null) { 
      DXSynonym dx = (DXSynonym) lsvAlias.getUserData(); 

      if (dx != null && dx == item) { 
       this.setStyle("-fx-text-fill: crimson;");      
      } else { this.setStyle("-fx-text-fill: black;"); } 

      this.setText(item.getDxName()); 

     } else { this.setText(Census.FORMAT_TEXT_NULL); } 
    }}; 
} 

私が選択したDXAliasインスタンスを取り、によって、リストビューに新しいデフォルトを選択した項目を作る「handleAliasDefault()」と呼ばれるボタンのハンドラを持っている:あなたは私が何を意味するか見ることができるようにここに私のlistcellファクトリクラスですそれをListView:lsvAlias.setUserData(選択されたDXAlias)に格納します。ここではハンドラのコードは次のとおりです。

// Handler for Button[fx:id="btnAliasDefault"] onAction 
    @FXML 
    void handleAliasDefault(ActionEvent event) { 

     int sel = lsvAlias.getSelectionModel().getSelectedIndex(); 
     if (sel >= 0 && sel < lsvAlias.getItems().size()) { 
      lsvAlias.setUserData(lsvAlias.getItems().get(sel)); 
     } 
    } 

既定値に設定]ボタンをクリックに応答して行われる変更は、リストが正しく示すものではありません、バッキングObservableListを変更せずにリストビューのUserDataを()を変更することであるため、新しいデフォルト。

強制的にListViewにListCellを再描画する方法はありますか?この件に関して、Androidユーザーからは数千万の質問がありますが、JavaFXには幸福感はないようです。再描画を強制するために、バッキングアレイに「意味のない変更」を行わなければならない場合があります。 Javafx ListView refreshing

答えて

5

今のところ、リストビューを再描画して、選択したデフォルトを正しく指定するには、forceListRefreshOnという名前のメソッドを使用することができます(これは、 )は、私のボタンハンドラに:

@FXML 
void handleAliasDefault(ActionEvent event) { 

    int sel = lsvAlias.getSelectionModel().getSelectedIndex(); 
    if (sel >= 0 && sel < lsvAlias.getItems().size()) { 
     lsvAlias.setUserData(lsvAlias.getItems().get(sel)); 
     this.<DXSynonym>forceListRefreshOn(lsvAlias); 
    } 
} 

ヘルパーメソッドは、単にリストビューからObservableListをスワップアウトして、後ろにそれを交換、おそらくそのListCellsを更新するために、リストビューを強制的に:

private <T> void forceListRefreshOn(ListView<T> lsv) { 
    ObservableList<T> items = lsv.<T>getItems(); 
    lsv.<T>setItems(null); 
    lsv.<T>setItems(items); 
} 
0

はそう、あなたが方法をupdateItem使用する必要はありません。

は、私は、これはJavaFXの2.1のために頼まれたことがわかります。必要なのは、現在のデフォルトセル(ユーザーデータにある)のインジケータをOblectPropertyに保存することです。各セルからこのプロパティにリスナーを追加します。値が変更されたら、スタイルを再割り当てします。

しかし、私は、スクロールすると新しいセルが作成されますが、古いセルは作成されますが、古いものはバインディングを残さずにメモリリークを引き起こすという別の問題があります。したがって、シーンから削除するセルにリスナーを追加する必要があります。私は、それがnullになったときにparentPropertyにリスナーを追加することによって行うことができると思います。バインディングを削除してください。

UIを強制的に更新するべきではありません。既存のノードのプロパティ/レンダリングが変更されたときに自動的に更新されます。したがって、既存のノード(セル)の外観/プロパティを更新するだけで済みます。

+0

私は、コードを示さなかったが、 ListViewのデータはObservableList内にあります。各データモデル項目はJavaFXプロパティです。 ListViewのコントラクトでは、データが変更されるとビューが自動的に更新されます。データそのものが表示されていないときに、データの表示方法が変わったときに何が起こるべきかについて何も言わないようです。新しいバインディングを追加すると解決策が得られるかもしれませんが、非常に醜いコード、多分脆弱なコードを犠牲にしています。私が必要とするのは、選択したデフォルトが変更されたときにListCellを再描画することだけです。 – scottb

+0

これは、javafx 2の仕組みの共通の特性です。シーングラフのノード(特定のセル)がそのプロパティを変更して視覚的表現に影響を及ぼす場合、シーングラフは必要に応じてそれらのノード(セル)を再描画します。したがって、セルの視覚的外観を更新すると、シーングラフによって再描画されます。 –

+0

私はあなたが言っていることを見ていますが、実際に私のデータモデルの一部ではないプロパティを含めるために、私のデータモデルをリファクタリングする必要があるとも思いません。私は、選択したデフォルトエイリアスを黒ではなく赤でレンダリングしたいだけです。 1つの簡単な方法があれば問題は解決します:lsvAlias.requestRefresh(); – scottb

12

わかりませんこれがJavで動作する場合aFX 2.2ですが、それはJavaFX 8で動作しています。これは、全体の監視可能なコレクションを交換することなくupdateItemを呼び出します

public void refresh() { 
    super.flow.recreateCells(); 
} 

:あなたはあなた自身のListViewSkinを作成し、同じようrefreshメソッドを追加する必要があります。

また、新しいスキンを使用するために、あなたはFXMLを使用している場合には、それをインスタンス化し、コントローラのinitialize方法でそれを設定する必要があります。

MySkin<Subscription> skin = new MySkin<>(this.listView); // Injected by FXML 
this.listView.setSkin(skin); 
... 
((MySkin) listView.getSkin()).refresh(); // This is how you use it  

私はの動作をデバッグした後、これを見つけましたアコーディオン。このコントロールは、展開するたびにListViewを更新します。

+0

JavaFX 2.2で正常に動作するようです。 –

+1

がここにこれが私のOSSプロジェクトの一つに使用されている方法は次のとおりです。https://github.com/Eldelshell/JobHunter/blob/master/jobhunter/src/main/java/jobhunter/gui/UpdateableListViewSkin.java – Eldelshell

10

このページで終わる他の任意の体のために:

これを行うための正しい方法は、「抽出」コールバックを使用して観察可能なリストを提供することです。これにより、リスト(およびListView)にプロパティの変更が通知されます。ディスプレイに

カスタムクラス:

public class Custom { 
    StringProperty name = new SimpleStringProperty(); 
    IntegerProperty id = new SimpleIntegerProperty(); 

    public static Callback<Custom, Observable[]> extractor() { 
     return new Callback<Custom, Observable[]>() { 
      @Override 
      public Observable[] call(Custom param) { 
       return new Observable[]{param.id, param.name}; 
      } 
     }; 
    } 

    @Override 
    public String toString() { 
     return String.format("%s: %s", name.get(), id.get()); 
    } 
} 

そして、あなたのメインのコード本体:

ListView<Custom> myListView; 
//...init the ListView appropriately 
ObservableList<Custom> items = FXCollections.observableArrayList(Custom.extractor()); 
myListView.setItems(items); 
Custom item = new Custom(); 
items.add(item); 
item.name.set("Mickey Mouse"); 
//^Should update your ListView!!! 

を参照してください(と同様の方法): https://docs.oracle.com/javafx/2/api/javafx/collections/FXCollections.html#observableArrayList(javafx.util.Callback)

+0

あなたはどのように説明できますあなたの例ではSimpleIntegerPropertyの使用が使用されています – serup

+1

@serupあなたの質問が完全にはわかりませんが、説明しようとしましょう。私の例では、明示的にIDの値を設定することはないので、デフォルトは0になります。これにより、ListViewの行に「Mickey Mouse:0」と表示されます。たとえば、ID値を設定したい場合は、 'item.name.set(6)'を使用すると、ListValueの更新が正しく表示されます。あなたのユースケースに応じて、 'Custom'クラスでデフォルト以外のコンストラクタを使うのは、おそらくは"良い "でしょう。 –