我的用例如下:我想要一个组合框,它在每次打开时刷新的目录中显示已过滤的文件列表。我还希望组合框中显示的字符串是没有目录或扩展名的文件名(它们是我不想向用户公开的实现细节)。
我已经实施了ComboBoxModel
,如下所示:
public class XMLComboModel extends DefaultComboBoxModel<Path> implements PopupMenuListener {
public static final String EXT = ".xml";
private final Path directory;
public XMLComboModel(String pathName) {
this.directory = Paths.get(pathName);
populateModel();
}
public Path getSelectedPath() {
return (Path) getSelectedItem();
}
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
populateModel();
}
private void populateModel() {
removeAllElements();
try {
Files.newDirectoryStream(directory, "*" + EXT).forEach(this::addElement);
if (getSelectedPath() != null) {
for (int i = 0; i < getSize(); i++) {
if (getElementAt(i).compareTo(getSelectedPath()) == 0)
setSelectedItem(getElementAt(i));
}
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
为了清楚起见,我遗漏了PopupMenuListener
中的一些方法。
这样可以正常工作,除了它显示组合框中的完整路径,这不是我想要的。我认为定制路径显示的最简单方法是自定义渲染器。我尝试了这个:
comboBox.setRenderer(new BasicComboBoxRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
Path path = (Path) value;
String fileName = path.getFileName().toString().replace(EXT, "");
Component component = super.getListCellRendererComponent(list, fileName, index,
isSelected, cellHasFocus);
return component;
}
});
不幸的是,这不起作用。它正确显示截断的文件名,但现在使用setSelectedItem
调用String
而不是Path
- 这有意义,但与模型中的路径不匹配。
我的选择似乎是:
BasicComboBoxRenderer
中的功能,但标签中的文字除外。setSelectedItem
以查找具有给定文件名的路径。其中第二个更直接实施:
@Override
public void setSelectedItem(Object item) {
for (int i = 0; i < getSize(); i++) {
if (super.getElementAt(i).equals(Paths.get(item.toString()))) {
super.setSelectedItem(super.getElementAt(i));
return;
}
}
}
但是这看起来非常笨重,因为我已经从模型中传递了Path
,这样可以让Path
恢复正常。我错过了一种更简单的方法来实现这个目标吗?
答案 0 :(得分:2)
但现在使用String而不是Path
调用setSelectedItem
渲染器与组合框模型的其他方法无关。
如果要将Path
对象传递给setSelectedItem(...)
方法,则需要确保Path
对象实现equals(...)
方法,以便模型可以选择正确的Path
。
然后只需创建自定义渲染器即可显示要显示的Path
对象的任何部分。
回到路径会很好。
如果模型包含Path对象,则getSelectedItem()
方法将返回Path对象。您需要将其强制转换为路径,或使用getSelectedPath()
方法。
因此,您不需要覆盖DefaultComboBoxModel的任何方法,只需添加getSelectedPath()
方法。
答案 1 :(得分:0)
为了清楚起见,如果另一位读者发现了这个问题,这里是基于@ camickr答案的完整工作代码。注意我已经使用现有的渲染器来创建新的渲染器以保持外观一致。
private JComboBox createDirectoryCombo(Path directory) {
ComboBoxModel<Path> model = new DefaultComboBoxModel<>();
populateModel(model);
JComboBox<Path> comboBox = new JComboBox(model);
comboBox.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
populateModel(model);
}
// Other methods omitted for clarity
});
ListCellRenderer renderer = comboBox.getRenderer();
comboBox.setRenderer((list, path, index, selected, focus) -> {
String fileName = path == null ? "" : path.getFileName().toString().replace(EXT, "");
return renderer.getListCellRendererComponent(list, fileName, index, selected, focus);
});
}
private void populateModel(ComboBoxModel<Path> model) {
try {
Path previousSelection = getSelectedPath();
removeAllElements();
for (Path path : Files.newDirectoryStream(directory, "*" + EXT)) {
addElement(path);
if (path.equals(previousSelection))
setSelectedItem(path);
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}