【问题标题】:Overlapping of paint GUI绘制 GUI 的重叠
【发布时间】:2018-05-16 01:10:26
【问题描述】:

所以我基本上是在我的代码运行时创建倒计时, 这很好,工作我唯一的问题是,而不是 数字消失并被 i 的新值替换,它们保持新值与现有数字重叠。我试图使用 repaint () 来解决这个问题,但它不起作用。我想知道我是否缺少某些东西,或者我是否应该完全尝试不同的方式。

这是计时器的代码:

public void timer (Graphics g,int x){
    x=550;

    for (int i = time;i>0;i--){
        g.setColor(deepPlum);
        g.setFont(new Font("Monospaced", Font.PLAIN, 25));
        g.drawString("time: "+i, x, 650);
        repaint();

    } 
}

【问题讨论】:

  • 您的断章取义代码 sn-p 提出了更多问题,我们可以回答。考虑提供一个Minimal, Complete, and Verifiable example - 关注“如何”执行代码(方法是如何调用的?绘画实际上是如何下来的?Graphics 来自哪里?),因为此刻,代码有点吓到我
  • 猜测 - 您在所需的绘画周期之外进行绘画;您正在使用基于 alpha 的颜色作为背景来尝试实现透明度;您没有遵守规定的油漆链要求(通过不调用 super 油漆方法) - 我无法想到的其他事情
  • 我确实调用了超级绘制方法,但在绘制方法中。我知道这很好,因为我所有的其他图形都工作正常。
  • 那么你做错了什么,这在你的上下文示例中没有展示。如果你调用 timer 而不是 paint 方法,那么你使用了错误的方法,这可能解释了整个问题
  • 至于背景我完全没碰过。我正在使用 JFrame 附带的基本灰色

标签: java swing user-interface for-loop repaint


【解决方案1】:

在“猜测”中

我不喜欢猜测,但似乎我们不会得到一个可运行的示例来清楚地了解问题是如何产生的。

证据...

我确实调用了超级绘制方法,但在绘制方法中。我知道这很好,因为我所有的其他图形都可以正常工作。

这“表明”timer 被称为正常 Swing“绘制”周期的一部分。如果这是真的,它解释了问题的原因。

这...

public void timer (Graphics g,int x){
    x=550;

    for (int i = time;i>0;i--){
        g.setColor(deepPlum);
        g.setFont(new Font("Monospaced", Font.PLAIN, 25));
        g.drawString("time: "+i, x, 650);
        repaint();

    } 
}

只是在每次绘制周期发生时将所有数字相互绘制,这实际上不是倒计时计时器应该绘制的方式。

相反,它应该简单地绘制剩余时间,单个值,而不是尝试绘制所有时间。

您可以将“绘制周期”视为“帧”,它绘制组件当前状态的快照。您需要绘制许多这样的“帧”,以便为从一个时间点到另一个时间点的变化设置动画。

我再次强烈建议您花时间阅读Performing Custom PaintingPainting in AWT and Swing,以更好地了解绘画的实际工作原理。

如果没有更多证据,我建议,Swing Timer 将是更好的解决方案。这可用于计算剩余时间并定期安排repaint

因为我似乎经常做这种事情,所以我将核心功能封装成一个简单的Counter 类,它有一个“持续时间”(或运行时),可以计算出它一直在运行的durationremaining 时间和 progressdurationrunTime 的百分比,可能比您需要的更多,但它为您提供了开始的基础。

