web-dev-qa-db-fra.com

Comment gérer l'action cliquée sur l'élément ListView?

J'ai mon application JavaFX 2.0, où je dois faire une action, après que l'utilisateur ait cliqué sur un élément dans l'élément ListView. Pour construire une interface utilisateur graphique, j'utilise FXML, dans lequel j'ai quelque chose comme ceci:

        <children>
            <ListView fx:id="listView" GridPane.columnIndex="0" 
            GridPane.rowIndex="1" labelFor="$pane" 
            onPropertyChange="#handleListViewAction"/>
        </children>

Et voici ce que j'ai dans un contrôleur pour cet événement:

        @FXML protected void handleListViewAction(ActionEvent event) {
           System.out.println("OK");
        }

Et voici une erreur, je comprends, lorsque la scène, qui est pour ce gui est construite:

javafx.fxml.LoadException: Java.lang.String does not define a property model for "property".
at javafx.fxml.FXMLLoader$Element.processEventHandlerAttributes(Unknown Source)
at javafx.fxml.FXMLLoader$ValueElement.processEndElement(Unknown Source)
at javafx.fxml.FXMLLoader.processEndElement(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at fxmlexample.FXMLExampleController.handleSubmitButtonAction(FXMLExampleController.Java:49)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
at Java.lang.reflect.Method.invoke(Method.Java:601)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
at com.Sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at com.Sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.event.Event.fireEvent(Unknown Source)
at javafx.scene.Node.fireEvent(Unknown Source)
at javafx.scene.control.Button.fire(Unknown Source)
at com.Sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(Unknown Source)
at com.Sun.javafx.scene.control.skin.SkinBase$5.handle(Unknown Source)
at com.Sun.javafx.scene.control.skin.SkinBase$5.handle(Unknown Source)
at com.Sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
at com.Sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at com.Sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at com.Sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.event.Event.fireEvent(Unknown Source)
at javafx.scene.Scene$MouseHandler.process(Unknown Source)
at javafx.scene.Scene$MouseHandler.process(Unknown Source)
at javafx.scene.Scene$MouseHandler.access$1300(Unknown Source)
at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
at com.Sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
at com.Sun.glass.ui.View.handleMouseEvent(Unknown Source)
at com.Sun.glass.ui.View.notifyMouse(Unknown Source)
at com.Sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.Sun.glass.ui.win.WinApplication.access$100(Unknown Source)
at com.Sun.glass.ui.win.WinApplication$2$1.run(Unknown Source)
at Java.lang.Thread.run(Thread.Java:722) Java.lang.NullPointerException
at javafx.scene.Scene.doCSSPass(Unknown Source)
at javafx.scene.Scene.access$2900(Unknown Source)
at javafx.scene.Scene$ScenePulseListener.Pulse(Unknown Source)
at com.Sun.javafx.tk.Toolkit.firePulse(Unknown Source)
at com.Sun.javafx.tk.quantum.QuantumToolkit.Pulse(Unknown Source)
at com.Sun.javafx.tk.quantum.QuantumToolkit$8.run(Unknown Source)
at com.Sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.Sun.glass.ui.win.WinApplication.access$100(Unknown Source)
at com.Sun.glass.ui.win.WinApplication$2$1.run(Unknown Source)
at Java.lang.Thread.run(Thread.Java:722)

Et le dernier bloc de cette exception (d'ici Java.lang.NullPointerException) est bouclé.

21
Agafonova Victoria

Les attributs et les valeurs FXML sont directement mappés à l'API FX. Donc, pour savoir comment écrire un gestionnaire, vous pouvez d'abord créer les entités requises par API.

Il semble que vous souhaitiez ajouter une action sur l'élément ListView lors d'un clic de souris, vous devez donc ajouter un gestionnaire de clic de souris. Dans l'API, la prochaine étape est la suivante:

    final ListView lv = new ListView(FXCollections.observableList(Arrays.asList("one", "2", "3")));
    lv.setOnMouseClicked(new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent event) {
            System.out.println("clicked on " + lv.getSelectionModel().getSelectedItem());
        }
    });

En regardant ce code, vous pouvez découvrir ce que dans FXML vous devez remplacer l'attribut onMouseClicked:

<ListView fx:id="listView" onMouseClicked="#handleMouseClick"/>

Et dans le contrôleur, vous devez fournir au gestionnaire le paramètre MouseEvent:

@FXML
private ListView listView;

@FXML public void handleMouseClick(MouseEvent arg0) {
    System.out.println("clicked on " + listView.getSelectionModel().getSelectedItem());
}
41
Sergey Grinev

la réponse de @Sergey Grinev peut avoir un problème. si votre ListView n'est pas suffisamment rempli, en d'autres termes, il peut y avoir un espace vide. alors vous avez cliqué sur l'espace vide ne déclenchera pas le changement de sélection. enter image description here

