Swing 重绘 repaint,updateUI区别

本文探讨Swing中组件的重绘机制,特别是repaint方法的工作原理及其延时问题的解决方案。介绍了paint(), repaint(), revalidate(), paintImmediately()等关键方法的作用,并通过示例代码展示如何在事件监听中动态添加组件。

repaint   

public void repaint()重绘此组件。

如果此组件是轻量组件,则此方法会尽快调用此组件的 paint 方法。否则此方法会尽快调用此组件的 update 方法。
注:有关 AWT 和 Swing 所用绘制机制的更多信息,包括如何编写最高效的绘制代码的信息,请参阅 。
updateUI
public void updateUI()UIManager 发出的关于 L&F 已经更改的通知。用 UIManager 的最新版本替换当前的 UI 对象。覆盖: 类 JComponent 中的 updateUI另请参见: JComponent.updateUI()
可能大家都试过在swing的事件监听中动态添加组件,但是JFrame却不会动态显示,只有变大变小(实际是重画了)才会显示。repaint,updateUI可以,用validate发现也可以。
API中对repaint()方法是这样描述的,调度完当前所有未完成的事件后重新绘制该组件,所以repaint方法不总是马上执行。

package tbutton;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Ss extends JFrame {
       Container con = this.getContentPane();
       JButton jb1 = new JButton("jb1");
       JButton jb2 = new JButton("jb2");
       JLabel jl1 = new JLabel("jl1");
       FlowLayout gly = new FlowLayout();
       JPanel jp = new JPanel(gly);


 
       public Ss() {
               con.add(jp);
               jp.add(jb1);
               jp.add(jb2);
               MyListener ml = new MyListener();
               jb1.addMouseListener(ml);
               this.setSize(300, 200);
               this.setVisible(true);
       }
       private class MyListener extends MouseAdapter {
               @Override
               public void mouseClicked(MouseEvent e) {
                       // TODO Auto-generated method stub
                       try {
                               JButton jb3 = new JButton("jb3");
                               jp.add(jb3);
//                               jp.updateUI();//可以正常显示
                               jp.repaint();//API中对repaint()方法是这样描述的,调度完当前所有未完成的事件后重新绘制该组件,repaint方法不总是马上执行,所以只有调整大小才可以显示。
                       //主要就是下面的invalidate和validate
                       //当然,用jp来invalidate和validatae也是可以的
//                                   jp.invalidate();
                       } catch (Exception ex) {
                               ex.printStackTrace();               }
       }
       public static void main(String s[]) {
               Ss sss = new Ss();
               sss.setVisible(true);}

swing重绘主要有四个关键方法:paint(),repaint(),revalidate(),paintImmediately(); 
而进行绘制时后它会依次调用update(),paint(),paintComponent(),paintBorder(),paintChildren()进行绘制; 

那么repaint()方法为什么会延时呢? 

调用repaint()会导致一个区域被增加到重绘列表队列中,且被预定重绘。产生一个防止到系统事件队列中的请求, 
一旦该请求被处理,内部工具自动毁掉组件的paintImmediately()方法。然后该方法立即执行绘制; 

也就是说通常情况下repaint()它是不会立即执行的,调用它后会有一个等待处理的过程。但repaint比较高效,会推迟实际的绘制并将多余的请求压缩到单个 paint 调用中。 

所以如何解决它的repaint()延时问题呢? 
1.让它立即执行:使用paintImmediately()。 

2.使用SwingUtilities工具类中的invokeLater(Runnable doRun)方法;把你要在repaint()后执行的操作写在要执行的线程中;(它会把这个方法也加入到java内部事件队列中,它排在repaint()之后,所以一般情况下,它是等repaint()执行完之后才会执行,虽然这样达到效果,但并不理想);


<think>我们正在讨论Java Swing中的组件问题。用户询问如何正确组件以及最佳实践。根据引用内容,我们有一些示例代码展示了Swing组件的创建和方法。 首先,在Swing中,组件通常意味着更新组件的显示外观。当组件的状态改变且需要反映在界面上时,我们需要请求Swing是单线程的,所有UI操作都应在事件调度线程(EDT)上执行。 组件的基本步骤: 1. 修改组件的状态(例如,改变文本、颜色、位置等)。 2. 调用组件的`repaint()`方法,该方法会异步请求。它会将请求加入事件队列,由EDT在适当的时候执行`paint()`方法。 3. 如果需要立即,可以在调用`repaint()`后调用`revalidate()`(当组件大小或布局变化时),然后调用`paintImmediately()`(不常用,通常让Swing自动处理)。 注意:不要直接调用`paint()`方法,而应使用`repaint()`。 最佳实践: - 始终在EDT上修改UI组件。 - 使用`repaint()`来触发,而不是直接调用`paint()`。 - 当组件的布局发生变化(例如添加或移除子组件,改变大小)时,调用`revalidate()`(对于容器)来触发新的布局。 引用[2]和[3]中提到了时没有清除之前制的内容的问题。这通常是因为我们没有在`paintComponent`方法中先调用`super.paintComponent(g)`来清除背景。在自定义制时,我们应该这样写: ```java @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 先调用父类方法清除背景 // 然后进行自定义制 } ``` 另外,引用[4]提到了动态添加控件并刷新界面,这通常需要调用`revalidate()`和`repaint()`。 示例:自定义一个制圆的组件 ```java import javax.swing.*; import java.awt.*; public class CircleComponent extends JComponent { private Color circleColor = Color.RED; private int diameter = 50; public void setCircleColor(Color color) { circleColor = color; repaint(); // 请求 } public void setDiameter(int diameter) { this.diameter = diameter; repaint(); // 请求 } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 清除背景 g.setColor(circleColor); g.fillOval(0, 0, diameter, diameter); } } ``` 使用这个组件时,改变颜色或直径后调用`setCircleColor`或`setDiameter`就会触发。 总结: 1. 修改组件状态后,调用`repaint()`。 2. 自定义制时,在`paintComponent`中先调用`super.paintComponent(g)`。 3. 当组件结构变化(如添加/移除子组件)时,调用`revalidate()`(然后会自动触发)。 相关问题: 1. 如何在Java Swing中实现自定义制? 2. 为什么在Swing中修改UI必须在事件调度线程(EDT)上? 3. `repaint()`和`revalidate()`有什么区别?</think>### Java Swing 组件方法与最佳实践 在 Java Swing 中正确组件需要理解其 **制机制** 和 **线程安全规则**。核心原则是:所有 UI 修改必须在 **事件调度线程 (EDT)** 中执行,并通过 `repaint()` 或 `revalidate()` 触发系统级请求。 #### 一、基本方法 1. **`repaint()` 方法**(最常用) - 请求异步整个组件或其指定区域 - 自动触发 `paintComponent()` 调用 ```java // 修改组件状态后调用 myComponent.setBackground(Color.BLUE); myComponent.repaint(); // 触发 ``` 2. **`revalidate()` 方法** - 当组件**布局结构变化**时使用(如添加/删除子组件) - 自动调用 `repaint()` ```java panel.remove(oldButton); panel.add(newButton); panel.revalidate(); // 更新布局+ ``` 3. **自定义制**(覆盖 `paintComponent()`) ```java class CustomPanel extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 必须先调用!清除背景[^3] g.setColor(Color.RED); g.drawOval(10, 10, 50, 50); // 自定义制 } } ``` #### 二、最佳实践 1. **EDT 规则** - 所有 UI 操作必须在 EDT 中执行: ```java SwingUtilities.invokeLater(() -> { component.repaint(); // 其他UI操作 }); ``` 2. **局部优化** - 指定区域提升性能: ```java // 只 (x,y,width,height) 矩形区域 component.repaint(10, 10, 100, 100); ``` 3. **双缓冲启用**(默认开启) - 减少闪烁,在组件构造器中启用: ```java setDoubleBuffered(true); // 平滑制[^2] ``` 4. **避免直接调用 `paint()`** - 始终通过 `repaint()` 间接触发,保持制流程可控 #### 三、常见问题解决 1. **残留制内容** → 检查是否遗漏 `super.paintComponent(g)`[^3] 2. **动态添加控件不显示** → 组合使用 `revalidate() + repaint()`[^4] 3. **性能瓶颈** → 使用局部 + 双缓冲 > **关键点**:Swing 采用 **被动制模型**。开发者修改状态后请求(`repaint`),系统在适当时机自动调用 `paintComponent`。直接操作 Graphics 对象会导致未定义行为[^2]。 #### 四、操作流程示例 ```java // 动态更新界面 SwingUtilities.invokeLater(() -> { customPanel.setNewData(data); // 更新数据 customPanel.repaint(); // 请求 }); // 自定义组件模板 class ValidComponent extends JComponent { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 关键! // 自定义制逻辑 } } ``` [^1]: 分割面板示例说明Swing组件结构 [^2]: 时未清除背景导致残留 [^3]: 正确覆盖paintComponent的模板 [^4]: 动态控件需revalidate刷新布局
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值