【问题标题】:Java Swing: JButtons and JSliders will not work individuallyJava Swing:JButton 和 JSlider 不能单独工作
【发布时间】:2018-02-28 17:06:38
【问题描述】:

我对使用 Java 中的 Swing 制作 UI 比较陌生,而且我在让按钮和滑块在我的项目中工作时遇到了麻烦。我试图测试我的滑块和按钮是否适用于打印,但发现它们不起作用。

如果我不检查与哪个滑块/按钮交互,我会得到结果。 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...");
        }
    }
}

我非常感谢任何有见识的人看到这个并指出我正确的方向。

非常感谢大家!

【问题讨论】:

  • 1) Java GUI 必须在不同的操作系统、屏幕尺寸、屏幕分辨率等上使用不同语言环境中的不同 PLAF。因此,它们不利于像素完美布局。而是使用布局管理器,或combinations of them 以及white space 的布局填充和边框。 2) 每个按钮或滑块都会添加一个唯一的侦听器是很常见的。

标签: java swing actionlistener eventhandler changelistener


【解决方案1】:

你定义了:

  • 按钮和滑块的字段
  • 同名的局部变量

在处理程序中,您访问字段,但它们从未被初始化!

initGUI,改写几行:

JSlider xSliceSlider = new JSlider(0, 255, 76); // define a local variable

作为:

xSliceSlider = new JSlider(0, 255, 76); // initialize the field

【讨论】:

  • 这就是解决方案。非常感谢!
【解决方案2】:

我敢打赌这不是您的 GUI 在您的计算机中的样子...是吗?

文本被裁剪,但是...为什么?您可能想知道。

嗯,那是因为您使用了 null-layout 并手动设置了每个组件的边界及其大小。

但是...我该如何解决这个问题?

首先是一些背景阅读:Null layout is evilWhy is it frowned upon to use a null layout in Swing?this answer

现在,对于您的实际问题,您可以关注@MadProgrammer 对this question 的回答,他说:

实际上,如果可以的话,您应该为每个滑块提供它自己的侦听器并直接从源头处理它...

JSlider slider = new JSlider();
slider.addChangeListener(new ChangeListener() {
    public void stateChanged(ChangeEvent e) {
        JSlider slider= (Slider)e.getSource();
        // Do funky stuff
    }
});

按照上述提示,您可以按以下方式修复您的 GUI,并让Layout managers 为您处理窗口大小,因此 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!

【讨论】:

  • 了解这一点非常有用。我不知道空布局是不受欢迎的。我想从理论上讲,自己明确设置所有内容似乎更容易,而不是让经理处理它。谢谢!
  • 正是它给人的印象,但从长远来看,最好使用布局管理器:)
【解决方案3】:

而不是为多个 GUI 对象使用同一个事件处理程序 为每个 GUI 对象使用单独的事件处理程序是很常见的。 这通常是通过实现事件侦听器接口的匿名类来完成的。

在您的应用程序中,您将替换您的代码

    // 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 类了, 并且在事件处理方法中无需检查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...");
    });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 2023-04-03
    • 2014-10-31
    • 1970-01-01
    相关资源
    最近更新 更多