Как обрабатывать действие элемента ListView по нажатию? - PullRequest
21 голосов
/ 15 марта 2012

У меня есть приложение JavaFX 2.0, где мне нужно выполнить какое-то действие после того, как пользователь щелкнул элемент в элементе ListView. Для создания пользовательского интерфейса я использую FXML, в котором у меня есть что-то вроде этого:

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

А вот что у меня есть в контроллере для этого события:

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

А вот ошибка, которую я получаю, когда создается сцена, предназначенная для этого графического интерфейса:

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)

И последний блок этого исключения (отсюда java.lang.NullPointerException) зацикливается.

Ответы [ 4 ]

41 голосов
/ 15 марта 2012

Атрибуты и значения FXML напрямую сопоставляются с FX API.Поэтому, чтобы узнать, как написать обработчик, вы можете сначала создать необходимые сущности с помощью API.

Кажется, вы хотите добавить действие к элементу ListView по щелчку мыши, поэтому вам нужно добавить обработчик щелчка мыши.В API это выглядит следующим образом:

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

Глядя на этот код, вы можете узнать, что в FXML необходимо для переопределения атрибута onMouseClicked:

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

И в контроллере вам нужнопредоставить обработчику параметр MouseEvent:

@FXML
private ListView listView;

@FXML public void handleMouseClick(MouseEvent arg0) {
    System.out.println("clicked on " + listView.getSelectionModel().getSelectedItem());
}
7 голосов
/ 17 апреля 2014

ответ от @Sergey Grinev может иметь проблему. если ваш ListView заполнен недостаточно полно, другими словами, может быть некоторое свободное место. тогда вы нажмете пробел, и выбор тигра не изменится. enter image description here

решение: использовать пользовательский ListCell.

  1. определение пользовательского класса extends ListCell.
  2. В функции updateItem вы можете установить обработчик события щелчка элемента для корневого узла ячейки.

Если вы не знаете, сколько у вас предметов. вы никогда не будете только делать это. Вы должны использовать время для пространства.

Demostrate:

в сегменте кода ListView:

listView.setCellFactory(new AppListCellFactory());

AppListCellFactory.java:

public class AppListCellFactory implements Callback, ListCell> {</p>

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

}

2 голосов
/ 04 ноября 2014

Вы также можете использовать нежелательные события на пустом месте.

в call() клеточного завода добавить:

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

Это также отключит click & dragStart.

Пример целой ячейки фабрики из моего проекта:

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 голосов
/ 28 сентября 2014

Простое исправление ответа Сергея, без необходимости создавать совершенно новый listCell (в вашем случае, но в обычном текстовом случае это не нужно).

все, что вам нужно просто сделать, это создать временную переменную, которая сначала будет равна первому элементу списка, и которая будет изменяться для каждого нового элемента, по которому щелкают. Как только вы попытаетесь щелкнуть по элементу еще раз, переменная temp узнает, что она такая же, и с помощью оператора if вы можете обойти это.

temp Предмет - это глобал, который вы ставите наверх String tempItem = "admin";

для меня я знал, что мое первое поле всегда будет помечено как «admin», поэтому я установил его так. вам нужно будет получить первую запись и установить ее вне метода, который вы собираетесь использовать для выбора списка.

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

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

        tempItem = item;    
}

Что касается меня, я использовал документ FXML, который называл мой метод

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

}

В этом случае вы можете просто взять все содержимое метода selUser () и поместить его в клик мыши userSel.

...