2016-06-17 5 views
0

JTableを次の文字の出現を示す別々の行でアルファベット順に並べ替える方法は?例えば、それは追跡方法のWindows Media Playerの12種類、JTableをアルファベット順にソートする方法は? [画像を参照してください]

Windows Media Player 12, example

を検討し、この問題のもう一つの例は、例えばどのようにMacintoshの「Finderの」並べ替え日付別最近オープニングアプリケーションです。私は彼らが日付とマッチングを囲む線を示す追加行をどのように追加したか把握できません。

どのようにこの問題にアプローチするのか?

Macintosh, Finder, example

+0

J(X)TreeTable、おそらく非フリーJideには類似したものがあります – mKorbel

+0

J(X)TreeTableとは何ですか? IDEとサードパーティ以外のソースのみを使用して、望ましい外観の実装がアプローチするのは時間がかかりますか? – blueFalcon

+0

[J(X)TreeTable](http://stackoverflow.com/a/7123228/714968)、私はMediaPalyerもTreeTableとしてコード化されていると思います – mKorbel

答えて

1

は、テーブルの行がJTableは通常、彼らがあることを期待場所以外の場所に表示されているために、あなたがオーバーライドする必要がありますJTableの方法はrowAtPointgetCellRect、そしてもちろん、paintComponentです。これらのメソッドをサポートするために、ソートされたMap内でセクションの見出しが出現する行を追跡し、2番目のソートされたMapで行の位置を追跡します。

テーブルモデルまたはソート列が変更されるたびに、またはテーブルが何らかの理由でそれ自身を検証するたびに、これらのマップを再計算します。つまり、それぞれtableChanged、sortedChangedおよびvalidateメソッドをオーバーライドします。

結果は、あなたが期待するかもしれないほど長いではありません:あなたは、見出しは、データから導出される方法を変更することで、Finderのテーブルを作るために、このクラスを適応させることができ

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

。 getValueAtで1つのセルを調べるだけでは不十分です。代わりにtranslate the sorted row to the corresponding model rowが必要で、TableModelからその行のデータオブジェクトを直接取得します。次に、文字列データではなく、年齢によって行を比較することができます。

+0

すばらしい解決策!しかし、私はSectionTableを使用してテーブルを下にスクロールすることはできません。また、DefaultTableModel.setDataVector(Object [] [] dataVector、Object [] columnIdentifiers)メソッドを呼び出すことによって私のテーブルモデルが変更された場合、テーブルは元のすべてのセクションを失います。どのように私はこれを修正することができますの任意のアイデア? – blueFalcon

+0

setDataVectorを呼び出した後でもテーブルにソート列が表示されていますか? – VGR

+0

いいえ、それはソートされていない元の順序に戻り、すべてのセクション(行と文字)はなくなりました。再度並べ替えるには、テーブルヘッダーを手動でクリックする必要があります。またスクロールダウンすると、次の文字で停止し、さらに進まなくなります。スクロールアップはうまく動作します。 – blueFalcon

1

私はグループ私のデータは、アルファベット順にして、自分のデータに含まれる各文字を別のテーブルを作成します。

TableCellRendererを使用してヘッダーの表示をカスタマイズして、目的の外観を得ることができます。例に示すように列を追加する場合は、ヘッダーを別のコンポーネントに置き換えることができます。

public class MyTableFrame extends JFrame { 

    MyTableFrame() { 

     List<String> data = Arrays.asList(
      "Alpha1", 
      "Beta1", 
      "Alpha2", 
      "Charlie2", 
      "Alpha3", 
      "Beta2", 
      "Charlie1" 
     ); 
     // Sort alphabetically 
     data.sort(String::compareTo); 

     // Group by first letter 
     Map<Character, List<String>> collect = data.stream() 
       .collect(Collectors.groupingBy(string -> string.charAt(0))); 

     JPanel tablesContainer = new JPanel(new GridBagLayout()); 
     GridBagConstraints c = new GridBagConstraints(); 
     c.fill = GridBagConstraints.BOTH; 
     c.weightx = 1; 
     c.weighty = 0; 
     c.gridy = 0; 

     // Create a table for each Letter 
     collect.entrySet().forEach(mapEntry -> { 
      Character letter = mapEntry.getKey(); 
      List<String> startingWithLetter = mapEntry.getValue(); 
      TableModel tableModel = new TableModel(startingWithLetter, letter); 
      JTable table = new JTable(tableModel); 
      // Table header must be added separately since there is no scroll pane 
      JTableHeader tableHeader = table.getTableHeader(); 
      tablesContainer.add(tableHeader, c); 

      c.gridy++; 
      tablesContainer.add(table, c); 

      c.gridy++; 
     }); 

     add(tablesContainer); 
     setVisible(true); 
     pack(); 
    } 

    static class TableModel extends AbstractTableModel { 
     private List<String> data; 
     private Character columnName; 

     TableModel(List<String> data, Character columnName) { 
      this.data = data; 
      this.columnName = columnName; 
     } 

     @Override 
     public String getColumnName(int column) { 
      return columnName.toString(); 
     } 

     @Override 
     public int getRowCount() { 
      return data.size(); 
     } 

     @Override 
     public int getColumnCount() { 
      return 1; 
     } 

     @Override 
     public Object getValueAt(int rowIndex, int columnIndex) { 
      return data.get(rowIndex); 
     } 
    } 

    public static void main(String args[]) { 
     new MyTableFrame(); 
    } 
} 
関連する問題