Search code examples
javaswingjtablerenderer

How can I redefine VerticalTableHeaderCellRenderer() for javax.swing.JTable(), to write text vertically


The goal to write the values of columns into JTable vertically. I read about DefaultTableCellRenderer, VerticalTableHeaderCellRenderer() but cannot implement it. Here is my code:

//=========================================================
    private JScrollPane getTablePane() {
        if (tablePane == null) {
            tablePane = new JScrollPane();
            tablePane.setRowHeaderView(getTableDictionary());
            tablePane.setViewportView(getTableDictionary());
        }
        return tablePane;
    }

//=============================================================

private JTable getTableDictionary(){
    if (table == null) {

            rowVector = new String[colCount];
                for(int i=0; i<colCount;i++) {rowVector[i]="";}
            data = new DefaultTableModel(rowVector, 0);
                for (int i = 0; i < rowCount; i++) { data.addRow(rowVector); }

            table = new JTable(data);
            table.getTableHeader().setDefaultRenderer(new VerticalTableHeaderCellRenderer());
                for(int i=1; i<colCount; i++)  { table.getColumnModel().getColumn(i).setPreferredWidth(withCol);}

                table.setSelectionForeground(Color.BLACK);
                table.setSelectionBackground(Color.YELLOW);
            table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            table.setAutoscrolls(true);
            table.setColumnSelectionAllowed(false);
            table.setRowSelectionAllowed(true);

        /*    DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer();
            int bg = table.getTableHeader().getBackground().getRGB();
            defaultTableCellRenderer.setBackground(Color.getHSBColor(125, 125, 125)); //задаем цвет столбца
            table.getColumnModel().getColumn(0).setCellRenderer(defaultTableCellRenderer);
        */    


        return table;
    }

Where is my mistake? Thank you


Solution

  • I read more resources and I found a decision, it works. I need to implement a render for that and call it when I am locating the table on the JScrollPane. It means that column header is processing apart from a JTable using datamodel. Also, I can define the height of the columns.

    private JScrollPane getTablePane() {
        if (tablePane == null) {
            tablePane = new JScrollPane();
            tablePane.setRowHeaderView(getTableDictionary());
            tablePane.setViewportView(getTableDictionary());
            tablePane.setColumnHeader(new JViewport() {
            @Override public Dimension getPreferredSize() {
                Dimension d = super.getPreferredSize();
                d.height = rowColumnHeigth;  // Col header Height
                return d;
                }
            });
        }
        return tablePane;
    }
    

    Also, I need for that additional classes:

    /**
     * @(#)DefaultTableHeaderCellRenderer.java  1.0 02/24/09
     */
    
    import java.awt.Component;
    import java.util.List;
    import javax.swing.Icon;
    import javax.swing.JTable;
    import javax.swing.RowSorter;
    import javax.swing.RowSorter.SortKey;
    import javax.swing.UIManager;
    import javax.swing.table.DefaultTableCellRenderer;
    import javax.swing.table.JTableHeader;
    
    /**
     * A default cell renderer for a JTableHeader.
     * <P>
     * DefaultTableHeaderCellRenderer attempts to provide identical behavior to the
     * renderer which the Swing subsystem uses by default, the Sun proprietary
     * class sun.swing.table.DefaultTableCellHeaderRenderer.
     * <P>
     * To apply any desired customization, DefaultTableHeaderCellRenderer may be
     * suitably extended.
     * 
     * @author Darryl
     */
    public class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer {
    
      /**
       * Constructs a <code>DefaultTableHeaderCellRenderer</code>.
       * <P>
       * The horizontal alignment and text position are set as appropriate to a
       * table header cell, and the opaque property is set to false.
       */
      public DefaultTableHeaderCellRenderer() {
        setHorizontalAlignment(CENTER);
        setHorizontalTextPosition(LEFT);
        setVerticalAlignment(BOTTOM);
        setOpaque(false);
      }
    
      /**
       * Returns the default table header cell renderer.
       * <P>
       * If the column is sorted, the approapriate icon is retrieved from the
       * current Look and Feel, and a border appropriate to a table header cell
       * is applied.
       * <P>
       * Subclasses may overide this method to provide custom content or
       * formatting.
       *
       * @param table the <code>JTable</code>.
       * @param value the value to assign to the header cell
       * @param isSelected This parameter is ignored.
       * @param hasFocus This parameter is ignored.
       * @param row This parameter is ignored.
       * @param column the column of the header cell to render
       * @return the default table header cell renderer
       */
      @Override
      public Component getTableCellRendererComponent(JTable table, Object value,
              boolean isSelected, boolean hasFocus, int row, int column) {
        super.getTableCellRendererComponent(table, value,
                isSelected, hasFocus, row, column);
        JTableHeader tableHeader = table.getTableHeader();
        if (tableHeader != null) {
          setForeground(tableHeader.getForeground());
        }
        setIcon(getIcon(table, column));
        setBorder(UIManager.getBorder("TableHeader.cellBorder"));
        return this;
      }
    
      /**
       * Overloaded to return an icon suitable to the primary sorted column, or null if
       * the column is not the primary sort key.
       *
       * @param table the <code>JTable</code>.
       * @param column the column index.
       * @return the sort icon, or null if the column is unsorted.
       */
      protected Icon getIcon(JTable table, int column) {
        SortKey sortKey = getSortKey(table, column);
        if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
          switch (sortKey.getSortOrder()) {
            case ASCENDING:
              return UIManager.getIcon("Table.ascendingSortIcon");
            case DESCENDING:
              return UIManager.getIcon("Table.descendingSortIcon");
          }
        }
        return null;
      }
    
      /**
       * Returns the current sort key, or null if the column is unsorted.
       *
       * @param table the table
       * @param column the column index
       * @return the SortKey, or null if the column is unsorted
       */
      protected SortKey getSortKey(JTable table, int column) {
        RowSorter rowSorter = table.getRowSorter();
        if (rowSorter == null) {
          return null;
        }
    
        List sortedColumns = rowSorter.getSortKeys();
        if (sortedColumns.size() > 0) {
          return (SortKey) sortedColumns.get(0);
        }
        return null;
      }
    }
    

    Also:

    /**
     * @(#)VerticalLabelUI.java 1.0 02/18/09
     */
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Rectangle;
    import javax.swing.Icon;
    import javax.swing.JComponent;
    import javax.swing.JLabel;
    import javax.swing.SwingUtilities;
    import javax.swing.plaf.ComponentUI;
    import javax.swing.plaf.basic.BasicLabelUI;
    
    /**
     * A UI delegate for JLabel that rotates the label 90є
     * <P>
     * Extends {@link BasicLabelUI}.
     * <P>
     * The only difference between the appearance of labels in the Basic and Metal
     * L&Fs is the manner in which diabled text is painted.  As VerticalLabelUI
     * does not override the method paintDisabledText, this class can be adapted
     * for Metal L&F by extending MetalLabelUI instead of BasicLabelUI.
     * <P>
     * No other changes are required.
     * 
     * @author Darryl
     */
    public class VerticalLabelUI extends BasicLabelUI {
    
       private boolean clockwise = false;
       // see comment in BasicLabelUI
       Rectangle verticalViewR = new Rectangle();
       Rectangle verticalIconR = new Rectangle();
       Rectangle verticalTextR = new Rectangle();
       protected static VerticalLabelUI verticalLabelUI =
             new VerticalLabelUI();
       private final static VerticalLabelUI SAFE_VERTICAL_LABEL_UI =
             new VerticalLabelUI();
    
       /**
        * Constructs a <code>VerticalLabelUI</code> with the default anticlockwise
        * rotation
        */
       public VerticalLabelUI() {
       }
    
       /**
        * Constructs a <code>VerticalLabelUI</code> with the desired rotation.
        * <P>
        * @param clockwise true to rotate clockwise, false for anticlockwise
        */
       public VerticalLabelUI(boolean clockwise) {
          this.clockwise = clockwise;
       }
    
       /**
        * @see ComponentUI#createUI(javax.swing.JComponent) 
        */
       public static ComponentUI createUI(JComponent c) {
          if (System.getSecurityManager() != null) {
             return SAFE_VERTICAL_LABEL_UI;
          } else {
             return verticalLabelUI;
          }
       }
    
       /**
        * Overridden to always return -1, since a vertical label does not have a
        * meaningful baseline.
        * 
        * @see ComponentUI#getBaseline(JComponent, int, int)
        */
       @Override
       public int getBaseline(JComponent c, int width, int height) {
          super.getBaseline(c, width, height);
          return -1;
       }
    
       /**
        * Overridden to always return Component.BaselineResizeBehavior.OTHER,
        * since a vertical label does not have a meaningful baseline 
        * 
        * @see ComponentUI#getBaselineResizeBehavior(javax.swing.JComponent)
        */
       @Override
       public Component.BaselineResizeBehavior getBaselineResizeBehavior(
             JComponent c) {
          super.getBaselineResizeBehavior(c);
          return Component.BaselineResizeBehavior.OTHER;
       }
    
       /**
        * Transposes the view rectangles as appropriate for a vertical view
        * before invoking the super method and copies them after they have been
        * altered by {@link SwingUtilities#layoutCompoundLabel(FontMetrics, String,
        * Icon, int, int, int, int, Rectangle, Rectangle, Rectangle, int)}
        */
       @Override
       protected String layoutCL(JLabel label, FontMetrics fontMetrics,
             String text, Icon icon, Rectangle viewR, Rectangle iconR,
             Rectangle textR) {
    
          verticalViewR = transposeRectangle(viewR, verticalViewR);
          verticalIconR = transposeRectangle(iconR, verticalIconR);
          verticalTextR = transposeRectangle(textR, verticalTextR);
    
          text = super.layoutCL(label, fontMetrics, text, icon,
                verticalViewR, verticalIconR, verticalTextR);
    
          viewR = copyRectangle(verticalViewR, viewR);
          iconR = copyRectangle(verticalIconR, iconR);
          textR = copyRectangle(verticalTextR, textR);
          return text;
       }
    
       /**
        * Transforms the Graphics for vertical rendering and invokes the
        * super method.
        */
       @Override
       public void paint(Graphics g, JComponent c) {
          Graphics2D g2 = (Graphics2D) g.create();
          if (clockwise) {
             g2.rotate(Math.PI / 2, c.getSize().width / 2, c.getSize().width / 2);
          } else {
             g2.rotate(-Math.PI / 2, c.getSize().height / 2, c.getSize().height / 2);
          }
          super.paint(g2, c);
       }
    
       /**
        * Returns a Dimension appropriate for vertical rendering
        * 
        * @see ComponentUI#getPreferredSize(javax.swing.JComponent)
        */
       @Override
       public Dimension getPreferredSize(JComponent c) {
          return transposeDimension(super.getPreferredSize(c));
       }
    
       /**
        * Returns a Dimension appropriate for vertical rendering
        * 
        * @see ComponentUI#getMaximumSize(javax.swing.JComponent)
        */
       @Override
       public Dimension getMaximumSize(JComponent c) {
          return transposeDimension(super.getMaximumSize(c));
       }
    
       /**
        * Returns a Dimension appropriate for vertical rendering
        * 
        * @see ComponentUI#getMinimumSize(javax.swing.JComponent)
        */
       @Override
       public Dimension getMinimumSize(JComponent c) {
          return transposeDimension(super.getMinimumSize(c));
       }
    
       private Dimension transposeDimension(Dimension from) {
          return new Dimension(from.height, from.width + 2);
       }
    
       private Rectangle transposeRectangle(Rectangle from, Rectangle to) {
          if (to == null) {
             to = new Rectangle();
          }
          to.x = from.y;
          to.y = from.x;
          to.width = from.height;
          to.height = from.width;
          return to;
       }
    
       private Rectangle copyRectangle(Rectangle from, Rectangle to) {
          if (to == null) {
             to = new Rectangle();
          }
          to.x = from.x;
          to.y = from.y;
          to.width = from.width;
          to.height = from.height;
          return to;
       }
    }
    

    Also:

    import java.awt.Component;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import javax.swing.Icon;
    import javax.swing.JTable;
    import javax.swing.RowSorter.SortKey;
    import javax.swing.SortOrder;
    import javax.swing.UIManager;
    
    /**
     * A renderer for a JTableHeader with text rotated 90В° counterclockwise.
     * <P>
     * Extends {@link DefaultTableHeaderCellRenderer}.
     * 
     * @see VerticalLabelUI
     * @author Darryl
     */
    public class VerticalTableHeaderCellRenderer
            extends DefaultTableHeaderCellRenderer {
    
      /**
       * Constructs a <code>VerticalTableHeaderCellRenderer</code>.
       * <P>
       * The horizontal and vertical alignments and text positions are set as
       * appropriate to a vertical table header cell.
       */
      public VerticalTableHeaderCellRenderer() {
        setHorizontalAlignment(LEFT);
        setHorizontalTextPosition(CENTER);
        setVerticalAlignment(CENTER);
        setVerticalTextPosition(TOP);
        setUI(new VerticalLabelUI());
      }
    
      /**
       * Overridden to return a rotated version of the sort icon.
       *
       * @param table the <code>JTable</code>.
       * @param column the colummn index.
       * @return the sort icon, or null if the column is unsorted.
       */
      @Override
      protected Icon getIcon(JTable table, int column) {
        SortKey sortKey = getSortKey(table, column);
        if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) {
          SortOrder sortOrder = sortKey.getSortOrder();
          switch (sortOrder) {
            case ASCENDING:
              return VerticalSortIcon.ASCENDING;
            case DESCENDING:
              return VerticalSortIcon.DESCENDING;
          }
        }
        return null;
      }
    
      /**
       * An icon implementation to paint the contained icon rotated 90В° clockwise.
       * <P>
       * This implementation assumes that the L&F provides ascending and
       * descending sort icons of identical size.
       */
      private enum VerticalSortIcon implements Icon {
    
        ASCENDING(UIManager.getIcon("Table.ascendingSortIcon")),
        DESCENDING(UIManager.getIcon("Table.descendingSortIcon"));
        private final Icon icon;// = ;
    
        private VerticalSortIcon(Icon icon) {
          this.icon = icon;
        }
    
        /**
         * Paints an icon suitable for the header of a sorted table column,
         * rotated by 90В° clockwise.  This rotation is applied to compensate
         * the rotation already applied to the passed in Graphics reference
         * by the VerticalLabelUI.
         * <P>
         * The icon is retrieved from the UIManager to obtain an icon
         * appropriate to the L&F.
         *
         * @param c the component to which the icon is to be rendered
         * @param g the graphics context
         * @param x the X coordinate of the icon's top-left corner
         * @param y the Y coordinate of the icon's top-left corner
         */
        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
          int maxSide = Math.max(getIconWidth(), getIconHeight()+(Integer)getIconHeight()/4);
          Graphics2D g2 = (Graphics2D) g.create(x, y, maxSide, maxSide);
          g2.rotate((Math.PI / 2));
          g2.translate(0, -maxSide);
          icon.paintIcon(c, g2, 0, 0);
          g2.dispose();
        }
    
        /**
         * Returns the width of the rotated icon.
         *
         * @return the <B>height</B> of the contained icon
         */
        @Override
        public int getIconWidth() {
          return icon.getIconHeight();
        }
    
        /**
         * Returns the height of the rotated icon.
         *
         * @return the <B>width</B> of the contained icon
         */
        @Override
        public int getIconHeight() {
          return icon.getIconWidth();
        }
      }
    }
    

    It works fine for me. Thank you! Also, it looks like:

    enter image description here