2013-03-27 20 views
17

Chciałbym wiedzieć, czy istnieje możliwość dodania przycisku obrazu po każdym obiekcie ListView. Na przykład: sampleJavaFX - ListView Element z przyciskiem obrazu

gdzie te czerwone kwadraty powinny mieć zamiast tego przycisk. Jeśli to możliwe, w jaki sposób mogę obsłużyć zdarzenie kliknięcia przycisku elementu?

EDYCJA: jeśli jest inna kontrola, która może to zrobić, proszę dać mi znać. Testuję TableView.

Z góry dziękuję!

Odpowiedz

27

Testowałem to niedawno. Moje rozwiązanie:

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); 
    } 
} 

Komórki będzie wyglądać następująco:

Custom List Cell with Button

Właściwa część jest metoda XCell.updateItem i wywołanie setGraphic. Przy setGraphic() zwykle ustawiona jest ikona dla etykiety, ale równie dobrze działa ona dla złożonego węzła - w tym przypadku dla HBox z etykietą i przyciskiem.

Należy upewnić się, że procedura obsługi zdarzenia Button odnosi się do poprawnej pozycji na liście. W pierwszym linku poniżej wspomniano, że obecnie wybrany element może być na razie wystarczający. Więc pobieraj aktualnie wybrany indeks z listy podczas obsługi zdarzenia przycisku.

Możecie lubię patrzeć na te:

+1

Doskonale! Dziękuję bardzo za kodowanie! Pracowałem dobrze! – Leonardo

+0

Nie ma potrzeby przechowywania bieżącego elementu w polu 'lastItem'. Możesz po prostu wywołać 'getItem()' bezpośrednio w procedurze obsługi przycisku. –

0

Dotychczasowe rozwiązanie działało w porządku, kiedy moja lista nie była duża i nie dodać więcej kontroli sprawiając, że Węzeł bardziej złożony. Kiedy dodałem więcej Kontrolek Znalazłem problem z współbieżnością, może to również dlatego, że rysuję mapy, które mają więcej niż 12000 obiektów (linie i wielokąty). Problem (co mogłem zobaczyć) polegał na tym, że niektóre pozycje w ListView będą wewnętrznie powtarzane, co oznacza, że ​​komórka zostanie utworzona dwa razy i/lub nie zostanie utworzona. Wygląda na to, że "updateItem" jest ostatnim w kolejce wątku. Aby obejść problem, muszę utworzyć węzeł przed utworzeniem ListView. Coś takiego:

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<>(); 
     } 
    }); 

użyłem prostego DefaultListCell zalecane w fxexperience.com

2

Można także użyć listy obiektów niestandardowych hbox. Przykład poniżej pokazuje użycie takiej niestandardowej klasy wewnętrznej HBox i procesu zagnieżdżania listy takich obiektów do kontrolki ListView.

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); 
    } 
} 

Wynikiem tej implementacji będzie wyglądać następująco:

enter image description here

Jednak w celu skuteczności zarządzania Guziki zdarzenia, należy rozważyć dodanie przedmiot dodatkowy niestandardowego przycisku do metody konstruktora HBoxCustom, i stworzyć odpowiednią metodę, która pozwoli kontrolować zdarzenia klikania przycisków.