JFrame在从ActionListener调用时未正确显示。

huangapple go评论65阅读模式
英文:

JFrame not showing up properly when called from ActionListener

问题

public class Start extends JFrame {
    public static void main(String...s) {
        Start obj = new Start();
        obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        obj.setBounds(100, 100, 300, 300);
        JPanel main = new JPanel();
        obj.add(main);
        JButton btn = new JButton("Login");
        main.add(btn);
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                obj.setVisible(false);
                obj.dispose();
                new Progress();
            }
        });
        obj.setVisible(true);
    }
}

class Progress extends JFrame {
    int pro = 0;
    
    Progress() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        setBounds(100, 100, 300, 300);
        JPanel main = new JPanel();
        add(main);
        main.add(new JLabel("ejfbasj"));
        JProgressBar progressBar = new JProgressBar(0, 100);
        main.add(progressBar);

        Timer timer = new Timer(10, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (pro <= 100) {
                    progressBar.setValue(pro);
                    pro++;
                }
            }
        });
        timer.start();
    }
}

Note: The translation above only includes the translated code part as requested. If you have any questions or need further assistance, feel free to ask.

英文:
public class Start extends JFrame{
public static void main(String...s){
Start obj = new Start();
obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
obj.setBounds(100,100,300,300);
JPanel main = new JPanel();
obj.add(main);
JButton btn = new JButton(&quot;Login&quot;);
main.add(btn);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
obj.setVisible(false);
obj.dispose();
new Progress();
}
});
obj.setVisible(true);
}
}

<br/>

class Progress extends JFrame{
int pro = 0;
Progress(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setBounds(100,100,300,300);
JPanel main = new JPanel();
add(main);
main.add(new JLabel(&quot;ejfbasj&quot;)); 
JProgressBar progressBar = new JProgressBar(0,100);
main.add(progressBar);
Thread t1 = new Thread() {
public void run() {
while(pro&lt;=100) {
/*
* Do something - set progressbar value
* */
try {
sleep(10);
} catch (InterruptedException e) {}
progressBar.setValue(pro);
pro++;
}
}
};
t1.start();
//Do something - gives progress values
try {
t1.join();
} catch (InterruptedException e) {}
}
}

Above is Minimal, Reproducible Example of my problem.
When Progress JFrame is called from ActionListner, the JFrame doesn't appear properly. I get a black JFrame and after a sec I get final JFrame with full progressbar. But if I call new Progress() directly, it works properly(i.e. seeing progressbar filling up).
I found that creating new Thread in UI thread is unable to draw Frame. And I should use Swing.Timer.
I don't know how to do it with Swing.Timer.
Any other approach is appreciated.

答案1

得分: 2

obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

有一个即时的问题但还有很多其他问题将其更改为以下内容以确保在销毁后 JRE 不会退出

obj.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

至于其他一些问题

- 参见[使用多个 JFrames是好还是坏的实践](http://stackoverflow.com/q/9554636/418556) 在这种情况下,我建议在将每个 GUI 重构为面板后,采取以下两种方法之一:
  1. 使用 [`CardLayout`](http://download.oracle.com/javase/8/docs/api/java/awt/CardLayout.html) 将两个面板都显示在一个框架中,如[此答案中所示](http://stackoverflow.com/a/5786005/418556)。
  2. 使用 `JDialog` 替代第二个框架
- 始终在事件分派线程上启动和更新 GUI不这样做会导致不可预测的差异
- 不要忽略异常它们准确地告诉我们出了什么问题除非实施了日志记录至少调用 `Throwable.printStackTrace()`
英文:
obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

There's the immediate problem, but there are so many more. Change it to the following to ensure the JRE does not exit once it is disposed:

obj.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

As to some of the other problems:

  • See The Use of Multiple JFrames, Good/Bad Practice? In this case I'd recommend one of two approaches, after refactoring each GUI into a panel:
    1. Display both panels in one frame using CardLayout as shown in this answer.
    2. Use a JDialog instead of the 2nd frame.
  • Always start and update the GUI on the Event Dispatch Thread. Not doing so causes unpredictable differences.
  • Don't ignore exceptions! They inform us exactly what went wrong. Unless logging is implemented, at least call Throwable.printStackTrace()

答案2

得分: 1

问题 #1

Progress(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);

在建立UI之后,最后调用setVisible。如果需要在UI可见后更新UI,您应该调用invalidaterepaint以触发新的布局和绘制过程。

问题 #2

Thread t1 = new Thread() {
public void run() {
while(pro<=100) {
/*
* 做一些事情 - 设置进度条的值
* */
try {
sleep(10);
} catch (InterruptedException e) {}
progressBar.setValue(pro);
pro++;
}
}
};
t1.start();
// 做一些事情 - 提供进度值
try {
t1.join();
} catch (InterruptedException e) {}

好的,实际上这是两个问题。

首先,Swing不是线程安全的,您不应该从事件分派线程之外的上下文更新UI。正如您所说,您应该使用Swing Timer,或者可能更合适的是SwingWorker

其次,t1.join正在阻塞当前线程。因为这个构造函数是从ActionListener中调用的,这意味着您在事件分派线程的上下文中,即用于更新UI的线程上下文中,所以在线程完成之前不会有任何变化。

例如:

其他问题...

我还想讨论一些关于使用多个框架,setBoundspacksetLocationRelativeTo之类的内容,以及使用边框或布局约束等其他内容,以增加填充,但是已经有很多其他的示例了。

英文:

Problem #1

Progress(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);

Call setVisible last, after you've established the UI. If you need to update the UI after it's visible, you should call invalidate and repaint to trigger a new layout and paint pass

Problem #2

Thread t1 = new Thread() {
public void run() {
while(pro&lt;=100) {
/*
* Do something - set progressbar value
* */
try {
sleep(10);
} catch (InterruptedException e) {}
progressBar.setValue(pro);
pro++;
}
}
};
t1.start();
//Do something - gives progress values
try {
t1.join();
} catch (InterruptedException e) {}

Ok, this is actually tw problems.

First, Swing is NOT thread safe and you should not update the UI from outside the context of the Event Dispatching Thread. You should, as you said, either use a Swing Timer or probably more suitably, a SwingWorker

Secondly, t1.join is blocking the current thread. Because this constructor was called from the ActionListener, this means you're in the context of the Event Dispatching Thread ie, the thread used to update the UI with, so, nothing will change until the thread is completed

For example:

Other problems...

I'd also discuss things about using multiple frames, setBounds of things like pack, setLocationRelativeTo and using other things like borders or layout constraints to increase the padding, but there are plenty of other examples

huangapple
  • 本文由 发表于 2020年8月30日 05:57:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/63652166.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定