java怎么清除JPanel里面所有组件并重绘

Swing界面更新实践
本文介绍了一种在Java Swing中实现界面动态更新的有效方法。通过使用removeAll(), repaint(), add() 和 revalidate()等方法组合,实现了JPanel组件的清除与重绘。详细解释了每个方法的作用及使用时机,确保了界面元素能够正确地更新和显示。

我拿我自己正在做的一个界面来介绍一下

这是是解决了清除JPanel组件并能重绘的代码

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;

public class TestFrame extends JFrame implements ActionListener {

JLabel welcome = new JLabel("欢迎使用报刊订阅系统:");
JLabel user = new JLabel("user");
JButton linsert = new JButton("报 刊 录 入");
JButton lsearch = new JButton("报 刊 查 询");
JButton laccount = new JButton("报 刊 统 计");
JLabel lcenter = new JLabel("欢迎试用!");
JPanel ptop = new JPanel();
JPanel pleft1 = new JPanel();
JPanel pleft2 = new JPanel();
JPanel pleft3 = new JPanel();
JPanel pleft = new JPanel();
JPanel pcenter = new JPanel();


public TestFrame() {

super("企业报刊订阅管理系统");

ptop.setLayout(new FlowLayout(0));
pleft.setLayout(new GridLayout(8, 1));
ptop.add(welcome);
ptop.add(user);
pleft1.add(linsert);
pleft2.add(lsearch);
pleft3.add(laccount);
pleft.add(pleft1);
pleft.add(pleft2);
pleft.add(pleft3);
pcenter.add(lcenter);
ptop.setBorder(new EtchedBorder(EtchedBorder.RAISED));
pleft.setBorder(new EtchedBorder(EtchedBorder.RAISED));
pcenter.setBorder(new EtchedBorder(EtchedBorder.RAISED));

Border border=BorderFactory.createEtchedBorder(Color.white, Color.white);
Border title=BorderFactory.createTitledBorder(border,"订阅管理",TitledBorder.RIGHT,TitledBorder.TOP,
new Font("楷体", Font.BOLD, 26),Color.blue);

welcome.setFont(new Font("楷体", Font.BOLD, 24));
user.setFont(new Font("楷体", Font.BOLD, 20));
user.setForeground(Color.CYAN);
linsert.setFont(new Font("楷体", Font.BOLD, 20));
lsearch.setFont(new Font("楷体", Font.BOLD, 20));
laccount.setFont(new Font("楷体", Font.BOLD, 20));
lcenter.setFont(new Font("楷体", Font.BOLD, 24));
pleft.setBorder(border);
pleft.setBorder(title);
this.setSize(800, 600);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setLayout(new BorderLayout());
this.setVisible(true);
this.add(ptop, BorderLayout.NORTH);
this.add(pleft, BorderLayout.WEST);
this.add(pcenter, BorderLayout.CENTER);
linsert.addActionListener(this);
lsearch.addActionListener(this);
laccount.addActionListener(this);
}
public static void main(String[] args) {
new TestFrame();

}

 

@Override
public void actionPerformed(ActionEvent e) {
//录入
if (e.getSource() == linsert) {
pcenter.removeAll();
pcenter.repaint();
pcenter.add(Insert.setInsert());
pcenter.revalidate();
}
//查询
if (e.getSource() == lsearch) {
pcenter.removeAll();
pcenter.repaint();
pcenter.add(Search.setSearch());
pcenter.revalidate();
}
//统计
if (e.getSource() == laccount) {
pcenter.removeAll();
pcenter.repaint();
pcenter.add(Account.setAccount());
pcenter.revalidate();
}
}

}

我把清空组件和重绘放在按钮单击按钮事件里面完成的,所以主要就是按钮里面的方法。

 

一开始我用的是

if (e.getSource() == laccount) {

pcenter.removeAll();
pcenter.add(Account.setAccount());

}

运行发现没用。

然后加了repaint()重绘图形区域

if (e.getSource() == laccount) {
   pcenter.removeAll();
pcenter.repaint();
pcenter.add(Account.setAccount());

}

运行发现图形区域被清空了,但是并没有把我重绘的东西显示出来。

然后我把rapaint()去掉,在末尾用revalidate();

if (e.getSource() == laccount) {
   pcenter.removeAll();
pcenter.add(Account.setAccount());
pcenter.revalidate();

}

运行后确实我重绘的东西显示出来了,但是观察后发现原来的组件斌没有删除,而是被遮盖了,注意是遮盖不是覆盖,原来的组件仍然存在,只是点击按钮后,把新添加进去的组件显示出来了,但是点击多个按钮会发现当所有按钮里面的组件都被添加进去后再点击按钮,显示的组件是最后一个,也就是组件不再刷新显示了,这有点像一个数组,把所有的组件添加进去后相同的组件不再添加。

之后我再把repaint(),添加进去

if (e.getSource() == laccount) {
pcenter.removeAll();
pcenter.repaint();
pcenter.add(Account.setAccount());
pcenter.revalidate();

}

最终得到了想要的结果,组件被清空,并且完成了自己的重绘功能

对于这个问题我的理解是:先用removeAll()移除了所有组件,但是并未刷新,所以单使用这个方法虽然内部组件确实改变了,但是界面显示不变;repaint()相当于刷新了整个面板,所以用了remover All()之后面板是空的,如果不先使用removeAll()那么接下来添加的组件会接在之前存在的组件后面(因为之前的组件未移除);然后在添加新的组件后使用revalidate(),重新对面板计算大小并且对面板中的组件进行布局,这样就把新添加的组件添加进去了。对于revalidate() JDK里面是这样说的:使用 validate 方法会使容器再次布置其子组件。已经显示容器后,在修改此容器的子组件的时候(在容器中添加或移除组件,或者更改与布局相关的信息),应该调用上述方法。我觉得这里应该有一个刷新作用,添加了组件后必须刷新面板才能再次显示出来。

总结:先移除组件( removeAll() ),然后重绘面板( repaint() ),再添加组件( add() ),最后重新布局面板( revalidate() )。

以上内容是我百度后自己摸索出来的,欢迎大家批评指正!

以上内容我参考了下面的网站

http://stackoverflow.com/questions/1097366/java-swing-revalidate-vs-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刷新布局
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值