创建 JPanel 实例后跳过代码

问题描述 投票:0回答:1

我正在制作俄罗斯方块游戏的副本并创建了一个游戏面板。

我试图交换

tetris.initGamePanel();
tetris.initWindow();
的顺序

public static void main(String[] args){ Tetris tetris = new Tetris(); tetris.initGamePanel(); tetris.initWindow();//Why the order matters??? //tetris.initGamePanel(); }
发现当先执行

tetris.initGamePanel();

时,程序运行得很好,但是当顺序改为相反时,出现了一个空白窗口。

于是我尝试调试代码,发现剩下的代码在

public void initGamePanel() { JPanel game_main = new JPanel(); game_main.setLayout(new GridLayout(game_x,game_y,1,1)); //initialize game panel for(int i = 0; i < text.length; i++) { for (int j = 0; j < text[i].length; j++) { //create a new JTextArea Object that contains parameter i and j text [i][j] = new JTextArea(); text [i][j].setBackground(Color.white); text [i][j].addKeyListener(this); text [i][j].setEditable(false); if(j == 0 || j == text[i].length-1 || i == text.length-1) { text [i][j].setBackground(Color.MAGENTA); data [i][j] = 1; } game_main.add(text[i][j]); } } this.setLayout(new BorderLayout()); this.add(game_main,BorderLayout.CENTER); }
在创建 JPanel 实例后被跳过 

public void initGamePanel(){ JPanel game_main = new JPanel();
但奇怪的是,尽管其余的代码甚至都没有完成,但一切似乎都运行良好(按正确的顺序)。

这是完整的代码:

import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class Tetris extends JFrame implements KeyListener { private static final int game_x = 24; private static final int game_y = 12; public Tetris(){ text = new JTextArea[game_x][game_y]; data = new int[game_x][game_y]; //initGamePanel(); } JTextArea[][] text; int [][] data; public void initWindow(){ this.setSize(400,800); this.setLocationRelativeTo(null);//align to the center if the component is set to null this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setResizable(false); this.setTitle("Tetris"); } public void initGamePanel(){ JPanel game_main = new JPanel(); game_main.setLayout(new GridLayout(game_x,game_y,1,1)); //initialize game panel for(int i = 0; i < text.length; i++){ for (int j = 0; j < text[i].length; j++){ //create a new JTextArea Object that contains parameter i and j text [i][j] = new JTextArea(); text [i][j].setBackground(Color.white); text [i][j].addKeyListener(this); text [i][j].setEditable(false); if(j == 0 || j == text[i].length-1 || i == text.length-1 ){ text [i][j].setBackground(Color.MAGENTA); data [i][j] = 1; } game_main.add(text[i][j]); } } this.setLayout(new BorderLayout()); this.add(game_main,BorderLayout.CENTER); } @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } public static void main(String[] args){ Tetris tetris = new Tetris(); tetris.initGamePanel(); tetris.initWindow();//Why the order matters??? //tetris.initGamePanel(); } }
我认为这是因为 JPanel 的构造函数以某种方式执行了其余的代码,但是如何执行?

java swing constructor jpanel awt
1个回答
2
投票

initGamePanel

 正在更新 UI。  如果在 
initWindow
 之前调用,则会计算 UI 的布局,并将在窗口变得可见的过程中进行绘制。

如果您反转调用,则不会告诉系统 UI 的状态已更改,因此它不会执行这些操作。

Swing 很懒。 当您更改 UI 的状态(尤其是布局(即添加/删除组件))时,您必须通知系统应执行新的布局和绘制通道。

您应该在

revalidate

 方法的末尾添加对 
repaint
initGamePanel
 的调用,例如...

public void initGamePanel(){ // ... revalidate(); repaint(); }

this.setSize(400,800);

是个坏主意。  窗口可查看的内容将是窗口的大小减去窗口装饰插图。

相反,您应该使用布局管理系统,并在窗口上调用

pack

(但您实际上应该在建立基本布局后才执行此操作)。

我还会将

text [i][j] = new JTextArea();

 更改为 
text [i][j] = new JTextArea(1, 1);
 之类的内容,以便让 
JTextArea
 报告合适的首选尺寸。  不知道为什么你要使用这个,似乎对于任务来说过于复杂,但这就是我。
JTextArea

对于监控键盘输入来说也是一个糟糕的选择。 我可能会考虑使用

text [i][j].addKeyListener(this);

,或者,如果您不使用文本输入组件,请使用 
键绑定 API
,这将使您避免许多看似随机的问题。
在窗口可见后调用 DocumentListener

时出现问题,这改变了窗口装饰的大小并做了各种奇怪的事情。 作为用户,如果您不这样做,而是广泛使用布局管理 API,以便在调整窗口大小时更好地管理组件,我将不胜感激。

此外,
this.setResizable(false);

所做的工作确实应该在构造函数中完成,这样在构造函数返回时 UI 就已经准备好了。

您还应该避免从顶级容器(例如
initGamePanel

)进行扩展。您没有向类添加新功能,而是将自己锁定在单一使用上下文中。

JFrame

也是一个复杂的复合组件,通常最好尽可能避免这种混乱。
	

© www.soinside.com 2019 - 2024. All rights reserved.