java- repaint() 方法运行异常 - 2?

java- repaint() method is misbehaving - 2?

这个问题是 java- repaint() method is misbehaving? 的延伸 (阅读,可选)

我正在研究 Music Player

我正在使用 JSlider 作为搜索栏并使用 JLabel 在屏幕上绘制文本,例如歌曲名称。

我是 Graphics2D

的新手

这是最小化的代码:

public class JSliderDemo extends JFrame
{
    
JLabel label;
JSlider seek = new JSlider();
int  y = 10;    

public JSliderDemo()
{
 setSize(400, 400);
 setLocationRelativeTo(null);
 setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 
 createWindow();
 setVisible(true);
 startThread();
}        
    
public void createWindow()
{
 JPanel panel = new JPanel(new BorderLayout());
 panel.setOpaque(true);
 panel.setBackground(Color.BLUE);
 panel.setBorder(new LineBorder(Color.YELLOW));
 
 JLayeredPane layeredPane = new JLayeredPane();
 layeredPane.setPreferredSize(new Dimension(300, 310));
 
 label = new Component();
 label.setSize(300, 300);
 
 createSlider();

 layeredPane.add(seek, new Integer(50));
 layeredPane.add(label, new Integer(100));
 
 panel.add(layeredPane);
 add(panel);
}        

protected void createSlider()
{
 seek.setUI(new SeekBar(seek, 300, 10, new Dimension(20, 20), 5, 
           Color.DARK_GRAY, Color.RED, Color.RED));
 seek.setOrientation(JProgressBar.HORIZONTAL);
 seek.setOpaque(false);
 seek.setLocation(10, 50);
 seek.setSize(300, 20);
 seek.setMajorTickSpacing(0);
 seek.setMinorTickSpacing(0);
 seek.setMinimum(0);
 seek.setMaximum(1000);    
 seek.setBorder(new MatteBorder(5, 5, 5, 5, Color.CYAN));
}        

protected void startThread()
{
 Thread thread = new Thread(new Runnable(){
 @Override
 public void run()
 {
  try
  {
   while(true)
   {
    if(y == label.getHeight()){y = 1;}   
    label.repaint();
    y += 1;
   
    Thread.sleep(100);   
   }    
  }   
  catch(Exception ex){}
 }
 });
 
 thread.start();
}        
        
protected class Component extends JLabel
{
@Override
public void paintComponent(Graphics g)
{
 Graphics2D gr = (Graphics2D) g;
 gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 
 gr.setColor(Color.RED);
 gr.setFont(new Font("Calibri", Font.PLAIN, 16));
 gr.drawString("Song Name", 50, y);
 
 gr.dispose();
}       
}          

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

问题是,当我为 JLabel 调用 repaint() 时,它会自动重新绘制 JSlider,即使 JSlider 不包含在 JLabel 中。

输出:

Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted.........

现在,如果我从 Thread 中删除 label.repaint(),则不会重新绘制 JSlider

输出:

Slider re-painted
Slider re-painted

repaint() 方法应该像这样工作吗?

在我的最后一个问题中,有人告诉我使用 Layout Manager,当我确实使用 GridLayout 只是为了检查它是否是解决方案时,它起作用了!

只重绘了JLabel

但是我想在JSlider上重叠JLabel,所以我想到了使用JLayeredPane。而现在,问题又来了。

我该如何解决这个问题?

底线:如何在 JSlider 上重叠 JLabel 而不会导致 repaint() 方法行为异常?

repaint()方法是这样的吗?

正如评论中已经提到的,您的 JSlider 被重绘的原因是它与 JLabel 有重叠的边界。即使您的标签没有在滑块的区域上绘制,swing 仍会将重叠区域标记为脏(即,滑块的重叠部分需要重新绘制),因为 swing 不知道您只在其中绘制组件的一部分。

要减少重绘的数量,您需要缩小 JLabel 的大小。最好只调用其 getPreferredSize() 方法所需的大小。然后您就可以通过移动标签的位置来移动文本。

此外,您不应该以普通方式对 gui 进行更新 Thread。请改用 javax.swing.Timer。它确保对 gui 的所有更新都发生在 swing 事件线程上,这是它们应该进行的地方。

对您的代码进行这些调整后,仅在标签实际位于滑块上方时重新绘制滑块。

public class JSliderDemo extends JFrame {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(JSliderDemo::new);
    }

    private final JLabel label = new CustomLabel();

    public JSliderDemo() {
        setSize(400, 400);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        createWindow();
        setVisible(true);
        startTimer();
    }

    public void createWindow() {
        JPanel panel = new JPanel(new BorderLayout());

        JLayeredPane layeredPane = new JLayeredPane();
        layeredPane.setPreferredSize(new Dimension(300, 310));

        label.setLocation(0, 0);
        label.setBorder(new LineBorder(Color.RED));
        label.setSize(label.getPreferredSize());

        layeredPane.add(createSlider(), Integer.valueOf(50));
        layeredPane.add(label, Integer.valueOf(100));

        panel.add(layeredPane);
        setContentPane(panel);
    }

    protected JSlider createSlider() {
        JSlider seek = new CustomSlider();
        seek.setOrientation(JProgressBar.HORIZONTAL);
        seek.setOpaque(false);
        seek.setLocation(10, 50);
        seek.setSize(300, 20);
        seek.setMajorTickSpacing(0);
        seek.setMinorTickSpacing(0);
        seek.setMinimum(0);
        seek.setMaximum(1000);
        seek.setBorder(new LineBorder(Color.BLUE));
        return seek;
    }

    private void startTimer() {
        new Timer(100, e -> {
            int y = label.getY();
            int maxY = label.getParent().getHeight();
            if (y == maxY) {
                y = -label.getHeight();
            }
            label.setLocation(label.getX(), y + 1);
            label.repaint();
        }).start();
    }

    private static class CustomLabel extends JLabel {

        protected CustomLabel() {
            setFont(new Font("Calibri", Font.PLAIN, 16));
            setText("Song Name");
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("Painting Label");
        }
    }

    protected static class CustomSlider extends JSlider {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("Painting Slider");
        }
    }
}