【问题标题】:JavaFX combobox, on item clickedJavaFX 组合框,在单击的项目上
【发布时间】:2017-05-29 23:49:52
【问题描述】:

我的问题如下,

为了这个问题,我在一个新项目中重现了这个问题。

假设我有一个包含组合框的应用程序,其中可能有 1 个或多个项目。我希望这样当用户单击组合框中的某个项目时,会发生“某事”。

我生成了以下代码:

        obsvList.add("item1");

        cbTest.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("Item clicked");
            }
        });

这在应用程序启动并且第一次选择项目时有效。这也适用于组合框中有 2 个或更多项目的情况(例如,当用户单击项目 1,然后是项目 2,然后是项目 1)

但是我的问题是,当组合框中只有 1 个项目时,我们说“item1”。并且用户重新打开组合框并再次单击“item1”然后它不会重做操作。

当点击“新”项目时,它只会打印“Item Clicked”行。

我希望它说明了我遇到的问题是什么,如果没有,请要求澄清,我会在需要的地方提供。

提前致谢!

【问题讨论】:

  • 您使用的是控件本身而不是它的项目!
  • 这听起来更像是菜单的行为,而不是组合框的行为。使用MenuButton 不是更好吗? (组合框的功能是进行“选择”。菜单(或菜单按钮)的功能是向用户呈现一组“命令”。您所描述的更像是命令而不是选择。)
  • @James_D 嗯,一个 menuButton 看起来很有趣,我看看能不能实现它而没有太多问题
  • @James_D 假设我正在制作这个银行应用程序,用户从 1 个银行账户开始,可以选择稍后添加更多,当用户从组合框中选择一个银行账户时,我想加载该帐户的详细信息(剩余金额等)菜单项是否仍然最适合这种情况?
  • 好的,这听起来像是选择。在这种情况下,当用户“重新选择”相同的选项时,执行任何操作都是没有意义的。如果用户选择“选项 1”,则显示“选项 1”的详细信息。如果用户随后再次选择相同的选项,则无需执行任何操作,因为这些详细信息已显示。如果您执行了其他操作导致这些详细信息不显示,那么您应该确保组合框在发生这种情况时显示不同的选项(否则您的 UI 实际上处于不一致状态)。

标签: java javafx combobox


【解决方案1】:

组合框的功能是向用户提供可供选择的选项列表。当您使用暗示选择的控件时,您应该真正确保 UI 始终与选择的选项一致。如果你这样做,那么当用户“重新选择”相同的选项时“重复一个动作”是没有意义的(因为 UI 已经处于所需的状态)。一种方法是对组合框的值使用绑定或侦听器:

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class ComboBoxExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        ComboBox<Item> choices = new ComboBox<>();
        for (int i = 1 ; i <=3 ; i++) {
            choices.getItems().add(new Item("Choice "+i, "These are the details for choice "+i));
        }

        Label label = new Label();

        choices.valueProperty().addListener((obs, oldItem, newItem) -> {
            label.textProperty().unbind();
            if (newItem == null) {
                label.setText("");
            } else {
                label.textProperty().bind(newItem.detailsProperty());
            }
        });

        BorderPane root = new BorderPane();
        root.setCenter(label);
        root.setTop(choices);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public class Item {
        private final String name ;
        private final StringProperty details = new SimpleStringProperty() ;

        public Item(String name, String details) {
            this.name = name ;
            setDetails(details) ;
        }

        public String getName() {
            return name ;
        }

        @Override
        public String toString() {
            return getName();
        }

        public final StringProperty detailsProperty() {
            return this.details;
        }


        public final String getDetails() {
            return this.detailsProperty().get();
        }


        public final void setDetails(final String details) {
            this.detailsProperty().set(details);
        }



    }

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

在这种情况下,当用户“重新选择”同一个选项时,无需重复操作,因为代码始终确保 UI 与所选择的内容一致(如果用户选择已选择的选项)。通过在显示细节的 UI 部分中使用绑定(在这种情况下只是一个简单的标签),我们可以确保如果数据在外部发生更改,UI 会保持最新。 (显然在实际应用中,这可能要复杂得多,但基本策略还是完全一样的。)

