如何在组合框中显示没有扩展名的文件名?

时间:2017-03-19 02:01:15

标签: java swing jcombobox

我的用例如下:我想要一个组合框,它在每次打开时刷新的目录中显示已过滤的文件列表。我还希望组合框中显示的字符串是没有目录或扩展名的文件名(它们是我不想向用户公开的实现细节)。

我已经实施了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恢复正常。我错过了一种更简单的方法来实现这个目标吗?

2 个答案:

答案 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);
    }
}