Java Swing:JButtons 和 JSliders 将无法单独工作
Java Swing: JButtons and JSliders will not work individually
我在 Java 中使用 Swing 制作 UI 还比较陌生,我在让按钮和滑块在我的项目中工作时遇到了问题。我试图测试我的滑块和按钮是否适用于印刷品,但发现它们不起作用。
如果我不检查与 slider/button 交互的内容,我将得到结果。 IE。如果按下项目中的任何滑块或按钮,我只会得到结果。但显然我希望他们都做不同的事情。
到目前为止,这是我的代码
Main.java
//Imports here, just saving space
public class Main
{
public static void main(String[] args) throws IOException
{
ImageProcessor e = new ImageProcessor();
e.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
e.initGUI();
}
}
ImageProcessor.java
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
public class ImageProcessor extends JFrame
{
JPanel imagePane;
JCheckBox xView, yView, zView, mip;
JLabel xSliceLabel, ySliceLabel, zSliceLabel;
JSlider xSliceSlider, ySliceSlider, zSliceSlider;
JLabel xImageIcon, yImageIcon, zImageIconl;
BufferedImage xImage, yImage, zImage;
short cthead[][][];
short min, max;
double ai, aj, ak;
JButton test;
public void initGUI()
{
// Build the window to contain everything
setTitle("Simple Example");
setSize(1200, 900);
/////// MAKE THE GUI ///////
// Container Window
Container container = getContentPane();
container.setLayout(null);
// Check boxes to select view
JCheckBox xView = new JCheckBox("X View");
xView.setBounds(150, 45, 65, 20);
container.add(xView);
JCheckBox yView = new JCheckBox("Y View");
yView.setBounds(225, 45, 65, 20);
container.add(yView);
JCheckBox zView = new JCheckBox("Z View");
zView.setBounds(300, 45, 65, 20);
container.add(zView);
// Panel to show CT Head image
JPanel imagePane = new JPanel();
imagePane.setBounds(25, 75, 512, 512);
imagePane.setBorder(BorderFactory.createTitledBorder("Image View"));
container.add(imagePane);
// Image labels to display BufferedImages
JLabel xImageIcon = new JLabel();
imagePane.add(xImageIcon);
// Checkbox for MIP on/off
JCheckBox mip = new JCheckBox("MIP");
mip.setBounds(125, 630, 65, 20);
container.add(mip);
// Slider labels
JLabel xSliceLabel = new JLabel("X Slice");
xSliceLabel.setBounds(50, 675, 60, 25);
container.add(xSliceLabel);
JLabel ySliceLabel = new JLabel("Y Slice");
ySliceLabel.setBounds(50, 725, 60, 25);
container.add(ySliceLabel);
JLabel zSliceLabel = new JLabel("Z Slice");
zSliceLabel.setBounds(50, 775, 60, 25);
container.add(zSliceLabel);
// Slice sliders
JSlider xSliceSlider = new JSlider(0, 255, 76);
xSliceSlider.setBounds(125, 675, 256, 25);
container.add(xSliceSlider);
JSlider ySliceSlider = new JSlider(0, 255, 76);
ySliceSlider.setBounds(125, 725, 256, 25);
container.add(ySliceSlider);
JSlider zSliceSlider = new JSlider(0, 112, 76);
zSliceSlider.setBounds(125, 775, 256, 25);
container.add(zSliceSlider);
JButton test = new JButton("Test");
test.setBounds(50, 700, 60, 25);
container.add(test);
// Handler class
GUIEventHandler handler = new GUIEventHandler();
xSliceSlider.addChangeListener(handler);
ySliceSlider.addChangeListener(handler);
zSliceSlider.addChangeListener(handler);
test.addActionListener(handler);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class GUIEventHandler implements ActionListener, ChangeListener
{
public void stateChanged(ChangeEvent e)
{
if (e.getSource() == xSliceSlider)
{
System.out.println("x Slider changed!");
} else if (e.getSource() == ySliceSlider)
{
System.out.println("y Slice changed!");
} else if (e.getSource() == zSliceSlider)
{
System.out.println("Z slice changed!");
}
//This will work if any of the sliders change state
//System.out.println("Something changed");
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == test)
{
System.out.println("Testing...");
}
//This will also work if any button is pressed
//System.out.println("Testing again...");
}
}
}
非常感谢任何知识渊博的人看到这个并指出我正确的方向。
非常感谢大家!
您定义了:
- 按钮和滑块字段
- 同名局部变量
在处理程序中,您访问字段,但它们从未被初始化!
在initGUI
中重写一些行:
JSlider xSliceSlider = new JSlider(0, 255, 76); // define a local variable
如:
xSliceSlider = new JSlider(0, 255, 76); // initialize the field
我敢打赌这不是您的 GUI 在计算机中的样子...是吗?
文本被裁剪了,但是...为什么?您可能想知道。
嗯,那是因为您使用了 null-layout
并手动设置了每个组件的边界及其大小。
但是...我该如何解决这个问题?
首先是一些背景阅读:Null layout is evil, Why is it frowned upon to use a null layout in Swing?,
现在,对于您的实际问题,您可以按照@MadProgrammer 对 this question 的回答,他说:
Realistically, if you can, you should be giving each slider it's own listener and dealing with it directly from the source...
JSlider slider = new JSlider();
slider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSlider slider= (Slider)e.getSource();
// Do funky stuff
}
});
按照上述提示,您可以按如下方式修复您的 GUI,让 Layout managers 为您处理 window 大小,这样 GUI 在每台计算机上看起来都正确,例如:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class ImageProcessingCorrected {
private JFrame frame;
private JPanel pane;
private JPanel imagePane;
private JPanel boxesPane;
private JPanel controlsPane;
private JPanel slidersPane;
private JCheckBox[] boxes;
private JButton button;
private JCheckBox mipBox;
private JSlider[] sliders;
private static final String[] AXIS = new String[] {"X", "Y", "Z"};
public static void main(String[] args) {
SwingUtilities.invokeLater(new ImageProcessingCorrected()::createAndShowGui);
}
@SuppressWarnings("serial")
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
pane = new JPanel();
imagePane = new JPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
};
boxesPane = new JPanel();
controlsPane = new JPanel();
slidersPane = new JPanel();
mipBox = new JCheckBox("MIP");
button = new JButton("Test");
button.addActionListener(e -> {
System.out.println("Test button pressed!");
});
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
boxesPane.setLayout(new GridLayout(1, 3));
slidersPane.setLayout(new GridLayout(3, 2, 5, 15));
boxes = new JCheckBox[3];
sliders = new JSlider[3];
for (int i = 0; i < boxes.length; i++) {
boxes[i] = new JCheckBox(AXIS[i] + " View");
boxes[i].setHorizontalAlignment(SwingConstants.CENTER);
boxesPane.add(boxes[i]);
}
for (int i = 0; i < sliders.length; i++) {
sliders[i] = new JSlider(0, 100, 50);
sliders[i].setName(AXIS[i] + " Slice");
JLabel label = new JLabel(AXIS[i] + " Slice");
label.setHorizontalAlignment(SwingConstants.CENTER);
sliders[i].addChangeListener(e -> {
System.out.println(((JSlider) e.getSource()).getName() + " changed!");
});
slidersPane.add(label);
slidersPane.add(sliders[i]);
}
controlsPane.add(mipBox);
controlsPane.add(button);
imagePane.setBorder(BorderFactory.createTitledBorder("Image View"));
pane.add(boxesPane);
pane.add(imagePane);
pane.add(controlsPane);
pane.add(slidersPane);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
这是 sysout
调用打印的示例:
X Slice changed!
X Slice changed!
X Slice changed!
Y Slice changed!
Y Slice changed!
Y Slice changed!
Y Slice changed!
Z Slice changed!
Z Slice changed!
Z Slice changed!
Z Slice changed!
Test button pressed!
而不是对多个 GUI 对象使用同一个事件处理程序
为每个 GUI 对象使用单独的事件处理程序是很常见的。
这通常是通过匿名 classes 实现事件侦听器接口来完成的。
在您的应用程序中,您将替换您的代码
// Handler class
GUIEventHandler handler = new GUIEventHandler();
xSliceSlider.addChangeListener(handler);
ySliceSlider.addChangeListener(handler);
zSliceSlider.addChangeListener(handler);
test.addActionListener(handler);
通过此代码
xSliceSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
System.out.println("x Slider changed!");
}
});
ySliceSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
System.out.println("y Slider changed!");
}
});
zSliceSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
System.out.println("z Slider changed!");
}
});
test.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Testing...");
}
});
这样做你就不再需要 GUIEventHandler
class,
并且事件处理方法中不需要检查e.getSource()
。
顺便说一句:
如果你是 运行 Java 8+ 你可以更简洁地重写上面的代码
通过使用 lambda expressions 作为事件处理程序:
xSliceSlider.addChangeListener(e -> {
System.out.println("x Slider changed!");
});
ySliceSlider.addChangeListener(e -> {
System.out.println("y Slider changed!");
});
zSliceSlider.addChangeListener(e -> {
System.out.println("z Slider changed!");
});
test.addActionListener(e -> {
System.out.println("Testing...");
});
我在 Java 中使用 Swing 制作 UI 还比较陌生,我在让按钮和滑块在我的项目中工作时遇到了问题。我试图测试我的滑块和按钮是否适用于印刷品,但发现它们不起作用。
如果我不检查与 slider/button 交互的内容,我将得到结果。 IE。如果按下项目中的任何滑块或按钮,我只会得到结果。但显然我希望他们都做不同的事情。
到目前为止,这是我的代码
Main.java
//Imports here, just saving space
public class Main
{
public static void main(String[] args) throws IOException
{
ImageProcessor e = new ImageProcessor();
e.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
e.initGUI();
}
}
ImageProcessor.java
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
public class ImageProcessor extends JFrame
{
JPanel imagePane;
JCheckBox xView, yView, zView, mip;
JLabel xSliceLabel, ySliceLabel, zSliceLabel;
JSlider xSliceSlider, ySliceSlider, zSliceSlider;
JLabel xImageIcon, yImageIcon, zImageIconl;
BufferedImage xImage, yImage, zImage;
short cthead[][][];
short min, max;
double ai, aj, ak;
JButton test;
public void initGUI()
{
// Build the window to contain everything
setTitle("Simple Example");
setSize(1200, 900);
/////// MAKE THE GUI ///////
// Container Window
Container container = getContentPane();
container.setLayout(null);
// Check boxes to select view
JCheckBox xView = new JCheckBox("X View");
xView.setBounds(150, 45, 65, 20);
container.add(xView);
JCheckBox yView = new JCheckBox("Y View");
yView.setBounds(225, 45, 65, 20);
container.add(yView);
JCheckBox zView = new JCheckBox("Z View");
zView.setBounds(300, 45, 65, 20);
container.add(zView);
// Panel to show CT Head image
JPanel imagePane = new JPanel();
imagePane.setBounds(25, 75, 512, 512);
imagePane.setBorder(BorderFactory.createTitledBorder("Image View"));
container.add(imagePane);
// Image labels to display BufferedImages
JLabel xImageIcon = new JLabel();
imagePane.add(xImageIcon);
// Checkbox for MIP on/off
JCheckBox mip = new JCheckBox("MIP");
mip.setBounds(125, 630, 65, 20);
container.add(mip);
// Slider labels
JLabel xSliceLabel = new JLabel("X Slice");
xSliceLabel.setBounds(50, 675, 60, 25);
container.add(xSliceLabel);
JLabel ySliceLabel = new JLabel("Y Slice");
ySliceLabel.setBounds(50, 725, 60, 25);
container.add(ySliceLabel);
JLabel zSliceLabel = new JLabel("Z Slice");
zSliceLabel.setBounds(50, 775, 60, 25);
container.add(zSliceLabel);
// Slice sliders
JSlider xSliceSlider = new JSlider(0, 255, 76);
xSliceSlider.setBounds(125, 675, 256, 25);
container.add(xSliceSlider);
JSlider ySliceSlider = new JSlider(0, 255, 76);
ySliceSlider.setBounds(125, 725, 256, 25);
container.add(ySliceSlider);
JSlider zSliceSlider = new JSlider(0, 112, 76);
zSliceSlider.setBounds(125, 775, 256, 25);
container.add(zSliceSlider);
JButton test = new JButton("Test");
test.setBounds(50, 700, 60, 25);
container.add(test);
// Handler class
GUIEventHandler handler = new GUIEventHandler();
xSliceSlider.addChangeListener(handler);
ySliceSlider.addChangeListener(handler);
zSliceSlider.addChangeListener(handler);
test.addActionListener(handler);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class GUIEventHandler implements ActionListener, ChangeListener
{
public void stateChanged(ChangeEvent e)
{
if (e.getSource() == xSliceSlider)
{
System.out.println("x Slider changed!");
} else if (e.getSource() == ySliceSlider)
{
System.out.println("y Slice changed!");
} else if (e.getSource() == zSliceSlider)
{
System.out.println("Z slice changed!");
}
//This will work if any of the sliders change state
//System.out.println("Something changed");
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == test)
{
System.out.println("Testing...");
}
//This will also work if any button is pressed
//System.out.println("Testing again...");
}
}
}
非常感谢任何知识渊博的人看到这个并指出我正确的方向。
非常感谢大家!
您定义了:
- 按钮和滑块字段
- 同名局部变量
在处理程序中,您访问字段,但它们从未被初始化!
在initGUI
中重写一些行:
JSlider xSliceSlider = new JSlider(0, 255, 76); // define a local variable
如:
xSliceSlider = new JSlider(0, 255, 76); // initialize the field
我敢打赌这不是您的 GUI 在计算机中的样子...是吗?
文本被裁剪了,但是...为什么?您可能想知道。
嗯,那是因为您使用了 null-layout
并手动设置了每个组件的边界及其大小。
但是...我该如何解决这个问题?
首先是一些背景阅读:Null layout is evil, Why is it frowned upon to use a null layout in Swing?,
现在,对于您的实际问题,您可以按照@MadProgrammer 对 this question 的回答,他说:
Realistically, if you can, you should be giving each slider it's own listener and dealing with it directly from the source...
JSlider slider = new JSlider(); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider slider= (Slider)e.getSource(); // Do funky stuff } });
按照上述提示,您可以按如下方式修复您的 GUI,让 Layout managers 为您处理 window 大小,这样 GUI 在每台计算机上看起来都正确,例如:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class ImageProcessingCorrected {
private JFrame frame;
private JPanel pane;
private JPanel imagePane;
private JPanel boxesPane;
private JPanel controlsPane;
private JPanel slidersPane;
private JCheckBox[] boxes;
private JButton button;
private JCheckBox mipBox;
private JSlider[] sliders;
private static final String[] AXIS = new String[] {"X", "Y", "Z"};
public static void main(String[] args) {
SwingUtilities.invokeLater(new ImageProcessingCorrected()::createAndShowGui);
}
@SuppressWarnings("serial")
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
pane = new JPanel();
imagePane = new JPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
};
boxesPane = new JPanel();
controlsPane = new JPanel();
slidersPane = new JPanel();
mipBox = new JCheckBox("MIP");
button = new JButton("Test");
button.addActionListener(e -> {
System.out.println("Test button pressed!");
});
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
boxesPane.setLayout(new GridLayout(1, 3));
slidersPane.setLayout(new GridLayout(3, 2, 5, 15));
boxes = new JCheckBox[3];
sliders = new JSlider[3];
for (int i = 0; i < boxes.length; i++) {
boxes[i] = new JCheckBox(AXIS[i] + " View");
boxes[i].setHorizontalAlignment(SwingConstants.CENTER);
boxesPane.add(boxes[i]);
}
for (int i = 0; i < sliders.length; i++) {
sliders[i] = new JSlider(0, 100, 50);
sliders[i].setName(AXIS[i] + " Slice");
JLabel label = new JLabel(AXIS[i] + " Slice");
label.setHorizontalAlignment(SwingConstants.CENTER);
sliders[i].addChangeListener(e -> {
System.out.println(((JSlider) e.getSource()).getName() + " changed!");
});
slidersPane.add(label);
slidersPane.add(sliders[i]);
}
controlsPane.add(mipBox);
controlsPane.add(button);
imagePane.setBorder(BorderFactory.createTitledBorder("Image View"));
pane.add(boxesPane);
pane.add(imagePane);
pane.add(controlsPane);
pane.add(slidersPane);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
这是 sysout
调用打印的示例:
X Slice changed!
X Slice changed!
X Slice changed!
Y Slice changed!
Y Slice changed!
Y Slice changed!
Y Slice changed!
Z Slice changed!
Z Slice changed!
Z Slice changed!
Z Slice changed!
Test button pressed!
而不是对多个 GUI 对象使用同一个事件处理程序 为每个 GUI 对象使用单独的事件处理程序是很常见的。 这通常是通过匿名 classes 实现事件侦听器接口来完成的。
在您的应用程序中,您将替换您的代码
// Handler class
GUIEventHandler handler = new GUIEventHandler();
xSliceSlider.addChangeListener(handler);
ySliceSlider.addChangeListener(handler);
zSliceSlider.addChangeListener(handler);
test.addActionListener(handler);
通过此代码
xSliceSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
System.out.println("x Slider changed!");
}
});
ySliceSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
System.out.println("y Slider changed!");
}
});
zSliceSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
System.out.println("z Slider changed!");
}
});
test.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Testing...");
}
});
这样做你就不再需要 GUIEventHandler
class,
并且事件处理方法中不需要检查e.getSource()
。
顺便说一句: 如果你是 运行 Java 8+ 你可以更简洁地重写上面的代码 通过使用 lambda expressions 作为事件处理程序:
xSliceSlider.addChangeListener(e -> {
System.out.println("x Slider changed!");
});
ySliceSlider.addChangeListener(e -> {
System.out.println("y Slider changed!");
});
zSliceSlider.addChangeListener(e -> {
System.out.println("z Slider changed!");
});
test.addActionListener(e -> {
System.out.println("Testing...");
});