2013-03-27 24 views
17

Ich würde gerne wissen, ob es möglich ist, einen Image Button nach jedem ListView Item hinzuzufügen. Zum Beispiel: sampleJavaFX - ListView Item mit einem Image Button

wo diese roten Quadrate sollten stattdessen eine Schaltfläche haben. Wenn es möglich ist, wie kann ich das Klickereignis des Gegenstandknopfes behandeln?

EDIT: Wenn es ein anderes Steuerelement gibt, das es tun kann, lass es mich wissen. Ich teste TableView.

Vielen Dank im Voraus!

Antwort

27

Ich habe dies kürzlich getestet. Meine Lösung:

Custom List Cell with Button

Der relevante Teil ist die XCell.updateItem Methode und der setGraphic Aufruf:

import javafx.application.Application; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.Label; 
import javafx.scene.control.ListCell; 
import javafx.scene.control.ListView; 
import javafx.scene.layout.HBox; 
import javafx.scene.layout.Pane; 
import javafx.scene.layout.Priority; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 
import javafx.util.Callback; 

public class SO extends Application { 
    static class XCell extends ListCell<String> { 
     HBox hbox = new HBox(); 
     Label label = new Label("(empty)"); 
     Pane pane = new Pane(); 
     Button button = new Button("(>)"); 
     String lastItem; 

     public XCell() { 
      super(); 
      hbox.getChildren().addAll(label, pane, button); 
      HBox.setHgrow(pane, Priority.ALWAYS); 
      button.setOnAction(new EventHandler<ActionEvent>() { 
       @Override 
       public void handle(ActionEvent event) { 
        System.out.println(lastItem + " : " + event); 
       } 
      }); 
     } 

     @Override 
     protected void updateItem(String item, boolean empty) { 
      super.updateItem(item, empty); 
      setText(null); // No text in label of super class 
      if (empty) { 
       lastItem = null; 
       setGraphic(null); 
      } else { 
       lastItem = item; 
       label.setText(item!=null ? item : "<null>"); 
       setGraphic(hbox); 
      } 
     } 
    } 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     StackPane pane = new StackPane(); 
     Scene scene = new Scene(pane, 300, 150); 
     primaryStage.setScene(scene); 
     ObservableList<String> list = FXCollections.observableArrayList(
       "Item 1", "Item 2", "Item 3", "Item 4"); 
     ListView<String> lv = new ListView<>(list); 
     lv.setCellFactory(new Callback<ListView<String>, ListCell<String>>() { 
      @Override 
      public ListCell<String> call(ListView<String> param) { 
       return new XCell(); 
      } 
     }); 
     pane.getChildren().add(lv); 
     primaryStage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

Die Zellen werden wie folgt aussehen. Mit setGraphic() sollte normalerweise ein Icon für ein Label gesetzt werden, aber es funktioniert genauso gut einen komplexen Knoten zu setzen - in diesem Fall die HBox mit Label und Button.

Sie müssen sicherstellen, dass der Ereignishandler der Schaltfläche auf das richtige Element in der Liste verweist. Im ersten Link unten wird erwähnt, dass der aktuell ausgewählte Gegenstand vorerst ausreichen könnte. Rufen Sie den aktuell ausgewählten Index in der Liste ab, wenn Sie das Ereignis der Schaltfläche behandeln.

Vielleicht möchten Sie auf diese aussehen:

+1

Ausgezeichnet! Vielen Dank für die Codierung! Arbeitete gut! – Leonardo

+0

Das aktuelle Element muss nicht in einem 'lastItem'-Feld gespeichert werden. Sie können 'getItem()' direkt im Button-Handler aufrufen. –

0

Die bisherige Lösung hat gut funktioniert, wenn meine Liste nicht groß war und ich nicht mehr Kontrollen fügen die Herstellung Knoten komplexer. Als ich weitere Controls hinzugefügt habe, habe ich ein Problem mit der Parallelität festgestellt, vielleicht liegt es auch daran, dass ich Maps zeichne, die mehr als 12000 Objekte (Linien und Polygone) haben. Das Problem (was ich sehen konnte) war, dass einige der Elemente in ListView intern wiederholt werden, was bedeutet, dass cellfactory zweimal erstellt und/oder nicht erstellt wird. Es scheint, dass "updateItem" zuletzt in der Warteschlange des Threads ist. Also um das Problem zu umgehen, muss ich den Knoten vor dem Erstellen der ListView erstellen. Etwas wie folgt aus:

ObservableList<Node> list = FXCollections.observableArrayList(); 
    for (AisaLayer layer : listLayers) { 
     mapLayers.put(layer.getLayerName(), layer); 
     list.add(new AisaNode(layer)); 
     System.out.println("Ordered:" + layer.getLayerName()); 
    } 
    lv.setItems(list); 
    lv.setCellFactory(new Callback<ListView<Node>, ListCell<Node>>() { 
     @Override 
     public ListCell<Node> call(ListView<Node> param) { 
      return new DefaultListCell<>(); 
     } 
    }); 

verwendete ich die einfachen DefaultListCell in fxexperience.com empfohlen

2

Sie können auch Liste der Objekte benutzerdefinierte HBox verwenden. Das folgende Beispiel zeigt die Verwendung einer solchen benutzerdefinierten inneren HBox-Klasse und den Prozess der Verschachtelung einer Liste solcher Objekte in das ListView-Steuerelement.

import java.util.ArrayList; 
import java.util.List; 
import javafx.application.Application; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.Label; 
import javafx.scene.control.ListView; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.HBox; 
import javafx.scene.layout.Priority; 
import javafx.stage.Stage; 


public class ListViewDemo extends Application { 

    public static class HBoxCell extends HBox { 
      Label label = new Label(); 
      Button button = new Button(); 

      HBoxCell(String labelText, String buttonText) { 
       super(); 

       label.setText(labelText); 
       label.setMaxWidth(Double.MAX_VALUE); 
       HBox.setHgrow(label, Priority.ALWAYS); 

       button.setText(buttonText); 

       this.getChildren().addAll(label, button); 
      } 
    } 

    public Parent createContent() { 
      BorderPane layout = new BorderPane(); 

      List<HBoxCell> list = new ArrayList<>(); 
      for (int i = 0; i < 12; i++) { 
       list.add(new HBoxCell("Item " + i, "Button " + i)); 
      } 

      ListView<HBoxCell> listView = new ListView<HBoxCell>(); 
      ObservableList<HBoxCell> myObservableList = FXCollections.observableList(list); 
      listView.setItems(myObservableList); 

      layout.setCenter(listView); 

      return layout; 
    } 

    @Override 
    public void start(Stage stage) throws Exception { 
      stage.setScene(new Scene(createContent())); 
      stage.setWidth(300); 
      stage.setHeight(200); 
      stage.show(); 
    } 

    public static void main(String args[]) { 
      launch(args); 
    } 
} 

Das Ergebnis dieser Implementierung wird wie folgt aussehen:

enter image description here

jedoch auf die Wirksamkeit um Buttons Ereignisse verwalten, die Sie in das Hinzufügen von zusätzlichen benutzerdefinierten Button-Objekt in HBoxCustom Konstruktor-Methode in Betracht ziehen sollten, und erstellen Sie eine geeignete Methode, mit der Sie Buttons Click-Events steuern können.