JAVA:Swing - JMenuItem 上的 actionPerformed() 触发但更新的值不持久
JAVA: Swing - actionPerformed() on JMenuItem fires but updated values do not Persist
好的,所以我一直在为我一直在开发的 opengl 图形引擎构建一个简单的 Java 基于 Swing 的 GUI。
我遇到的问题是菜单栏无法正常工作。具体来说,我有一个 MenuBarBuilder class 可以构建各种菜单、子菜单和菜单项。当一个菜单项被添加时,它被分配一个新的 actionEventListener 并被赋予一个将布尔值设置为 true 的简单任务;此布尔值存储在与该菜单关联的状态列表中。每个菜单项状态都是在将菜单项添加到菜单时创建的。这些状态作为 'State'.
类型的对象存储在 'StatesList' class 中
在 MenuBarBuilder class 中,每个需要作为一个单元运行的菜单项都添加为 'group'。完成后,第二个动作事件侦听器将添加到每个菜单项,将菜单组的所有关联值设置为 false。最终,这会确保组中只有一个菜单项在状态列表中的状态设置为 true。
单击菜单项时会触发动作侦听器事件。当他们被解雇时,他们会打印出一个列表来确定他们被解雇的顺序,并确保状态值按预期设置,见下文:
setfalse
list
MenuItem1Name: false
MenuItem2Name: false
MenuItem1Name
setTrue
list
MenuItem1Name: true
MenuItem2Name: false
setfalse
list
MenuItem1Name: false
MenuItem2Name: false
MenuItem2Name
setTrue
list
MenuItem1Name: false
MenuItem2Name: true
这可以确保事件以正确的顺序触发。我们可以看到,当 MenuItem1Name 按钮被按下时,值被正确设置为 false,相关字段随后更新为 true。 MenuItem2Name 也是如此。现在进入实际问题。
当我获取状态列表并通过 GUI class 循环检查值时,我只从状态列表中获取 'false' 值。我使用 Swing 工具栏以类似的方式实现了这种模式,效果很好,但我无法理解为什么我无法检索由动作侦听器事件正确设置的值。我看不到任何地方的值被覆盖或有一个奇怪的 order/mixing 对象实例化。
所以,问题是:
为什么我在测试状态列表时只得到 false?
更新:尝试参加 SSCCE
Here is UML class Diagram with the relevant classes
下面是相关classes的实现。
State.java
public class State {
private String name;
private Object value;
State(String name, Object value){
this.name=name;
this.value=value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String toString(){
return getName() + ": " + getValue();
}
}
StatesList.java
import java.util.ArrayList;
public class StatesList {
private ArrayList<State> states;
StatesList(){
states= new ArrayList<State>();
}
void addState(String name, Object value){
states.add(new State(name, value));
}
State getState(String name){
for(State s : states){
if(s.getName().equals(name)){
return s;
}
}
return null;
}
ArrayList<State> getStates() {
return states;
}
public String toString(){
String rString="";
rString+="list\n";
for (State s : states) {
rString+=s.toString()+"\n";
}
return rString;
}
}
MenuBar.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class MenuBar {
private JMenuBar menuBar;
private StatesList menustates;
public MenuBar() {
menuBar = new JMenuBar();
menustates = new StatesList();
}
void createMenu(String title) {
JMenu menu = new JMenu(title);
menuBar.add(menu);
}
void createMenuItem(JMenu menu, String itemTitle) {
JMenuItem menuItem = new JMenuItem(itemTitle);
menustates.addState(itemTitle, false);
menuItem.addActionListener(new ActionListener() {
int menuItemindex = menustates.getStates().size()-1 ;
@Override
public void actionPerformed(ActionEvent actionEvent) {
System.out.println(actionEvent.getActionCommand());
menustates.getStates().get(menuItemindex).setValue(true);
//print list of current states
System.out.println("setTrue");
System.out.println(menustates.toString());
}
});
menu.add(menuItem);
}
//not a real group, just ensures MenuItem behaves the same as other menu items
void addMenuGroup(JMenuItem[] jMenuItems, int startComponentIndex) {
for (int i = 0; i < jMenuItems.length; i++) {
jMenuItems[i].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int i2 = 0; i2 < jMenuItems.length; i2++) {
//make sure each state related to the menuItemGroup is to false
menustates.getStates().get(i2 + startComponentIndex).setValue(false);
}
//print list of current states
System.out.println("setfalse");
System.out.println(menustates.toString());
}
});
}
}
JMenuBar getMenuBar() {
return menuBar;
}
public StatesList getStates() {
return menustates;
}
}
MenuBarBuilder.java
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class MenuBarBuilder {
private MenuBar DataViewMenuBar;
MenuBarBuilder() {
DataViewMenuBar = buildDataViewMenu();
}
MenuBar buildDataViewMenu() {
MenuBar tb = new MenuBar();
//create menu
tb.createMenu("TestScenes");
//add two menuItems
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem1Name");
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem2Name");
//add buttons two a 'group' sort of
tb.addMenuGroup(new JMenuItem[] {
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(0),
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(1), },
tb.getStates().getStates().size() - 2);
return tb;
}
StatesList getDataViewMenuBarStates() {
return DataViewMenuBar.getStates();
}
JMenuBar getDataViewMenuBarMenubar() {
return DataViewMenuBar.getMenuBar();
}
}
GUI.java
import javax.swing.JFrame;
public class GUI {
MenuBarBuilder mbb;
boolean Selected = false;
GUI() {
mbb = new MenuBarBuilder();
}
void run() {
final JFrame frame = new JFrame("Main");
frame.setJMenuBar(mbb.buildDataViewMenu().getMenuBar());
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (!Selected) {
selectLoadScene();
}
System.out.print("bap- selection must have occured");
}
void selectLoadScene() {
String selectedTest = "";
// The Test Occurs here
// loop through states list
System.out.println(mbb.getDataViewMenuBarStates().getStates().toString());
for (State s : mbb.getDataViewMenuBarStates().getStates()) {
// check the value for each state -this value is always false,..
// thats the problem
if ((boolean) s.getValue() == true) {
// if true save value
selectedTest = s.getName();
break;
}
}
// test selected value and see if it matches the following cases
switch (selectedTest) {
case "MenuItem1Name":
// dosomething
Selected = true; // breaks while loop
break;
case "MenuItem2Name":
// dosomething
Selected = true;// breaks while loop
break;
default:
// do nothing
}
}
}
Start.java
public class Start {
GUI gui;
public static void main(String[] args) {
GUI gui= new GUI();
gui.run();
}
}
while循环中的菜单栏与执行动作方法中的菜单栏不同。为什么?
如果你仔细看class GUI中的以下两行,你就会知道。
mbb = new MenuBarBuilder();
frame.setJMenuBar(mbb.buildDataViewMenu().getMenuBar());
这里创建了 2 个不同的菜单栏。因为 MenuBarBuilder 的构造函数在内部调用 buildDataViewMenu(),所以 2 次调用 "buildDataViewMenu()"。
这将最终创建 2 个菜单栏。
为了避免这种情况
不要在
中调用 "buildDataViewMenu()"
frame.setJMenuBar(mbb.buildDataViewMenu().getMenuBar());
因为你只想要菜单栏,你应该以这样一种方式管理代码,你将获得 this.tb = new MenuBar() 的句柄,你可以添加 getter setter 相同。
当我添加 getter 和 setter 时,它开始工作了。
public class MenuBarBuilder {
private MenuBar DataViewMenuBar;
MenuBar tb;
MenuBarBuilder() {
DataViewMenuBar = buildDataViewMenu();
}
MenuBar buildDataViewMenu() {
this.tb = new MenuBar();
//create menu
tb.createMenu("TestScenes");
//add two menuItems
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem1Name");
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem2Name");
//add buttons two a 'group' sort of
tb.addMenuGroup(new JMenuItem[] {
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(0),
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(1), },
tb.getStates().getStates().size() - 2);
return tb;
}
public MenuBar getMenuBar(){
return this.tb;
}
StatesList getDataViewMenuBarStates() {
return DataViewMenuBar.getStates();
}
JMenuBar getDataViewMenuBarMenubar() {
return DataViewMenuBar.getMenuBar();
}
}
并且在 class GUI 调用应该是这样的,
frame.setJMenuBar(mbb.getMenuBar().getMenuBar());
这将解决问题。如果您遇到任何问题,请告诉我。
好的,所以我一直在为我一直在开发的 opengl 图形引擎构建一个简单的 Java 基于 Swing 的 GUI。
我遇到的问题是菜单栏无法正常工作。具体来说,我有一个 MenuBarBuilder class 可以构建各种菜单、子菜单和菜单项。当一个菜单项被添加时,它被分配一个新的 actionEventListener 并被赋予一个将布尔值设置为 true 的简单任务;此布尔值存储在与该菜单关联的状态列表中。每个菜单项状态都是在将菜单项添加到菜单时创建的。这些状态作为 'State'.
类型的对象存储在 'StatesList' class 中在 MenuBarBuilder class 中,每个需要作为一个单元运行的菜单项都添加为 'group'。完成后,第二个动作事件侦听器将添加到每个菜单项,将菜单组的所有关联值设置为 false。最终,这会确保组中只有一个菜单项在状态列表中的状态设置为 true。
单击菜单项时会触发动作侦听器事件。当他们被解雇时,他们会打印出一个列表来确定他们被解雇的顺序,并确保状态值按预期设置,见下文:
setfalse
list
MenuItem1Name: false
MenuItem2Name: false
MenuItem1Name
setTrue
list
MenuItem1Name: true
MenuItem2Name: false
setfalse
list
MenuItem1Name: false
MenuItem2Name: false
MenuItem2Name
setTrue
list
MenuItem1Name: false
MenuItem2Name: true
这可以确保事件以正确的顺序触发。我们可以看到,当 MenuItem1Name 按钮被按下时,值被正确设置为 false,相关字段随后更新为 true。 MenuItem2Name 也是如此。现在进入实际问题。
当我获取状态列表并通过 GUI class 循环检查值时,我只从状态列表中获取 'false' 值。我使用 Swing 工具栏以类似的方式实现了这种模式,效果很好,但我无法理解为什么我无法检索由动作侦听器事件正确设置的值。我看不到任何地方的值被覆盖或有一个奇怪的 order/mixing 对象实例化。
所以,问题是: 为什么我在测试状态列表时只得到 false?
更新:尝试参加 SSCCE
Here is UML class Diagram with the relevant classes
下面是相关classes的实现。
State.java
public class State {
private String name;
private Object value;
State(String name, Object value){
this.name=name;
this.value=value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String toString(){
return getName() + ": " + getValue();
}
}
StatesList.java
import java.util.ArrayList;
public class StatesList {
private ArrayList<State> states;
StatesList(){
states= new ArrayList<State>();
}
void addState(String name, Object value){
states.add(new State(name, value));
}
State getState(String name){
for(State s : states){
if(s.getName().equals(name)){
return s;
}
}
return null;
}
ArrayList<State> getStates() {
return states;
}
public String toString(){
String rString="";
rString+="list\n";
for (State s : states) {
rString+=s.toString()+"\n";
}
return rString;
}
}
MenuBar.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class MenuBar {
private JMenuBar menuBar;
private StatesList menustates;
public MenuBar() {
menuBar = new JMenuBar();
menustates = new StatesList();
}
void createMenu(String title) {
JMenu menu = new JMenu(title);
menuBar.add(menu);
}
void createMenuItem(JMenu menu, String itemTitle) {
JMenuItem menuItem = new JMenuItem(itemTitle);
menustates.addState(itemTitle, false);
menuItem.addActionListener(new ActionListener() {
int menuItemindex = menustates.getStates().size()-1 ;
@Override
public void actionPerformed(ActionEvent actionEvent) {
System.out.println(actionEvent.getActionCommand());
menustates.getStates().get(menuItemindex).setValue(true);
//print list of current states
System.out.println("setTrue");
System.out.println(menustates.toString());
}
});
menu.add(menuItem);
}
//not a real group, just ensures MenuItem behaves the same as other menu items
void addMenuGroup(JMenuItem[] jMenuItems, int startComponentIndex) {
for (int i = 0; i < jMenuItems.length; i++) {
jMenuItems[i].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int i2 = 0; i2 < jMenuItems.length; i2++) {
//make sure each state related to the menuItemGroup is to false
menustates.getStates().get(i2 + startComponentIndex).setValue(false);
}
//print list of current states
System.out.println("setfalse");
System.out.println(menustates.toString());
}
});
}
}
JMenuBar getMenuBar() {
return menuBar;
}
public StatesList getStates() {
return menustates;
}
}
MenuBarBuilder.java
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class MenuBarBuilder {
private MenuBar DataViewMenuBar;
MenuBarBuilder() {
DataViewMenuBar = buildDataViewMenu();
}
MenuBar buildDataViewMenu() {
MenuBar tb = new MenuBar();
//create menu
tb.createMenu("TestScenes");
//add two menuItems
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem1Name");
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem2Name");
//add buttons two a 'group' sort of
tb.addMenuGroup(new JMenuItem[] {
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(0),
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(1), },
tb.getStates().getStates().size() - 2);
return tb;
}
StatesList getDataViewMenuBarStates() {
return DataViewMenuBar.getStates();
}
JMenuBar getDataViewMenuBarMenubar() {
return DataViewMenuBar.getMenuBar();
}
}
GUI.java
import javax.swing.JFrame;
public class GUI {
MenuBarBuilder mbb;
boolean Selected = false;
GUI() {
mbb = new MenuBarBuilder();
}
void run() {
final JFrame frame = new JFrame("Main");
frame.setJMenuBar(mbb.buildDataViewMenu().getMenuBar());
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (!Selected) {
selectLoadScene();
}
System.out.print("bap- selection must have occured");
}
void selectLoadScene() {
String selectedTest = "";
// The Test Occurs here
// loop through states list
System.out.println(mbb.getDataViewMenuBarStates().getStates().toString());
for (State s : mbb.getDataViewMenuBarStates().getStates()) {
// check the value for each state -this value is always false,..
// thats the problem
if ((boolean) s.getValue() == true) {
// if true save value
selectedTest = s.getName();
break;
}
}
// test selected value and see if it matches the following cases
switch (selectedTest) {
case "MenuItem1Name":
// dosomething
Selected = true; // breaks while loop
break;
case "MenuItem2Name":
// dosomething
Selected = true;// breaks while loop
break;
default:
// do nothing
}
}
}
Start.java
public class Start {
GUI gui;
public static void main(String[] args) {
GUI gui= new GUI();
gui.run();
}
}
while循环中的菜单栏与执行动作方法中的菜单栏不同。为什么? 如果你仔细看class GUI中的以下两行,你就会知道。
mbb = new MenuBarBuilder();
frame.setJMenuBar(mbb.buildDataViewMenu().getMenuBar());
这里创建了 2 个不同的菜单栏。因为 MenuBarBuilder 的构造函数在内部调用 buildDataViewMenu(),所以 2 次调用 "buildDataViewMenu()"。 这将最终创建 2 个菜单栏。
为了避免这种情况
不要在
中调用 "buildDataViewMenu()"frame.setJMenuBar(mbb.buildDataViewMenu().getMenuBar());
因为你只想要菜单栏,你应该以这样一种方式管理代码,你将获得 this.tb = new MenuBar() 的句柄,你可以添加 getter setter 相同。
当我添加 getter 和 setter 时,它开始工作了。
public class MenuBarBuilder {
private MenuBar DataViewMenuBar;
MenuBar tb;
MenuBarBuilder() {
DataViewMenuBar = buildDataViewMenu();
}
MenuBar buildDataViewMenu() {
this.tb = new MenuBar();
//create menu
tb.createMenu("TestScenes");
//add two menuItems
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem1Name");
tb.createMenuItem(tb.getMenuBar().getMenu(0), "MenuItem2Name");
//add buttons two a 'group' sort of
tb.addMenuGroup(new JMenuItem[] {
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(0),
(JMenuItem) tb.getMenuBar().getMenu(0).getItem(1), },
tb.getStates().getStates().size() - 2);
return tb;
}
public MenuBar getMenuBar(){
return this.tb;
}
StatesList getDataViewMenuBarStates() {
return DataViewMenuBar.getStates();
}
JMenuBar getDataViewMenuBarMenubar() {
return DataViewMenuBar.getMenuBar();
}
}
并且在 class GUI 调用应该是这样的,
frame.setJMenuBar(mbb.getMenuBar().getMenuBar());
这将解决问题。如果您遇到任何问题,请告诉我。