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");
}
}
}
这个问题是 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");
}
}
}