【问题标题】:java- repaint() method is misbehaving - 2?java- repaint() 方法行为不端 - 2?
【发布时间】:2020-06-29 03:26:35
【问题描述】:

这个问题是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() 方法是这样工作的吗?

【问题讨论】:

  • 我认为swing的RepaintManager也可以考虑脏区,而且由于你自己处理组件的大小和位置,它们是否有可能重叠并导致RepaintManager重新绘制脏区中的所有组件?
  • 问题是,当我为 JLabel 调用 repaint() 时,即使 JSlider 不包含在 JLabel 中,它也会自动用它重新绘制 JSlider。 - 为什么会出现问题? JLabel 是透明的。在绘制透明组件时,Swing首先需要重新绘制不透明标签的父组件,以便正确绘制背景。
  • 为什么要扩展 JLabel 来绘制文本字符串?如果您想要动画,请使用常规 JLabel 并更改标签的位置。
  • @camickr 我不明白!在这个答案中,stackoverflow.com/a/62631255/13824394 你说,你不应该设置组件的位置。这是布局管理器的工作,但在这里你建议我手动更改JLabel 的位置以模拟动画。
  • 标签和滑块的边界重叠。重绘标签时,这种摆动将需要重绘它们,因为单独重绘标签可能会覆盖滑块的一部分。请注意,即使重新绘制了标签,它实际上也只会重新绘制与滑块重叠的部分(因此实际上可能不会做任何事情)。

标签: java swing graphics2d jlayeredpane null-layout-manager


【解决方案1】:

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

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

此外,您不应该在普通的Thread 中更新 gui。请改用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");
        }
    }
}

【讨论】:

  • 你的 JSlider 被重绘的原因是它与 JLabel 有重叠的界限 - 这并不完全正确,因为我试图解释。 1) 将动画减慢到 250 。 2) 使标签不透明。 3)当标签进入并完全覆盖滑块时,滑块不会被重新绘制。这是因为 Swing 并不关心标签下方的组件。当组件不透明时,它会优化绘画。因此,当标签在滑块上移动时绘制滑块的原因是因为标签是透明的。
  • 4) 但是,当标签移出滑块底部时,滑块和标签都会重新绘制,因为 Swing 需要重新绘制标签覆盖滑块的先前区域。 5) 另外,请注意label.repaint() 不是必需的。当组件的属性发生更改时,Swing 组件将重新绘制自己。
猜你喜欢
  • 2013-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多