【问题标题】:JPanel flickering [closed]JPanel闪烁[关闭]
【发布时间】:2014-09-04 23:17:22
【问题描述】:

目前我正在使用自定义 JPanel 创建自定义 JFrame。渲染 JFrame 不会闪烁,但是当我渲染自定义 `JPanel 时,组件会闪烁。我的代码:

我绘制JFrame的代码:

    public void paint(Graphics g) {
    Dimension d = getSize();
    Graphics og = g;
    g = b.getGraphics();
    //background
    g.setColor(SConstants.ct.FRAME_BACKGROUND());
    g.fillRect(SConstants.ct.FRAME_FRAME_WIDTH(), SConstants.ct.FRAME_FRAME_HEIGHT(), d.width - SConstants.ct.FRAME_FRAME_WIDTH(), d.height - SConstants.ct.FRAME_FRAME_WIDTH());

    //frame
    if(isFocused()){
        g.setColor(SConstants.ct.FRAME_FOCUSED_FRAME());
    }else{
        g.setColor(SConstants.ct.FRAME_UNFOCUSED_FRAME());
    }
    //top
    g.fillRect(0, 0, d.width, SConstants.ct.FRAME_FRAME_HEIGHT());
    //left
    g.fillRect(0, 0, SConstants.ct.FRAME_FRAME_WIDTH(), d.height);
    //bottom
    g.fillRect(0, d.height - SConstants.ct.FRAME_FRAME_WIDTH(), d.width, SConstants.ct.FRAME_FRAME_WIDTH());
    //right
    g.fillRect(d.width - SConstants.ct.FRAME_FRAME_WIDTH(), 0, SConstants.ct.FRAME_FRAME_WIDTH(), d.height);

    //icon
    if(getIconImage() != null)
        g.drawImage(getIconImage(), 15, SConstants.ct.FRAME_FRAME_HEIGHT() / 2 - getIconImage().getHeight(null) / 2, null);

    Graphics2D g2d = (Graphics2D) g;
    g2d.setFont(SConstants.ct.FRAME_FONT());
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setColor(Color.BLACK);
    g2d.drawString(getTitle(), d.width / 2 - g2d.getFontMetrics().stringWidth(getTitle()) / 2, SConstants.ct.FRAME_FRAME_HEIGHT() / 2 - g2d.getFontMetrics().getHeight() / 2 + g2d.getFontMetrics().getAscent());
    getContentPane().paint(og);
    og.drawImage(b, 0, 0, null);
}

我正在像这样设置contentPane:

getRootPane().setLayout(null);
    SPanel cp = new SPanel();
    cp.setBounds(SConstants.ct.FRAME_FRAME_WIDTH(), SConstants.ct.FRAME_FRAME_HEIGHT(), getWidth() - SConstants.ct.FRAME_FRAME_WIDTH() * 2, getHeight() - SConstants.ct.FRAME_FRAME_HEIGHT() - SConstants.ct.FRAME_FRAME_WIDTH());
    setContentPane(cp);

SPanel 是这样的:

public void paint(Graphics g) {
    paintComponent(g);
}

protected void paintComponent(Graphics g) {
//paintComponents(g);
    for(Component c : getComponents())
        c.paint(g);
}

我正在像这样渲染SLabel:

public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.drawString(text, getX() + SConstants.ct.LABEL_SPACING_HORIZONTAL(), getY());
}

public Dimension getPreferredSize() {
    Rectangle2D d = getGraphics().getFontMetrics(SConstants.ct.LABEL_FONT()).getStringBounds(text, getGraphics());
    int w = (int)(d.getWidth() + SConstants.ct.LABEL_SPACING_HORIZONTAL() * 2);
    int h = (int)(d.getHeight() + SConstants.ct.LABEL_SPACING_VERTICAL() * 2);
    return new Dimension(w, h);
}

可运行示例:https://drive.google.com/file/d/0B8czlxc22-xqOHlPdUZxV2dkMjQ/edit?usp=sharing

有人知道为什么SLabel 会闪烁吗?

【问题讨论】:

  • 你为什么要手动绘制Components?他们有自己的绘画过程。您正在执行双重绘画动作,因为组件可以由paintComponents 绘画或由RepaintManager 指示自己绘画 - 您无法控制绘画过程。你已经多次打破油漆链。您提供的所有内容都毫无意义,坦率地说,您不应该这样做
  • 运行绘制组件甚至不显示标签。
  • 这是什么意思?您是否实际上将SLabel 添加到其中一个容器中? SLabel 是某种JComponent 吗?
  • 您还替换了内容窗格,请考虑提供一个 runnable example 来证明您的问题。这将导致更少的混乱和更好的响应
  • 另外,通常情况下,paint 会调用paintComponent,因为您认为正常流程对您来说不够好,您破坏了底层的默认双缓冲支持由基于 Swing 的组件提供

标签: java swing jframe rendering


【解决方案1】:

您似乎不了解 Swing 的工作原理或 Swing/AWT 中的绘画如何工作......

SMain...

这...

public class SMain {

    public static void main(String[] args) throws Exception {
        //...
        while(true){
            sf.repaint();
            Thread.sleep((int)(1000.0 / 24.0));
        }
    }
}

是个坏主意...Swing 使用被动渲染模型,这意味着更新会在需要时发生,因为您似乎没有对 UI 的状态进行更改,这只是增加了更多压力到您的系统上。

如果调用得当,它还可能阻止您的 UI 永远更新。


SFrame...

getRootPane().setLayout(null);

为什么?只是为什么? JRootPane 有它自己的布局管理过程,确保 contentPane 提供包含框架内的所有空间减去菜单栏的空间(如果提供的话),是的,默认实现将允许contentPane 占用所有可用空间...

setSize 可以连续调用多次,最好简单地使b 无效(将其设置为null)并仅在需要时创建它,(当调用paint 时) ,但这引出了为什么?

为什么不让内容窗格为您执行此操作...

pack 会自动调整contentPane 的大小,如果你真的让JRootPane 来完成它的工作...

paint 做了许多重要的工作,而你已经绕过了大部分,这会给你带来无穷无尽的问题和怪诞......

举个例子,在尝试绘制内容窗格之前,您没有翻译 Graphics 上下文,这意味着内容绘制的内容可能会覆盖您所绘制的内容(即标题) .

你最好使用...

  • 内容窗格原样,将标题添加到BorderLayout.NORTH 位置和剩余内容,包裹在另一个容器中到默认contentPane 的BorderLayout.CENTER 位置
  • 实现您自己的JRootPane 以提供您想要的布局,对标题和内容使用单独的组件
  • 为JFrame 实现您自己的外观委托并自己绘制边框
  • 在contentPane 上使用EmptyBorder 为标题提供空间...
  • 除了你所做的事情之外的其他任何事情......

请注意,MouseListener 和 MouseMotionListener 可能会被容器层次结构中实现其中一个或两个接口的更高组件阻塞


面板...

paint 做了很多重要的工作,包括但不限于绘制组件、边框和组件(以及实现双缓冲),但你决定绕过它们,做你自己的事...

paintComponent 假设绘制组件的背景,而不是子组件。 paint 和 paintChildren 和 paintComponents 已经完成了这项工作

子组件也可以独立于其父组件进行绘制...


S组件...

注意,Component 是重量级 (AWT) 组件,它的涂装要求与轻量级 (Swing) 组件不同,实际上,您应该避免在轻量级容器中添加重量级组件....

Component 已经定义了paint(Graphics)...

默认情况下,重量级组件也不双缓冲...这意味着更新可能会导致闪烁...因此您的问题...

“但是我已经对帧进行了双重缓冲”...很抱歉让您失望了,但是组件可以并且通常独立于其父级进行绘制,因此它们可能不会作为父级更新的一部分进行绘制...


结论...

您似乎不了解 AWT/Swing 中绘画的工作原理。更多详情请查看Painting in AWT and Swing...

您似乎也不了解 Swing 和 AWT 之间的区别。虽然 Swing 位于 AWT 之上,但它有自己的框架要求,不同于 AWT 和一些重要的方式......

您似乎正在尝试实现自己的框架...为什么,我不知道,显然过去 20 多年的 Swing 和 AWT 开发还不够好或其他什么...

您可以考虑查看Modifying the Look and Feel 并使用 UI 委托 API 来实现您正在尝试做的事情...

您有两个选择,您可以选择了解当前框架 (Swing/AWT) 的工作原理并在其中工作以实现您的目标,或者在余生中追逐怪异、无法解释的问题和错误。

您还可以考虑查看JavaFX,它允许您通过 CSS 修改外观...

【讨论】:

    猜你喜欢
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多