另一方面,如果用户选择相同的功能,则需要重复执行某项操作的功能最好被视为向用户呈现一组“操作”。相应的控件包括菜单、带有按钮的工具栏和MenuButtons

一组可重复操作的示例是:

import java.util.stream.Collectors;
import java.util.stream.Stream;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class MenuButtonExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        MenuButton menuButton = new MenuButton("Items");
        Label label = new Label();

        Item[] items = new Item[3];
        for (int i = 1 ; i <=3 ; i++) {
            items[i-1] = new Item("Item "+i);
        }

        for (Item item : items) {
            MenuItem menuItem = new MenuItem(item.getName());
            menuItem.setOnAction(e -> item.setTimesChosen(item.getTimesChosen() + 1));
            menuButton.getItems().add(menuItem);
        }

        label.textProperty().bind(Bindings.createStringBinding(() -> 
            Stream.of(items)
                .map(item -> String.format("%s chosen %d times", item.getName(), item.getTimesChosen()))
                .collect(Collectors.joining("\n")), 
            Stream.of(items)
                .map(Item::timesChosenProperty)
                .collect(Collectors.toList()).toArray(new IntegerProperty[0])));

        BorderPane root = new BorderPane();
        root.setCenter(label);
        root.setTop(menuButton);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static class Item {
        private final String name ;
        private final IntegerProperty timesChosen = new SimpleIntegerProperty();

        public Item(String name) {
            this.name = name ;
        }

        public String getName() {
            return name ;
        }

        @Override
        public String toString() {
            return getName();
        }

        public final IntegerProperty timesChosenProperty() {
            return this.timesChosen;
        }


        public final int getTimesChosen() {
            return this.timesChosenProperty().get();
        }


        public final void setTimesChosen(final int timesChosen) {
            this.timesChosenProperty().set(timesChosen);
        }



    }

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

【讨论】:

  • 阅读我在主要问题下的评论,您是否建议菜单按钮在这种情况下也是一个不错的选择?
  • 嘿,感谢您的出色回答,现在我使用 MenuButton 来实现我想要的,但正如您所说,我应该重新思考我真正想要什么以及如何实现这一目标。
  • @James_D 假设用户单击组合框,然后看到/意识到他已经选择了正确的选项,因此他再次单击它。它不应该只是关闭显示的列表视图而不是什么都不做。这就是我们正在寻找的行为,你有什么建议?
  • @JawadElFou 不确定我是否理解您的观点。这是组合框的默认行为; valueProperty 在这种情况下不会改变。
【解决方案2】:

这个想法是在 ListView 窗格上设置一个侦听器,每当您单击 ComboBox 时就会出现该侦听器。一旦 ComboBox 首次加载到 JavaFX 场景中,就会创建 ListView 实例。因此,我们在 ComboBox 上添加一个监听器,检查它何时出现在场景中,然后通过“lookup”方法获取 ListView 并为其添加监听器。

private EventHandler<MouseEvent> cboxMouseEventHandler;

private void initComboBox() {
    ComboBox<String> comboBox = new ComboBox<String>();
    comboBox.getItems().add("Item 1");
    comboBox.getItems().add("Item 2");
    comboBox.getItems().add("Item 3");

    comboBox.sceneProperty().addListener((a,oldScene,newScene) -> {
        if(newScene == null || cboxMouseEventHandler != null)
            return;
            
        ListView<?> listView = (ListView<?>) comboBox.lookup(".list-view");
        if(listView != null) {
            cboxMouseEventHandler = (e) -> {
                Platform.runLater(()-> {
                    String selectedValue = (String) listView.getSelectionModel().getSelectedItem();
                    if(selectedValue.equals("Item 1"))
                        System.out.println("Item 1 clicked");
                });
            }; // cboxMouseEventHandler
        
            listView.addEventFilter(MouseEvent.MOUSE_PRESSED, cboxMouseEventHandler); 
        } // if
    });
} // initComboBox

【讨论】:

    猜你喜欢
    • 2014-11-10
    • 2019-10-20
    • 2015-11-28
    • 2015-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-15
    相关资源
    最近更新 更多