JAVA - 从动作侦听器

时间:2018-05-17 09:04:57

标签: java swing jframe

当我按下一个按钮调用时,我的一个框架看起来不应该看起来有问题。

框架看起来好像被渲染得不正确,其中的标签文本被缩短了,但是当我在动作监听器外面移动相同的代码行时,它就可以正常工作。

我有一个主菜单,有两个按钮,目前只有Generate Menu工作,它看起来像这样:

https://i.imgur.com/k1Ne5v9.png

动作监听器的代码:

    runMenuButt.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Generate Menu pressed");
            mF.dispose();
            MenuGenerator.generateTheMenu();
        }
    });

结果看起来不对:https://i.imgur.com/n86y4CD.png 框架也没有响应,但是X没有响应,而它应该关闭框架和应用程序。

但是将代码更改为:

    runMenuButt.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Generate Menu pressed");
            //mF.dispose();

        }
    });
    MenuGenerator.generateTheMenu();

产生正确的外观:https://i.imgur.com/TFbkmAO.png

"主菜单"

的代码
public static void openMainMenu() {
    Font menuFont = new Font("Courier",Font.BOLD,16);
    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
    mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mF.setSize(465,230);
    mF.setLocation(dim.width/2-mF.getSize().width/2, dim.height/2-mF.getSize().height/2);
    mF.getContentPane().setBackground(Color.WHITE);

    Color blueSteel = new Color(70,107,176);
    JPanel p = new JPanel();
    p.setSize(600,50);
    p.setLayout(new GridBagLayout());
    GridBagConstraints gbc = new GridBagConstraints();
    p.setLocation((mF.getWidth() - p.getWidth()) /2, 20);
    p.setBackground(blueSteel);
    JLabel l = new JLabel("Welcome to the menu GENERATORRRR");
    l.setFont(menuFont);
    l.setForeground(Color.WHITE);
    p.add(l, gbc);

    JButton runMenuButt = new JButton("Generate Menu");
    runMenuButt.setLocation(20 , 90);
    JButton manageRecipButt = new JButton("Manage Recipients");
    manageRecipButt.setLocation(240 , 90);
    menuUtilities.formatButton(runMenuButt);
    menuUtilities.formatButton(manageRecipButt);

    mF.setResizable(false);
    mF.setLayout(null);
    mF.add(runMenuButt);
    mF.add(manageRecipButt);
    mF.add(p);
    mF.setVisible(true);

    runMenuButt.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Generate Menu pressed");
            //mF.dispose();

        }
    });
    MenuGenerator.generateTheMenu();

    manageRecipButt.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            JOptionPane.showMessageDialog(null, "Not supported yet", "Function not yet available",JOptionPane.INFORMATION_MESSAGE);
        }
    });
    //System.out.println(mF.getContentPane().getSize());
}

状态栏:

public class StatusBar {
private static JLabel statusLabel= new JLabel("Starting");
private static JFrame statusFrame = new JFrame("Generation Status");

public static void createStatusBar() {
    Font menuFont = new Font(Font.MONOSPACED,Font.BOLD,20);
    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();

    statusFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    statusFrame.setSize(700,100);

    JPanel p = new JPanel();
    p.setPreferredSize(new Dimension(100,100));
    statusLabel.setFont(menuFont);
    p.add(statusLabel);

    statusFrame.add(p,BorderLayout.CENTER);
    statusFrame.setLocation(dim.width/2-statusFrame.getSize().width/2, dim.height/2-statusFrame.getSize().height/2);
    statusFrame.setVisible(true);
}

public static void setStatusBar(String statusText) {
    statusLabel.setText(statusText);
    statusLabel.paintImmediately(statusLabel.getVisibleRect());
    statusLabel.revalidate();
}

public static void closeStatusBar(){
    statusFrame.dispose();
}
}

我用这一行创建了这个栏:     StatusBar.createStatusBar();

为什么MenuGenerator.generateTheMenu();状态栏无法正常呈现?是从动作监听器调用的吗?

以下是为需要测试它的任何人重现此行为的最小代码:它还使用已发布的StatusBar类。

