如何对按字母顺序分隔的JTable排序? [请参阅图片以进行澄清] [英] How to sort JTable alphabetically separated by first letter? [see picture for clarification]

查看:70
本文介绍了如何对按字母顺序分隔的JTable排序? [请参阅图片以进行澄清]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何按字母顺序对JTable进行排序,并用分隔线显示下一个字母的出现?例如,考虑一下Windows Media Player 12如何对其进行排序,

How to sort JTable alphabetically with separated lines showing the occurrence of the next letter? For example, consider how Windows Media Player 12 sorts it tracks,

此问题的另一个示例是例如Macintosh'Finder'如何按日期对最近打开的应用程序进行排序.我不知道他们是如何添加额外的行来显示日期和包含匹配项的行的.

Another example of this problem is for instance how Macintosh 'Finder' sorts recent opening applications by date. I can't figure out how they added additional row showing the date and lines enclosing the matching.

关于如何解决此问题的任何想法?

Any ideas of how to approach this problem?

Macintosh,Finder,示例

推荐答案

要使表行出现在JTable通常期望的位置以外的其他地方,您需要重写的JTable中的方法是rowAtPointgetCellRect,当然还有paintComponent.为了支持这些方法,我将跟踪排序的Map中节标题所在的行,并跟踪第二个排序的Map中行的位置.

To have the table rows appear in a place other than where JTable normally expects them to be, the methods in JTable you’ll need to override are rowAtPoint, getCellRect, and of course, paintComponent. To support those methods, I would keep track of the rows where section headings occur in a sorted Map, and I would keep track of the row positions in a second sorted Map.

然后,只要表模型或排序列发生更改,或者表出于任何原因对其进行自我验证(即分别覆盖tableChanged,sortedChanged和validate方法),我都会重新计算这些Maps.

I would then recompute those Maps whenever the table model or sort column changes, or whenever the table validates itself for any reason, which means override the tableChanged, sortedChanged, and validate methods, respectively.

结果不像您预期​​的那么冗长:

The result isn’t as lengthy as you might expect:

public class SectionedTable
extends JTable {
    private static final long serialVersionUID = 1;

    private final NavigableMap<Integer, String> sectionHeadings =
                                                        new TreeMap<>();

    private final NavigableMap<Integer, Integer> rowTopEdges =
                                                        new TreeMap<>();

    // Used when calling SwingUtilities.layoutCompoundLabel.
    private final Rectangle iconBounds = new Rectangle();
    private final Rectangle textBounds = new Rectangle();

    public SectionedTable() {
        init();
    }

    public SectionedTable(TableModel model) {
        super(model);
        init();
    }

    private void init()
    {
        setShowGrid(false);
        setAutoCreateRowSorter(true);

        recomputeSections();
        recomputeRowPositions();
    }

    private void recomputeSections() {
        if (sectionHeadings == null) {
            return;
        }

        sectionHeadings.clear();

        RowSorter<? extends TableModel> sorter = getRowSorter();
        if (sorter == null) {
            return;
        }

        for (RowSorter.SortKey key : sorter.getSortKeys()) {
            SortOrder order = key.getSortOrder();
            if (order != SortOrder.UNSORTED) {
                int sortColumn = key.getColumn();

                String lastSectionStart = "";
                int rowCount = getRowCount();
                for (int row = 0; row < rowCount; row++) {
                    Object value = getValueAt(row, sortColumn);
                    if (value == null) {
                        value = "?";
                    }

                    String s = value.toString();
                    if (s.isEmpty()) {
                        s = "?";
                    }

                    String sectionStart = s.substring(0,
                        s.offsetByCodePoints(0, 1));
                    sectionStart = sectionStart.toUpperCase();

                    if (!sectionStart.equals(lastSectionStart)) {
                        sectionHeadings.put(row, sectionStart);
                        lastSectionStart = sectionStart;
                    }
                }
                break;
            }
        }
    }

    private void recomputeRowPositions() {
        if (rowTopEdges == null) {
            return;
        }

        rowTopEdges.clear();

        int y = getInsets().top;
        int rowCount = getRowCount();
        int rowHeight = getRowHeight();
        for (int row = 0; row < rowCount; row++) {
            rowTopEdges.put(y, row);
            y += getRowHeight(row);
            if (sectionHeadings.containsKey(row)) {
                y += rowHeight;
            }
        }
    }

    @Override
    public void tableChanged(TableModelEvent event) {
        recomputeSections();
        recomputeRowPositions();
        super.tableChanged(event);
    }

    @Override
    public void sorterChanged(RowSorterEvent event) {
        recomputeSections();
        recomputeRowPositions();
        super.sorterChanged(event);
    }

    @Override
    public void validate() {
        super.validate();
        recomputeRowPositions();
    }

    @Override
    public int rowAtPoint(Point location) {
        Map.Entry<Integer, Integer> entry = rowTopEdges.floorEntry(location.y);
        if (entry != null) {
            int row = entry.getValue();
            return row;
        }
        return -1;
    }

    @Override
    public Rectangle getCellRect(int row,
                                 int column,
                                 boolean includeSpacing) {

        Rectangle rect = super.getCellRect(row, column, includeSpacing);

        int sectionHeadingsAbove = sectionHeadings.headMap(row, true).size();
        rect.y += sectionHeadingsAbove * getRowHeight();

        return rect;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        boolean ltr = getComponentOrientation().isLeftToRight();

        int rowHeight = getRowHeight();
        FontMetrics metrics = g.getFontMetrics();
        int ascent = metrics.getAscent();
        for (Map.Entry<Integer, String> entry : sectionHeadings.entrySet()) {
            int row = entry.getKey();
            String heading = entry.getValue();

            Rectangle bounds = getCellRect(row, 0, true);
            bounds.y -= rowHeight;
            bounds.width = getWidth();
            bounds.grow(-6, 0);

            iconBounds.setBounds(0, 0, 0, 0);
            textBounds.setBounds(0, 0, 0, 0);
            String text = SwingUtilities.layoutCompoundLabel(this,
                metrics, heading, null,
                SwingConstants.CENTER, SwingConstants.LEADING,
                SwingConstants.CENTER, SwingConstants.CENTER,
                bounds, iconBounds, textBounds, 0);

            g.drawString(text, textBounds.x, textBounds.y + ascent);

            int lineY = textBounds.y + ascent / 2;
            if (ltr) {
                g.drawLine(textBounds.x + textBounds.width + 12, lineY,
                                  getWidth() - getInsets().right - 12, lineY);
            } else {
                g.drawLine(textBounds.x - 12, lineY,
                                  getInsets().left + 12, lineY);
            }
        }
    }
}

您可以通过更改标题从数据派生的方式来使此类适合于创建Finder表.仅用getValueAt检查一个单元格是不够的;相反,您需要翻译排序后的行到相应的模型行,然后直接从TableModel获取该行的数据对象.然后,您可以按年龄而不是按字符串数据比较行.

You can adapt this class to make a Finder table, by changing how the headings are derived from the data. Merely examining one cell with getValueAt won’t be sufficient; instead, you’ll need to translate the sorted row to the corresponding model row, and obtain the data object for that row directly from the TableModel. Then you can compare the rows by age rather than by string data.

这篇关于如何对按字母顺序分隔的JTable排序? [请参阅图片以进行澄清]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