实现 JFrame 和 JPanel 的区别
Difference between implementing JFrame and JPanel
我试图了解 JFrame 和 JPanel 之间的区别。我倾向于使用 JFrame 的子类而不是 JPanel,但是人们总是告诉我最好使用 JPanel 的子类。这是我使用 JFrame 的示例:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Game extends JFrame implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
public void move() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public class AL extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(+1);
}
if (keyCode == e.VK_UP) {
setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
setYCoord(+1);
}
Game.this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(0);
}
if (keyCode == e.VK_UP) {
setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
setYCoord(0);
}
Game.this.repaint();
}
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
addKeyListener(new AL());
setTitle("Game");
setSize(500, 500);
setResizable(true);
setVisible(true);
setBackground(Color.BLACK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 250;
y = 250;
}
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
@Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
@Override
public void run() {
try {
while (true) {
move();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
这工作正常(除了当我按下其中一个按钮时有一个小的延迟),但是当我尝试通过实现 JPanel 而不是 JFrame 来更改我的代码时,没有任何显示...这是代码对于 JPanel 子类:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
JFrame frame;
public void move() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public class AL extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(+1);
}
if (keyCode == e.VK_UP) {
setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
setYCoord(+1);
}
Game.this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(0);
}
if (keyCode == e.VK_UP) {
setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
setYCoord(0);
}
Game.this.repaint();
}
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
frame = new JFrame();
frame.addKeyListener(new AL());
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.setVisible(true);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 250;
y = 250;
}
@Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
@Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
@Override
public void run() {
try {
while (true) {
move();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
如代码所示,您需要自定义 JPanel
,因为您想要更改某些方法的行为,例如 paintComponent
.
但是,您不需要 自定义 JFrame
,因此无需创建 class 扩展它。
最后,您的主要 class 无需成为您的面板 class。
这是一个示例 class,我将框架内容从 Game
的构造函数移至此主要 class。
public class MainClass {
public static void main(String[] args) {
Game game = new Game();
JFrame frame = new JFrame();
frame.addKeyListener(new AL());
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.getContentPane().add(game);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread(game);
t.start();
}
}
JFrame
是一个比您想象的要复杂得多的组件,对于初学者来说,它有一个 JRootPane
作为主容器,其中包含 contentPane
、JMenuBar
并控制 glassPane
有关详细信息,请参阅 How to Use Root Panes。
JFrame
也有装饰(边框),这些边框是在window本身的范围内绘制的,然后内容在其中布局,所以边框不会绘制到此为止。
当您覆盖顶级容器的 paint
时,例如 JFrame
,您会 运行 遇到许多问题:
- 它不是双缓冲的,这会导致 window 更新时出现闪烁
- 其他组件可以独立于框架绘制(因此不会调用框架的
paint
方法),这可能会导致无休止的问题
- 您现在可以在相框的装饰下方作画,有关详细信息,请参阅Java graphic image, How to get the EXACT middle of a screen, even when re-sized and How can I set in the midst?。
- 您将自己锁定在单个用例中,无法将 windows 添加到其他容器,这会减少您的组件 re-use 价值
一般来说,从 OOP 的角度来看,您实际上并没有向 class 添加任何新功能(或者至少 none,这无法通过更好的方法生成)。
当你使用像JPanel
这样的东西时,以上所有其他的都不再关心:
- 它们默认是双缓冲的
- 如果您使用布局管理器(在内容窗格上),组件将布置在框架的装饰内
- 组件的
width
和height
代表整个可视区域
- 您可以将此组件添加到您想要的任何容器中
您还应该覆盖 JPanel
的 getPreferredSize
方法和 return 您希望面板通常的首选大小,然后您可以使用 JFrame#pack
"pack" 周围的 window,这将使 window 大于内容区域,但这意味着您不会挠头想知道为什么将 window 设置为一定尺寸,但你的组件更小
我试图了解 JFrame 和 JPanel 之间的区别。我倾向于使用 JFrame 的子类而不是 JPanel,但是人们总是告诉我最好使用 JPanel 的子类。这是我使用 JFrame 的示例:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Game extends JFrame implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
public void move() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public class AL extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(+1);
}
if (keyCode == e.VK_UP) {
setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
setYCoord(+1);
}
Game.this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(0);
}
if (keyCode == e.VK_UP) {
setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
setYCoord(0);
}
Game.this.repaint();
}
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
addKeyListener(new AL());
setTitle("Game");
setSize(500, 500);
setResizable(true);
setVisible(true);
setBackground(Color.BLACK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 250;
y = 250;
}
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
@Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
@Override
public void run() {
try {
while (true) {
move();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
这工作正常(除了当我按下其中一个按钮时有一个小的延迟),但是当我尝试通过实现 JPanel 而不是 JFrame 来更改我的代码时,没有任何显示...这是代码对于 JPanel 子类:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
JFrame frame;
public void move() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public class AL extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(+1);
}
if (keyCode == e.VK_UP) {
setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
setYCoord(+1);
}
Game.this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(0);
}
if (keyCode == e.VK_UP) {
setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
setYCoord(0);
}
Game.this.repaint();
}
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
frame = new JFrame();
frame.addKeyListener(new AL());
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.setVisible(true);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 250;
y = 250;
}
@Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
@Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
@Override
public void run() {
try {
while (true) {
move();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
如代码所示,您需要自定义 JPanel
,因为您想要更改某些方法的行为,例如 paintComponent
.
但是,您不需要 自定义 JFrame
,因此无需创建 class 扩展它。
最后,您的主要 class 无需成为您的面板 class。
这是一个示例 class,我将框架内容从 Game
的构造函数移至此主要 class。
public class MainClass {
public static void main(String[] args) {
Game game = new Game();
JFrame frame = new JFrame();
frame.addKeyListener(new AL());
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.getContentPane().add(game);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread(game);
t.start();
}
}
JFrame
是一个比您想象的要复杂得多的组件,对于初学者来说,它有一个 JRootPane
作为主容器,其中包含 contentPane
、JMenuBar
并控制 glassPane
有关详细信息,请参阅 How to Use Root Panes。
JFrame
也有装饰(边框),这些边框是在window本身的范围内绘制的,然后内容在其中布局,所以边框不会绘制到此为止。
当您覆盖顶级容器的 paint
时,例如 JFrame
,您会 运行 遇到许多问题:
- 它不是双缓冲的,这会导致 window 更新时出现闪烁
- 其他组件可以独立于框架绘制(因此不会调用框架的
paint
方法),这可能会导致无休止的问题 - 您现在可以在相框的装饰下方作画,有关详细信息,请参阅Java graphic image, How to get the EXACT middle of a screen, even when re-sized and How can I set in the midst?。
- 您将自己锁定在单个用例中,无法将 windows 添加到其他容器,这会减少您的组件 re-use 价值
一般来说,从 OOP 的角度来看,您实际上并没有向 class 添加任何新功能(或者至少 none,这无法通过更好的方法生成)。
当你使用像JPanel
这样的东西时,以上所有其他的都不再关心:
- 它们默认是双缓冲的
- 如果您使用布局管理器(在内容窗格上),组件将布置在框架的装饰内
- 组件的
width
和height
代表整个可视区域 - 您可以将此组件添加到您想要的任何容器中
您还应该覆盖 JPanel
的 getPreferredSize
方法和 return 您希望面板通常的首选大小,然后您可以使用 JFrame#pack
"pack" 周围的 window,这将使 window 大于内容区域,但这意味着您不会挠头想知道为什么将 window 设置为一定尺寸,但你的组件更小