public class MinimalClass {
private static JFrame mF = new JFrame("Main Menu");

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

public static void openMainMenu() {
    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
    mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mF.setSize(465,230);
    mF.setLocation(dim.width/2-mF.getSize().width/2, dim.height/2-mF.getSize().height/2);
    mF.getContentPane().setBackground(Color.WHITE);

    JButton runMenuButt = new JButton("Generate Menu");
    runMenuButt.setLocation(20 , 90);
    runMenuButt.setSize(200 , 85);

    mF.setResizable(false);
    mF.setLayout(null);
    mF.add(runMenuButt);
    mF.setVisible(true);

    runMenuButt.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Generate Menu pressed");
            mF.dispose();
            generateTheMenu();
        }
    });
}

public static void generateTheMenu() {
    System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
    String rawMenuOutput = "";

    try {
        rawMenuOutput= getMenuInJavaNow();
    } catch (Exception e){
        System.out.println("Something went terribly wrong");
    }
    System.out.println(rawMenuOutput);
}

public static String getMenuInJavaNow() throws IOException {

    String rawMenuOutput = "Restaurant Menu" ;
    rawMenuOutput = rawMenuOutput + "Test line";
    String []menuOtpArr = new String [3];

    try {
        StatusBar.createStatusBar();
        TimeUnit.SECONDS.sleep(2);
        StatusBar.setStatusBar("Test1");
        TimeUnit.SECONDS.sleep(2);
        menuOtpArr[0]="Test line";
        StatusBar.setStatusBar("Test2");
        TimeUnit.SECONDS.sleep(2);
        menuOtpArr[1]="Test line";
        StatusBar.setStatusBar("Test3");
        TimeUnit.SECONDS.sleep(2);
        menuOtpArr[2]="Test line";
        StatusBar.setStatusBar("Test4");
        TimeUnit.SECONDS.sleep(2);
        StatusBar.closeStatusBar();
    } catch (Exception e) {
    }

    for (int i=0;i < menuOtpArr.length;i++) {
        rawMenuOutput = rawMenuOutput + "\n\n" +menuOtpArr[i];
    }
    return rawMenuOutput;
}
}

感谢您的时间

1 个答案:

答案 0 :(得分:1)

statusLabel.paintImmediately(statusLabel.getVisibleRect());似乎掩盖了一个更大的问题。

