Java Swing JList自定义JPanel渲染器内容重复显示问题咨询
问题根本原因
JList的自定义渲染器采用单实例复用机制:整个JList只会创建1个你定义的渲染器实例,所有行渲染时都会复用这同一个实例的getListCellRendererComponent方法。你在方法内每次调用都新增控件、不清除上一行添加的控件,调用多少次就累加多少个控件,自然会出现所有行的元素叠加、重复显示的问题。同时你每次渲染都重复读取图片、创建新控件,也会造成严重的性能浪费。
可行解决方案
渲染器的控件应该在构造方法中一次性创建完成并设置好布局,getListCellRendererComponent方法内只更新控件的内容,不要新增/删除控件。
修改后的CountryRenderer代码(带显示国家名的按钮)
import java.awt.*; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.*; public class CountryRenderer extends JPanel implements ListCellRenderer<Country> { // 提前声明所有控件 private final JButton iconBtn; private final JTextField nameTxt; public CountryRenderer() { // 提前设置布局 setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2)); // 提前创建控件 iconBtn = new JButton(); iconBtn.setPreferredSize(new Dimension(80,25)); nameTxt = new JTextField(); nameTxt.setPreferredSize(new Dimension(80,25)); // 提前添加到面板,只加一次 add(iconBtn); add(nameTxt); } @Override public Component getListCellRendererComponent(JList<? extends Country> list, Country country, int index, boolean isSelected, boolean cellHasFocus) { // 仅更新控件内容,不新增控件 String code = country.getCode(); Image scaledImg = null; try { Image rawImg = ImageIO.read(getClass().getResource("./images/" + code + "1.png")); scaledImg = rawImg.getScaledInstance(25, 25, Image.SCALE_SMOOTH); } catch (IOException e) { e.printStackTrace(); } iconBtn.setIcon(scaledImg != null ? new ImageIcon(scaledImg) : null); // 更新按钮显示国家名 iconBtn.setText(country.getName()); nameTxt.setText(country.getName()); // 设置选中状态样式 if (isSelected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } return this; } }
注意事项
如果需要实现按钮的点击交互,因为渲染器只是绘制控件的快照,本身不响应事件,需要额外给JList添加MouseListener,通过点击坐标判断点击的行和按钮区域,再处理对应逻辑。
内容的提问来源于stack exchange,提问作者Madar
相关产品推荐
相关产品推荐

