CardLayout 中显示错误的 JPanel。 getGraphics() 的问题
我有一个名为 SpacePotaters(JFrame 的子类)的顶级 GUI 类,并将一个名为 panelViews(它使用 CardLayout 布局管理器)的 JPanel 添加到其内容窗格中。除此之外,我还添加了一个 MainMenu 对象(JPanel 的子类)和一个 GameView 对象(JPanel 的子类)作为 panelView 的卡片。
public SpacePotaters(){
super("Space Potaters");
Container content = getContentPane();
// initialize components
gameView = new GameView(this);
mainMenu = new MainMenu(this);
leaderboard = new Leaderboard(this);
instructions = new JPanel(); // to do
cLayout = new CardLayout();
// add "cards" to CardLayout manager
panelViews = new JPanel(cLayout);
panelViews.setPreferredSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
panelViews.add("gameView", gameView);
panelViews.add("mainMenu", mainMenu);
panelViews.add("leaderboard", leaderboard);
panelViews.add("instructions", instructions);
// initially display menu menu
content.add(panelViews);
cLayout.show(panelViews,"mainMenu");
addWindowListener(this);
pack();
setResizable(false);
// relocate window to center of screen
setLocationRelativeTo(getRootPane());
}
如果它有用,主菜单绘制方法在这里:
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(mainMenuBackground, 0, 0, spTop.getWindowWidth(), spTop.getWindowHeight(), null);
}
现在我只绘制背景图像 - 稍后我将添加 JButtons,单击时将切换到不同的卡。
我的想法是从主菜单卡开始,并在必要时切换到其他卡。我运行该程序并注意到主菜单卡短暂闪烁,然后被为 gameView 创建的图像替换。我知道我应该等待渲染 gameView 图形,直到我真正切换到该卡,这样游戏就不会过早开始。这不是我感到困惑的地方。即使我不等待,gameView不应该就开始运行而无法查看吗?换句话说,无论 gameView 内部发生什么,mainMenu 不应该仍然是唯一可见的卡片吗?
我认为问题出在 gameView 中,它使用主动渲染和双缓冲。 GameView 有这个方法,在游戏的每个循环中都会调用一次:
public void paintScreen(){
// get fresh graphics context for GameView each time
Graphics context = getGraphics();
if (context != null && dbImage != null){
context.drawImage(dbImage, 0, 0, null);
}
context.dispose();
}
注释掉该方法的主体会产生所需的结果 - 仅显示 mainMenu。我很困惑为什么会出现这种情况。我认为每个组件(JFrame、JPanel 等)都有自己的图形上下文。因此,当我在 GameView 中调用 getGraphics() 时,它不应该返回 gameView 面板的图形上下文,而不是 spTop 或 panelViews 的图形上下文吗?而且由于它仅更改特定的图形上下文,因此如果 gameView 面板不可见(如果我在 mainMenu 对象上使用 CardLayout 的 show() 方法,则不应显示该面板),那么它不应该影响我对主菜单的视图。有什么想法吗?
注意:如果我使用 isVisible() 测试 gameView 和 mainMenu,gameView 返回 false,mainMenu 返回 true。另外,我使用 getGraphics() 来测试 mainMenu、spTop 和 gameView 图形上下文是否都不同 - 它们确实不同。所以gameView正在它自己的图形上下文上绘制图像并且不可见,但是paintScreen()中的drawImage()效果仍然显示!真的很困惑。
I have a top-level GUI class called SpacePotaters (subclass of JFrame) and I add a JPanel called panelViews (which uses the CardLayout layout manager) to its content pane. Among other things, I add a MainMenu object (subclass of JPanel) and a GameView object (subclass of JPanel) as cards to panelView.
public SpacePotaters(){
super("Space Potaters");
Container content = getContentPane();
// initialize components
gameView = new GameView(this);
mainMenu = new MainMenu(this);
leaderboard = new Leaderboard(this);
instructions = new JPanel(); // to do
cLayout = new CardLayout();
// add "cards" to CardLayout manager
panelViews = new JPanel(cLayout);
panelViews.setPreferredSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
panelViews.add("gameView", gameView);
panelViews.add("mainMenu", mainMenu);
panelViews.add("leaderboard", leaderboard);
panelViews.add("instructions", instructions);
// initially display menu menu
content.add(panelViews);
cLayout.show(panelViews,"mainMenu");
addWindowListener(this);
pack();
setResizable(false);
// relocate window to center of screen
setLocationRelativeTo(getRootPane());
}
In case its useful, the main menu paint method is here:
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(mainMenuBackground, 0, 0, spTop.getWindowWidth(), spTop.getWindowHeight(), null);
}
Right now I just paint the background image - later I'll add JButtons that when clicked will switch to different cards.
The idea is that I start off with the main menu card and switch to the other cards when necessary. I ran the program and noticed that the main menu card briefly flashes before being replaced by the image created for gameView. I know that I should wait to render the gameView graphics until I actually switch to that card, so that the game doesn't start prematurely. That's not where I get confused. Even if I don't wait, shouldn't gameView just start running without being able to view it? In other words, shouldn't mainMenu remain the only visible card regardless of what is going on inside gameView?
I figured the issue was something within gameView, which uses active rendering and double-buffering. GameView has this method which is called once every loop of the game:
public void paintScreen(){
// get fresh graphics context for GameView each time
Graphics context = getGraphics();
if (context != null && dbImage != null){
context.drawImage(dbImage, 0, 0, null);
}
context.dispose();
}
Commenting out the body of this method produces the desired results - only the mainMenu is shown. I'm confused as to why this is the case. I thought each component (JFrame, JPanel, etc) has its own graphics context. So when I call getGraphics() inside GameView, shouldn't it return the graphics context for the gameView panel, not the graphics context for spTop or panelViews? And since it only changes that specific graphics context, if the gameView panel is not visible (which it shouldn't be if I use CardLayout's show() method on the mainMenu object), then it shouldn't affect my view of the main menu. Any ideas?
Note: If I test both gameView and mainMenu with isVisible(), gameView returns false and mainMenu returns true. Also, I used getGraphics() to test if the mainMenu, spTop, and gameView graphics contexts were all different - they were. So gameView is drawing an image on its own graphics context and is not visible, but the effects of drawImage( ) within paintScreen() are still showing! Really confused.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在花费了太多时间试图弄清楚发生了什么之后,我只能得出结论,嵌入式图形组件中的主动渲染(至少以我正在使用它的形式)忽略了该组件的可见性。因此,paintScreen() 中的drawImage() 将始终显示可见结果,无论调用它的JPanel 是否可见。
我不完全确定,但我认为这种混乱可能与尝试将主动渲染与被动渲染混合有关。 mainMenu 重写了paintComponent(),因此使用被动渲染来绘制自身。可能是通过将我的 gameView 的可见性设置为 false,顶级 JFrame 根本不会调用 gameView 的 PaintComponent()。但是,由于 gameView 使用 PaintScreen() 进行绘制,因此将其可见性设置为 false 不会影响其行为。这都是基于实验的猜测,因为我找不到任何具体信息。
另外,我不确定如果我尝试使用 BufferStrategy 执行主动渲染(即 BufferStrategy 的 show() 也会忽略其相关图形组件的可见性并绘制到可见屏幕)是否会得到相同的结果以及)。这仍然是一个需要研究的问题。
至于我将如何使这种理解与我当前的程序相协调 - 因为我不想重写所有内容,所以我决定使用混合方法。我将使用被动渲染在 panelViews/“cards”(使用 CardLayout)之间切换。仅当我切换到 gameView 时,我才会打开其主动渲染行为。当我退出并返回到另一张卡时,我会关闭 gameView 的主动渲染。我知道不鼓励混合被动和主动渲染,但希望不会出现问题,因为我从不同时使用这两种策略。这种方法目前适用于 mainMenu 和 gameView 之间的切换,因此我希望当我添加其他被动渲染组件时它也能工作。
After spending way too much time trying to figure out what's going on, I can only conclude that active rendering within an embedded graphics component (at least in the form I'm using it) ignores the visibility of that component. Therefore, drawImage() within paintScreen() will always show visible results, regardless of whether the JPanel it is called from is visible or not.
I'm not entirely sure, but I think this confusion might have something to do with trying to mix active rendering with passive rendering. The mainMenu overrides paintComponent() and so uses passive rendering to paint itself. It might be that by setting the visibility of my gameView to false, the top-level JFrame simply does not call gameView's paintComponent(). However, since gameView draws with paintScreen(), setting its visibility to false does not affect its behavior. This is all speculation based on experimentation, since I could not find any concrete information about this.
Also, I'm not sure if I would have gotten the same results if I would have tried to perform active rendering using a BufferStrategy (i.e. if BufferStrategy's show() would also ignore the visibility of its related graphics component and draw to the visible screen as well). That's something that still needs to be researched.
As far as how I'm going to reconcile this understanding with my current program - since I don't want to rewrite everything, I've decided to use a hybrid approach. I'm going to use passive rendering for switching between my panelViews/"cards" (using CardLayout). Only when I switch to gameView do I turn on its active-rendering behavior. When I exit and return to another card, I turn the active-rendering of gameView back off. I know that mixing passive and active rendering is discouraged, but hopefully no issues arise since I never use both strategies concurrently. This approach currently works for switching between mainMenu and gameView, so I expect it to work when I add other passively-rendered components.