Swing UI 在后台重复元素

问题描述 投票:0回答:1

尝试为具有简单计时器(带有开始/重置和停止键绑定)的游戏创建覆盖。使用自定义进度条来绘制计时器进度和计时器中的其他一些关键点。

由于某种未知的原因,启动计时器、计时器滴答和停止计时器会导致一些 UI 重影问题,调整窗口大小会暂时清除它,直到下一个导致重影的操作为止。

造成这种重影的原因是什么以及如何防止它?

截图:

How the timer should look at all times After starting the timer

最小复制:

import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;

@Slf4j
public class SmudgeTimerPanel extends JPanel {

    private static final int TIMER_SPIRIT = 180; // seconds
    private static final int TIMER_DEMON = 60; // seconds
    private static final int TIMER_OTHER = 90; // seconds

    private final CustomProgressBar progressBar;
    private final JLabel timeRemainingLabel;
    private final JLabel statusLabel;
    private final JLabel ghostLabel;

    private Timer timer;
    private int remainingTime;
    private boolean isRunning;

    public SmudgeTimerPanel() {
        setLayout(new GridBagLayout());
        setBackground(new Color(50, 50, 50, 150)); // Transparent gray background

        progressBar = new CustomProgressBar(0, TIMER_SPIRIT);

        timeRemainingLabel = new JLabel("3:00");
        timeRemainingLabel.setForeground(Color.WHITE);
        timeRemainingLabel.setBackground(new Color(0, 0, 0, 0));
        timeRemainingLabel.setFont(timeRemainingLabel.getFont().deriveFont(24f));

        statusLabel = new JLabel();
        statusLabel.setOpaque(true);
        statusLabel.setBackground(Color.RED);
        statusLabel.setPreferredSize(new Dimension(10, 10));

        ghostLabel = new JLabel("None");
        ghostLabel.setForeground(Color.WHITE);
        ghostLabel.setFont(ghostLabel.getFont().deriveFont(24f));

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(10, 10, 10, 10);
        add(progressBar, gbc);

        gbc.gridy = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.CENTER;

        JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        statusPanel.setOpaque(false);
        statusPanel.add(statusLabel);
        statusPanel.add(timeRemainingLabel);
        add(statusPanel, gbc);

        gbc.gridy = 2;
        gbc.gridx = 0;
        gbc.fill = GridBagConstraints.NONE;
        gbc.anchor = GridBagConstraints.CENTER;
        add(ghostLabel, gbc);

        gbc.gridy = 3;
        gbc.fill = GridBagConstraints.HORIZONTAL;

        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        buttonPanel.setOpaque(false);

        JButton startResetButton = new JButton("Start/Reset Timer");
        startResetButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                resetTimer(TIMER_SPIRIT);
            }
        });

        JButton stopButton = new JButton("Stop Timer");
        stopButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                stopTimer();
            }
        });

        buttonPanel.add(startResetButton);
        buttonPanel.add(stopButton);
        add(buttonPanel, gbc);
    }

    public void resetTimer(int timerDuration) {
        if (timer != null) {
            timer.cancel();
        }

        remainingTime = timerDuration;
        isRunning = true;
        updateGhostLabel();
        updateTimeDisplay();
        progressBar.setMaximum(timerDuration);
        progressBar.setValue(timerDuration);
        statusLabel.setBackground(Color.GREEN);

        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                SwingUtilities.invokeLater(() -> {
                    if (remainingTime > 0) {
                        remainingTime--;
                        progressBar.setValue(remainingTime);
                        updateTimeDisplay();
                        updateGhostLabel();
                    } else {
                        stopTimer();
                    }
                });
            }
        }, 0, 1000);
    }

    public void stopTimer() {
        if (timer != null) {
            timer.cancel();
        }
        isRunning = false;
        statusLabel.setBackground(Color.RED);
    }

    public void startOrResetTimer(String ghostType) {
        resetTimer(TIMER_SPIRIT);
    }

    private void updateTimeDisplay() {
        int minutes = remainingTime / 60;
        int seconds = remainingTime % 60;
        timeRemainingLabel.setText(String.format("%d:%02d", minutes, seconds));
        timeRemainingLabel.setBackground(new Color(0, 0, 0, 0));
    }

    private void updateGhostLabel() {
        if (remainingTime > TIMER_OTHER) {
            ghostLabel.setText("Spirit");
        } else if (remainingTime > TIMER_DEMON) {
            ghostLabel.setText("All Others");
        } else {
            ghostLabel.setText("Demon");
        }
    }

    public boolean isTimerRunning() {
        return isRunning;
    }

    @Slf4j
    private static class CustomProgressBar extends JComponent {
        private int value;
        private int maximum;

        public CustomProgressBar(int min, int max) {
            this.value = min;
            this.maximum = max;
            setPreferredSize(new Dimension(300, 20));
            setMinimumSize(new Dimension(300, 20));
        }

        public void setValue(int value) {
            this.value = Math.min(value, maximum);
            repaint();
        }

        public void setMaximum(int maximum) {
            this.maximum = maximum;
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            int width = getWidth();
            int height = getHeight();
            log.info("Width: {}, Height: {}", width, height);

            // Draw background
            g2d.setColor(Color.GRAY);
            g2d.fillRect(0, 0, width, height);

            // Draw progress
            int progressWidth = (int) ((value / (double) maximum) * width);
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, progressWidth, height);

            // Draw tick marks
            g2d.setColor(Color.BLACK);
            int demonMark = (int) (((TIMER_SPIRIT - TIMER_DEMON) / (double) maximum) * width);
            int otherMark = (int) ((TIMER_OTHER / (double) maximum) * width);

            g2d.drawLine(demonMark, 0, demonMark, height);
            g2d.drawLine(otherMark, 0, otherMark, height);
        }
    }

    // Test the SmudgeTimerPanel in its own JFrame
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Smudge Timer Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 300);

            SmudgeTimerPanel timerPanel = new SmudgeTimerPanel();
            frame.add(timerPanel);
            frame.setVisible(true);
        });
    }
}
java swing user-interface
1个回答
0
投票

通过一些修改,我们解决了问题...

// change attribute type, because modifications to   
// the Guide must be made from the event thread (EDT).   
private javax.swing.Timer timer;

public void resetTimer( int timerDuration ) {
      if( timer != null ) {
         timer.stop();
      }
      remainingTime = timerDuration;
      isRunning = true;
      updateGhostLabel();
      updateTimeDisplay();
      progressBar.setMaximum( timerDuration );
      progressBar.setValue( timerDuration );
      statusLabel.setBackground( Color.GREEN );

      // change initialization
      timer = new javax.swing.Timer( 1000, e -> {

         // delete "SwingUtilities.invokeLater( () -> {" it isn't necesary
         if( remainingTime > 0 ) {
            remainingTime --;
            progressBar.setValue( remainingTime );
            updateTimeDisplay();
            updateGhostLabel();
             // call "repaint" to update the interface
            repaint();
         }
         else {
            stopTimer();
         }
      } );
      timer.start();
   }

   public void stopTimer() {
      if( timer != null ) {
         // change "cancel()" to "stop()"
         timer.stop();
      }
      isRunning = false;
      statusLabel.setBackground( Color.RED );
   }
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.