问题是,Swing是单线程的(并且不是线程安全的)。这意味着当您从TimeUnit.SECONDS.sleep(2); getMenuInJavaNow调用generateTheMenu时,ActionListener调用getMenuInJavaNowSwingWorker调用import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingWorker; public class MinimalClass { private static JFrame mF = new JFrame("Main Menu"); public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { openMainMenu(); } }); } public static void openMainMenu() { Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mF.setLocationRelativeTo(null); mF.getContentPane().setBackground(Color.WHITE); JButton runMenuButt = new JButton("Generate Menu"); runMenuButt.setMargin(new Insets(25, 25, 25, 25)); JPanel buttons = new JPanel(new GridLayout(0, 2)); buttons.add(runMenuButt); mF.add(buttons); mF.pack(); mF.setVisible(true); runMenuButt.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("Generate Menu pressed"); mF.dispose(); generateTheMenu(); } }); } public static void generateTheMenu() { System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider"); StatusBar.createStatusBar(); SwingWorker<String, String> worker = new SwingWorker<String, String>() { @Override protected String doInBackground() throws Exception { String rawMenuOutput = "Restaurant Menu"; rawMenuOutput = rawMenuOutput + "Test line"; String[] menuOtpArr = new String[3]; try { TimeUnit.SECONDS.sleep(2); publish("1234567890123456789012345678901234567890"); TimeUnit.SECONDS.sleep(2); publish("This is a test"); TimeUnit.SECONDS.sleep(2); publish("More testing"); TimeUnit.SECONDS.sleep(2); publish("Still testing"); TimeUnit.SECONDS.sleep(2); } catch (Exception e) { } for (int i = 0; i < menuOtpArr.length; i++) { rawMenuOutput = rawMenuOutput + "\n\n" + menuOtpArr[i]; } return rawMenuOutput; } @Override protected void done() { StatusBar.closeStatusBar(); } @Override protected void process(List<String> chunks) { StatusBar.setStatusBar(chunks.get(chunks.size() - 1)); } }; worker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (worker.getState() == SwingWorker.StateValue.DONE) { try { String result = worker.get(); System.out.println(result); StatusBar.closeStatusBar(); } catch (InterruptedException | ExecutionException ex) { ex.printStackTrace(); } } } }); worker.execute(); } public static class StatusBar { private static JLabel statusLabel = new JLabel("Starting"); private static JFrame statusFrame = new JFrame("Generation Status"); public static void createStatusBar() { Font menuFont = new Font(Font.MONOSPACED, Font.BOLD, 20); Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); statusFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); statusFrame.setSize(700, 100); JPanel p = new JPanel(); p.setBackground(Color.RED); // p.setPreferredSize(new Dimension(100, 100)); statusLabel.setFont(menuFont); p.add(statusLabel); statusFrame.add(p, BorderLayout.CENTER); statusFrame.setLocationRelativeTo(null); statusFrame.setVisible(true); } public static void setStatusBar(String statusText) { statusLabel.setText(statusText); } public static void closeStatusBar() { statusFrame.dispose(); } } } 事件调度线程。

这使得EDT处于睡眠状态,这意味着它没有处理布局或绘制请求(正确)

首先阅读Concurrency in Swing了解更多详情

现在,你有一个更大的问题,如何解决它。对于该问题的答案,我们需要更多的上下文,然后才能获得。

static似乎正在返回一些价值观,以及我不确定的结果。

&#34; A&#34;解决方案是使用setLayout(null)(有关详细信息,请参阅Worker Threads and SwingWorker)。它提供了在后台执行长时间运行任务的能力,但也提供了将更新同步回UI的方法,例如......

setPreferred/Minimum/MaximumSize

观测...

  • done不是你的朋友,特别是在这种情况下。你真的,真的,真的需要学会没有它的生活。
  • SwingWorker对你没有好处,特别是从长远来看。花点时间通过Laying Out Components Within a Container并开始正确使用布局管理器,它们可能看起来很复杂&#34;但它们可以帮助您避免大量脱发
  • 尽可能避免使用PropertyChangeListener,您正在抢夺能够提供有用的渲染提示的组件,这些提示可能会跨平台和渲染管道发生变化
  

快速跟进问题,done和addPropertyListener有什么区别?有没有?使用两者都不是多余的吗?

这里的示例非常基本,对我来说,我已经使用StatusBar.setStatusBar("")来处理publish&#34;知道&#34;我们需要做,但不知道结果会怎么做。

我已经使用interface来处理这个问题 - 重点 - 它就是一个例子。

  

我还注意到,我不需要实际发布,如调用StatusBar.setStatusBar(&#34;&#34;);也有效。是否有必要使用发布?

总之,是的。 Swing不是线程安全的,直接调用interface会导致一些奇怪和意外的结果。 SwingWorker将调用推送到Event Dispatching Thread,从而可以安全地从内部更新UI。

  

我有生成字符串的代码我想在另一个类中设置为StatusBar Title,而不是在generateTheMenu中,因此我只需调用.setStatusBar就更方便了。我所拥有的最小代码实际上就是这样的

这就像String这样的东西非常方便。你&#34;字符串&#34;生成课程&#34; can&#34;要么返回结果文本,要么可以传递对publish实现的引用,该实现用于显示&#34;它。这样,您的json.loads()可以充当pd.Series()的消费者,并通过In [85]: df.join(df.pop('col3').apply(lambda x: pd.Series(json.loads(x)))) Out[85]: col1 col2 col4 col3_1 col3_2 0 1 Programming 11 None Java 1 2 Sport 22 None Soccer 2 3 Food 33 None Pizza 方法传递它。

有许多非常重要的概念需要理解。

  • 您想要解耦代码。这样可以更轻松地更改代码的某些部分而不会影响其他部分
  • 您希望能够将代码编程为接口,而不是实现&#34;。这与第一条评论密切相关。基本上,你想&#34;隐藏&#34;尽可能多的实现细节 - 它有很多不同的原因,但它有助于保持代码的精简,有助于使后续更容易理解,并阻止代码的一部分访问另一部分它真的没有责任这样做(是字符串生成真的负责更新状态栏?恕我直言 - 不是真的)
  • 还有一个design patterns的招摇,可以更轻松地解决问题。我已经提到了&#34;生产/消费者&#34;的概念,但这只是一个

&#34;事件调度线程&#34;