la solution: utilisez ListCell personnalisé.

  1. définir une classe personnalisée étend ListCell.
  2. dans la fonction updateItem, vous pouvez définir le gestionnaire d'événements de clic d'élément sur le nœud racine de la cellule.

Si vous ne savez pas combien d'articles vous avez. vous ne le ferez jamais seulement. vous devez utiliser le temps pour l'espace.

Démonstration:

dans le segment de code ListView:

listView.setCellFactory(new AppListCellFactory());

AppListCellFactory.Java:

 classe publique AppListCellFactory implémente Callback, ListCell> {
 
 // only one global event handler
private EventHandler<MouseEvent> oneClickHandler;


public AppListCellFactory(){
    oneClickHandler = new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            Parent p = (Parent) event.getSource();
            //  do what you want to do with data.
            AppClickHandler.onAppBeanClicked((AppBean) p.getUserData());
        }
    };
}

@Override
public ListCell<AppBean> call(ListView<AppBean> param) {
    return new DemoListCell(oneClickHandler);
}

public static final class DemoListCell extends ListCell<AppBean> {

    private EventHandler<MouseEvent> clickHandler;

    /**
     * This is ListView item root node.
     */
    private Parent itemRoot;

    private Label label_AppName;
    private ImageView imgv_AppIcon;

    DemoListCell(EventHandler<MouseEvent> clickHandler) {
        this.clickHandler = clickHandler;
    }

    @Override
    protected void updateItem(AppBean app, boolean empty) {
        super.updateItem(app, empty);
        if (app == null) {
            setText(null);
            setGraphic(null);
            return;
        }
        if (null == itemRoot) {
            try {
                itemRoot = FXMLLoader.load(getClass().getResource(("fxml/appList_item.fxml")));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            label_AppName = (Label) itemRoot.lookup("#item_Label_AppName");
            imgv_AppIcon = (ImageView) itemRoot.lookup("#item_ImageView_AppIcon");
            itemRoot.setOnMouseClicked(clickHandler);
        }
        //  set user data. like Android's setTag(Object).
        itemRoot.setUserData(app);
        label_AppName.setText(app.name);
        imgv_AppIcon.setImage(new Image(getClass().getResource("img/icon_64.png").toExternalForm()));
        setGraphic(itemRoot);
    }
}

}

7
gordonpro

Vous pouvez également consommer des événements indésirables sur un espace vide.

dans call() de la fabrique de cellules, ajoutez:

cell.setOnMousePressed((MouseEvent event) -> {
    if (cell.isEmpty()) {
       event.consume();
    }
});

Cela désactivera également click & dragStart.

Exemple de cellule entière de mon projet:

public static Callback<ListView<BlockFactory>, ListCell<BlockFactory>> getCellFactory() {
    return new Callback<ListView<BlockFactory>, ListCell<BlockFactory>>() {

        @Override
        public ListCell<BlockFactory> call(ListView<BlockFactory> param) {
            ListCell<BlockFactory> cell = new ListCell<BlockFactory>() {

                @Override
                protected void updateItem(BlockFactory item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item != null) {
                        ImageView img = new ImageView(item.img);
                        Label label = new Label(item.desc);
                        HBox bar = new HBox(img, label);
                        bar.setSpacing(15);
                        bar.setAlignment(Pos.CENTER_LEFT);
                        setGraphic(bar);
                    }
                }
            };
            cell.setOnMousePressed((MouseEvent event) -> {
                if (cell.isEmpty()) {
                    event.consume();
                }
            });
            return cell;
        }
    };
}
2
Steel Talon

Un correctif simple à la réponse de Sergey, sans avoir à créer un tout nouveau listCell (dans votre cas, vous le faites, mais nous n'avons pas à le faire dans un cas de texte normal).

tout ce que vous devez faire est de créer une variable temporaire qui est égale au premier élément de la liste au début, et qui est modifiée pour chaque nouvel élément cliqué. Une fois que vous essayez de cliquer à nouveau sur l'élément, la variable temporaire sait que c'est la même chose, et avec une instruction if, vous pouvez contourner cela.

l'élément temp est un global que vous placez en haut String tempItem = "admin";

pour moi, je savais que mon premier champ allait toujours être étiqueté "admin", donc je l'ai défini comme tel. vous devrez obtenir la première entrée et la définir en dehors de la méthode que vous allez utiliser pour la sélection de liste.

private void selUser() throws IOException
    {
    String item = userList.getSelectionModel().getSelectedItem().toString();

        if(item != tempItem)
        {
           //click, do something
        }

        tempItem = item;    
}

Quant à moi, j'ai utilisé un document FXML qui appelait ma méthode

@FXML 
public void userSel(MouseEvent mouse) throws IOException
{
selUser();

}

Dans ce cas, vous pouvez simplement prendre tout le contenu de la méthode selUser () et le placer dans le clic de souris userSel.

2
XaolingBao