因为绘画是一种不精确的艺术(绘画周期之间的时间是可变的,即使使用Timer),我使用了基于“时间”的方法,而不是简单的“计数器”。在处理这些类型的场景时,这为您提供了更高水平的精确度

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                // The following two lines just set up the
                // "default" size of the frame
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JComponent {

        private Counter counter;

        public TestPane() {
            Timer timer = new Timer(1000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (counter == null) {
                        counter = new Counter(Duration.of(10, ChronoUnit.SECONDS));
                        counter.start();
                    }
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (counter != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                FontMetrics fm = g2d.getFontMetrics();

                long time = counter.getRemaining().getSeconds();

                int x = (getWidth() - fm.stringWidth(Long.toString(time))) / 2;
                int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
                g2d.drawString(Long.toString(time), x, y);
                g2d.dispose();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

    public static class Counter {

        private Duration length;
        private LocalDateTime startedAt;

        public Counter(Duration length) {
            this.length = length;
        }

        public Duration getLength() {
            return length;
        }

        public void start() {
            startedAt = LocalDateTime.now();
        }

        public void stop() {
            startedAt = null;
        }

        public Duration getDuration() {
            if (startedAt == null) {
                return null;
            }
            Duration duration = Duration.between(startedAt, LocalDateTime.now());
            if (duration.compareTo(getLength()) > 0) {
                duration = getLength();
            }
            return duration;
        }

        public Double getProgress() {
            if (startedAt == null) {
                return null;
            }
            Duration duration = getDuration();
            return Math.min(1.0, (double) duration.toMillis() / length.toMillis());
        }

        public Duration getRemaining() {
            if (startedAt == null) {
                return null;
            }
            Duration length = getLength();
            Duration time = getDuration();

            LocalDateTime endTime = startedAt.plus(length);
            LocalDateTime runTime = startedAt.plus(time);

            Duration duration = Duration.between(runTime, endTime);
            if (duration.isNegative()) {
                duration = Duration.ZERO;
            }

            return duration;
        }
    }

}

在有人告诉我clearRect 将如何解决问题之前,请务必注意clearRect 将做两件事。一,它只会显示循环的“最后”值,这显然不是OP想要的,它会用Graphics上下文的当前背景颜色填充指定区域,“可能”不是什么OP 想要的(因为它会在之前绘制的其他内容的顶部绘制一个漂亮的矩形)。

如果使用得当,Swing 绘制系统会在调用组件的paint 方法之前准备好Graphics 上下文,从而需要使用clearRect 静音

【讨论】:

    【解决方案2】:

    在再次绘制之前调用 clearRect 恢复矩形区域的背景

    【讨论】:

    • 所有事情都是平等的,Graphics 上下文应该由 Swing 绘制引擎“准备”,包括用组件的背景颜色填充它,或者如果它是透明的则清除上下文,这建议 OP 正在超出所需的绘画周期进行绘画,或者他们正在做其他事情(比如破坏绘画链),无论如何clearRect 要么无法解决问题,要么只会掩盖更大的问题
    • clearRect 将“掩盖”更大的问题 - 因此,从长远来看,您实际上并没有真正帮助 OP,而是实际上在伤害他们,这意味着我们将收到更多关于代码原因的问题进一步打破轨道,这意味着我们需要强调他们过去给出的“坏建议”,这让我们都看起来......好吧,糟糕:P - 真正需要的(来自OP)是一个 MCV - 那么我们就可以停止猜测了
    • 用潜在的“黑客”引导一些人误入歧途对任何人都没有帮助 - 找出造成问题的“什么”将更有助于为答案提供合适的解决方案 - 命运多舛听起来,我只是想提高问题的质量和提供的答案-并要求您考虑这样一个事实,即它们可能是一个“更大的问题”,并不意味着“有害”任何人,再次,我很抱歉你错误地接受了我的建议
    • 仅仅因为它存在,并不意味着它是这项工作的“正确”工具——我只是说我们缺乏了解“正确”方法的背景——如果你'对你的答案有信心,然后无论如何,骑它
    • 我不同意@MadProgrammer 只是因为他们从未明确表示“这是一个废话 答案”。它比噪音更糟糕,因为它取决于黑客(即使在这种情况下也可能有效也可能无效),而不是发现问题的真实性质。它肯定会赚到“负一”。
    猜你喜欢
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-28
    • 2019-08-12
    相关资源
    最近更新 更多