简体   繁体   English

重新验证JPanel时闪烁

[英]Flickering while revalidating JPanel

I have looked all round, and no one seems to have an answer for me. 我四处张望,似乎没有人为我解答。 I have a JPanel where I add/remove labels from it while it;s running. 我有一个JPanel ,可以在运行时从中添加/删除标签。 The problem is that every time I add a new label to it I have to revalidate() it so the new label shows up. 问题在于,每次我向其添加新标签时,都必须revalidate()以便显示新标签。 Every time I revalidate() , it I get a very annoying flicker, and all my labels ghost up to the top of my window for a millisecond then return to what they were doing. 每次我revalidate() ,都会出现非常烦人的闪烁,并且我所有的标签都在窗口顶部隐瞒一毫秒,然后返回到它们正在执行的操作。 The only time I call revalidate() is after I add a label; 我唯一调用revalidate()是在添加标签之后; I do not change the paint method or anything only did 我不更改涂漆方法或仅做任何事情

public void paintComponent(Graphics page)
{
    super.paintComponent(page);
}

so that my page would not look bad. 这样我的页面看起来不会很糟。 I could really use some help on this. 我真的可以为此提供一些帮助。 I've made a asteroids game using JFrame , Jlabel and Jpanel game; 我使用JFrameJlabelJpanel游戏制作了一个小行星游戏; it work great, but this flickering and component "ghosting quickly ghosting to top of panel" is just unbearable, and I know for a fact this is the revalidate() method. 它工作得很好,但是这种闪烁和组件“快速重影到面板顶部的幻影”简直难以忍受,我知道这是一个revalidate()方法。

When I was having problems with filckering Swing components it turned out to be concurrency issues. 当我在处理Swing组件时遇到问题时,事实证明是并发问题。 Since you are doing a game, which means you are doing animation it may be the case here. 由于您正在做游戏,这意味着您正在做动画,在这里可能就是这种情况。

Take care to modify Swing components only from AWT-EVENT thread . 注意仅从AWT-EVENT thread修改Swing组件。 Either use SwingUtilities.invokeLater or do your animation using javax.swing.Timer . 使用SwingUtilities.invokeLater或使用javax.swing.Timer进行动画处理。

As a concrete example, Asteroids doesn't flicker at all. 作为一个具体的例子, Asteroids根本不会闪烁。 Here are a few reasons: 原因如下:

  • It runs on the event dispatch thread . 它在事件分发线程上运行。

  • It uses JPanel , which is double buffered by default. 它使用JPanel ,默认情况下是双缓冲的。

  • It paces the animation using javax.swing.Timer , updating the game's model in actionPerformed() . 它使用javax.swing.Timer调整动画的速度,并在actionPerformed()更新游戏的模型。

  • It overrides paintComponent() , but can safely omit super.paintComponent() because the action listener repaints the entire panel. 它重写了paintComponent() ,但是可以安全地省略super.paintComponent()因为操作侦听器会重新绘制整个面板。

  • The listener calls paintImmediately() to avoid any potential delay; 侦听器调用paintImmediately()以避免任何潜在的延迟。 substitute repaint() to see if there's any difference on your platform. 替换repaint()来查看您的平台上是否有任何区别。

Addendum: As shown in this counter-example , revalidate() does not cause flickering. 附录:如本反例所示revalidate()不会引起闪烁。 Instead of replacing labels, update the label's icon, as shown here . 而不是更换标签, 更新标签的图标,如图所示这里

Have you considered reusing them - ie using setVisible(false) when they're not needed? 您是否考虑过重用它们-即在不需要它们时使用setVisible(false)

Not sure if adding and removing labels is the best way of achieving what you want for a game. 不确定添加和删除标签是否是实现游戏所需的最佳方法。 I'd personally be tempted to manage the drawing myself. 我本人很想亲自管理绘图。

Using labels (or generally GUI components) to represent quickly animating, dynamically created/moved objects is not practical. 使用标签(或通常的GUI组件)来表示快速动画化,动态创建/移动的对象是不切实际的。 Its like using a car to drive from the living room to the kitchen. 就像用汽车从客厅开车到厨房一样。

The Swing components come with a lot of nice features, eg they can determine the space they require and a layout manager can use that to automatically place them nicely. Swing组件具有许多不错的功能,例如,它们可以确定所需的空间,而布局管理器可以使用该功能自动将它们很好地放置。 Unfortunately for a game, you neither need all the nice stuff, nor do you want to pay the processing overhead involved with it. 不幸的是,对于游戏而言,您既不需要所有好东西,也不想支付与之相关的处理开销。 Matters are further complicated by swings multi-platform capabilities which mandate that each component has a peer component (the peer deals with rendering the component to the active look). 摆动的多平台功能使每个组件都具有一个对等组件(使对等组件将组件呈现为活动外观),使事情变得更加复杂。

To solve this, you do not represent individual game objects as swing components. 要解决此问题,您不能将单个游戏对象表示为挥杆组件。 Instead you use a single swing component (usually JPanel) as a canvas to paint your game objects on to. 取而代之的是,您使用单个挥杆组件(通常是JPanel)作为画布在其上绘制游戏对象。 You simply call repaint() for that JPanel periodically (eg 30 or 60 times a second). 您只需定期(例如每秒30或60次)为该JPanel调用repaint()。 Of course you will override paintComponent() on the JPanel to do paint all the game objects using your own code. 当然,您将重写JPanel上的paintComponent()以使用您自己的代码绘制所有游戏对象。

Edit: To outline how you do your own rendering 编辑:概述您如何进行自己的渲染

You need to decide how to organize your game related objects. 您需要确定如何组织与游戏相关的对象。 A very simple skeleton could look like this (getters/setters omitted): 一个非常简单的骨架可能看起来像这样(省略getters / setter):

public class GameObject {
    public int x;
    public int y;
    public Image image;
}

In practice this class will have additional members (like a behavior that controls how the object moves, and maybe some states/counters for animation). 实际上,该类将具有其他成员(例如,控制对象移动方式的行为,以及某些动画的状态/计数器)。 It may also be abstract with a concrete subclass for each possible behavior (like ship, asteroid and bullet). 它也可以是抽象的,并针对每种可能的行为(例如轮船,小行星和子弹)使用具体的子类。 All instances of GameObject are managed in some kind of collection, I assume you use a List. GameObject的所有实例都通过某种集合进行管理,我假设您使用的是List。

When implementing the paintComponent it comes down to just loop through the list and render each objects's image: 在实现paintComponent时,它可以简化为遍历列表并呈现每个对象的图像:

public void paintComponent(Graphics g) {
    List<GameObject> list = ... // however you obtain it in your game
    for (GameObject gobject : list) {
        g.drawImage(gobject.image, gobject.x, gobject.y, (ImageObserver) null);
    }
}

More elaborate games may order the gameobjects to get control of how game objects overlap each other. 更精细的游戏可以命令游戏对象来控制游戏对象如何相互重叠。 But in principle its incredibly simple. 但是原则上它非常简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM