如何在组合框中显示不带扩展名的文件名?
How to display the name of files without extensions in a combobox?
我的用例如下:我想要一个组合框,它在每次打开时刷新的目录中显示经过过滤的文件列表。我还希望组合框中显示的字符串是没有目录或扩展名的文件名(它们是我不想向用户公开的实现细节)。
我实现了一个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;
}
});
不幸的是,这不起作用。它正确显示截断的文件名,但现在使用 String
而不是 Path
调用 setSelectedItem
- 这是有道理的,但与模型中的路径不匹配。
我的选择似乎是:
- 编写我自己的自定义组件,复制
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
。我是否缺少实现此目的的更简单方法?
but now calls setSelectedItem with the String rather than the Path
渲染器与组合框模型的其他方法无关。
如果要将 Path
对象传递给 setSelectedItem(...)
方法,则需要确保 Path
对象实现了 equals(...)
方法,以便模型可以 select 适当的 Path
.
然后只需创建自定义渲染器即可显示要显示的 Path
对象的任何部分。
it'd be good to get a Path back.
如果您的模型包含 Path 对象,那么 getSelectedItem()
方法将 return 一个 Path 对象。您需要将其转换为路径,或使用您的 getSelectedPath()
方法。
所以你不需要覆盖DefaultComboBoxModel的任何方法,只添加getSelectedPath()
方法。
为了清楚起见,如果另一个 reader 发现了这个问题,这里是基于@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);
}
}
我的用例如下:我想要一个组合框,它在每次打开时刷新的目录中显示经过过滤的文件列表。我还希望组合框中显示的字符串是没有目录或扩展名的文件名(它们是我不想向用户公开的实现细节)。
我实现了一个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;
}
});
不幸的是,这不起作用。它正确显示截断的文件名,但现在使用 String
而不是 Path
调用 setSelectedItem
- 这是有道理的,但与模型中的路径不匹配。
我的选择似乎是:
- 编写我自己的自定义组件,复制
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
。我是否缺少实现此目的的更简单方法?
but now calls setSelectedItem with the String rather than the Path
渲染器与组合框模型的其他方法无关。
如果要将 Path
对象传递给 setSelectedItem(...)
方法,则需要确保 Path
对象实现了 equals(...)
方法,以便模型可以 select 适当的 Path
.
然后只需创建自定义渲染器即可显示要显示的 Path
对象的任何部分。
it'd be good to get a Path back.
如果您的模型包含 Path 对象,那么 getSelectedItem()
方法将 return 一个 Path 对象。您需要将其转换为路径,或使用您的 getSelectedPath()
方法。
所以你不需要覆盖DefaultComboBoxModel的任何方法,只添加getSelectedPath()
方法。
为了清楚起见,如果另一个 reader 发现了这个问题,这里是基于@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);
}
}