2013-02-22 66 views
3

我遇到了一个前面已经讨论过的问题:获取包含JTableJScrollPane,以按我的意愿显示水平滚动条。 HERE是我试过的帖子,但由于某种原因,它似乎不适用于我的情况(我认为这是因为我已将其中一列设置为固定宽度)基于一列宽度的JTable水平滚动条

这是一个SSCCE:

import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.table.DefaultTableModel; 

@SuppressWarnings("serial") 
public class TableScrollTest extends JFrame 
{ 
    public TableScrollTest() { 

     DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"},0); 
     model.addRow(new Object[]{"short", "blah"}); 
     model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"}); 

     JTable table = new JTable(model) { 
      public boolean getScrollableTracksViewportWidth() { 
       return getPreferredSize().width < getParent().getWidth(); 
      } 
     };   
     table.getColumn("key").setPreferredWidth(60); 
     table.getColumn("key").setMinWidth(60); 
     table.getColumn("key").setMaxWidth(60); 
     table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 

     JScrollPane scrollPane = new JScrollPane(table); 
     getContentPane().add(scrollPane); 
    } 

    public static void main(String[] args) { 
     TableScrollTest frame = new TableScrollTest(); 
     frame.setDefaultCloseOperation(EXIT_ON_CLOSE); 
     frame.pack(); 
     frame.setSize(200, 200); 
     frame.setResizable(false); 
     frame.setVisible(true); 
    } 
} 

总之,我有两列的表,用于显示键/值对。保持所述表中的容器是固定宽度的,并且该表的第一列也是固定的宽度(因为我知道如何长的所有键名将是)第二列将包含不同字符串长度的值水平滚动条应该只出现在存在的值太长而不适合列分配的宽度时

enter image description here

由于上述第二值具有这样的长的长度,滚动条应该是可见的。然而,事实并非如此,而且我所尝试过的所有方法都只能成功地使其始终可见,这不是我想要的......我只希望它在“长”值存在的情况下可见。看起来好像getScrollableTracksViewportWidth()方法在表构造函数中被覆盖,检查表的首选宽度是什么......所以我需要引导表根据第二列的内容来选择更大的宽度......但是我很难过。

任何想法?

+1

你直接的问题是,因为你覆盖'getScrollableTracksViewportWidth'这意味着该表格永远不会比滚动窗格更宽... – MadProgrammer 2013-02-22 01:07:55

+0

@MadProgrammer你确定吗?我链接的示例中已覆盖该方法,并且在该示例中,表的确会比滚动窗格更宽。 – The111 2013-02-22 01:11:36

+1

但是表的preferredWidth是基于'TableColumn'的宽度,它没有考虑到内容的宽度,这意味着除非你改变第二列的宽度,就像改变第一列的宽度一样,将始终返回“true”。我删除了'getScrollableTracksViewportWidth'方法并手动调整了列的大小,并能够显示水平滚动条。 – MadProgrammer 2013-02-22 01:14:09

回答

6

这是一个hackey溶液

基本上,它的作用是根据从所有的行中的值的所有列的“优选的”宽度。

它考虑到对模型的更改以及父容器的更改。

一旦完成,它会检查“首选”宽度是大于还是小于可用空间,并相应地设置变量。

您可以添加固定列的检查(我没有打扰过),这会使进程“略快”一些,但是随着您向表中添加更多的列和行,这会受到影响,因为每次更新都是将需要整个模型的散步。

你可以把某种缓存,但说得对,那么,我会考虑固定宽度的列;)

import java.awt.Container; 
import java.awt.EventQueue; 
import java.awt.event.ComponentAdapter; 
import java.awt.event.ComponentEvent; 
import javax.swing.JFrame; 
import static javax.swing.JFrame.EXIT_ON_CLOSE; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.JViewport; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 
import javax.swing.event.ChangeEvent; 
import javax.swing.event.ListSelectionEvent; 
import javax.swing.event.TableColumnModelEvent; 
import javax.swing.event.TableColumnModelListener; 
import javax.swing.event.TableModelEvent; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableColumn; 
import javax.swing.table.TableColumnModel; 

public class TableScrollTest extends JFrame { 

    public TableScrollTest() { 

     DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"}, 0); 
     model.addRow(new Object[]{"short", "blah"}); 
     model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"}); 

     JTable table = new JTable(model) { 
      private boolean trackViewportWidth = false; 
      private boolean inited = false; 
      private boolean ignoreUpdates = false; 

      @Override 
      protected void initializeLocalVars() { 
       super.initializeLocalVars(); 
       inited = true; 
       updateColumnWidth(); 
      } 

      @Override 
      public void addNotify() { 
       super.addNotify(); 
       updateColumnWidth(); 
       getParent().addComponentListener(new ComponentAdapter() { 
        @Override 
        public void componentResized(ComponentEvent e) { 
         invalidate(); 
        } 
       }); 
      } 

      @Override 
      public void doLayout() { 
       super.doLayout(); 
       if (!ignoreUpdates) { 
        updateColumnWidth(); 
       } 
       ignoreUpdates = false; 
      } 

      protected void updateColumnWidth() { 
       if (getParent() != null) { 
        int width = 0; 
        for (int col = 0; col < getColumnCount(); col++) { 
         int colWidth = 0; 
         for (int row = 0; row < getRowCount(); row++) { 
          int prefWidth = getCellRenderer(row, col). 
            getTableCellRendererComponent(this, getValueAt(row, col), false, false, row, col). 
            getPreferredSize().width; 
          colWidth = Math.max(colWidth, prefWidth + getIntercellSpacing().width); 
         } 

         TableColumn tc = getColumnModel().getColumn(convertColumnIndexToModel(col)); 
         tc.setPreferredWidth(colWidth); 
         width += colWidth; 
        } 

        Container parent = getParent(); 
        if (parent instanceof JViewport) { 
         parent = parent.getParent(); 
        } 

        trackViewportWidth = width < parent.getWidth(); 
       } 
      } 

      @Override 
      public void tableChanged(TableModelEvent e) { 
       super.tableChanged(e); 
       if (inited) { 
        updateColumnWidth(); 
       } 
      } 

      public boolean getScrollableTracksViewportWidth() { 
       return trackViewportWidth; 
      } 

      @Override 
      protected TableColumnModel createDefaultColumnModel() { 
       TableColumnModel model = super.createDefaultColumnModel(); 
       model.addColumnModelListener(new TableColumnModelListener() { 
        @Override 
        public void columnAdded(TableColumnModelEvent e) { 
        } 

        @Override 
        public void columnRemoved(TableColumnModelEvent e) { 
        } 

        @Override 
        public void columnMoved(TableColumnModelEvent e) { 
         if (!ignoreUpdates) { 
          ignoreUpdates = true; 
          updateColumnWidth(); 
         } 
        } 

        @Override 
        public void columnMarginChanged(ChangeEvent e) { 
         if (!ignoreUpdates) { 
          ignoreUpdates = true; 
          updateColumnWidth(); 
         } 
        } 

        @Override 
        public void columnSelectionChanged(ListSelectionEvent e) { 
        } 
       }); 
       return model; 
      } 
     }; 
     table.getColumn("key").setPreferredWidth(60); 
//  table.getColumn("key").setMinWidth(60); 
//  table.getColumn("key").setMaxWidth(60); 
//  table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); 

     JScrollPane scrollPane = new JScrollPane(table); 
     getContentPane().add(scrollPane); 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
       } 

       TableScrollTest frame = new TableScrollTest(); 
       frame.setDefaultCloseOperation(EXIT_ON_CLOSE); 
       frame.pack(); 
       frame.setSize(200, 200); 
       frame.setResizable(true); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 
+0

谢谢!这与我上面提到的非常相似,但是我只是因为你的评论和@ kleopatra的帖子而与我联系。然而,在你的解决方案中有一个小错误...你的宽度累加了每一行的宽度值......所以你添加的行越多,你在每行末尾得到的“死”空间就越多。我想你想像我一样使用最大化功能。但是,我仍然需要在我的回答中添加我评论的1-5像素模糊因子。 Upvoting你的帮助。 :-) – The111 2013-02-22 01:40:34

+0

也会接受这个答案,因为覆盖到'addNotify','tableChanged'等是聪明的,并使其全部自动。我计划在每次更改表格时手动调用一些'update'方法,但将它包含在表格定义中就像你做的那样好得多。 – The111 2013-02-22 01:42:41

+1

@ The111固定错误 - 也加入了intercellSpacing因子。好地方! – MadProgrammer 2013-02-22 01:58:17

4

看来那时,我的目标是迫使单元格渲染自动使细胞适应长串

这不是渲染器的工作。您必须手动设置列的宽度。

请参阅Table Column Adjuster一个方法来做到这一点。

1

如果发布了更智能的东西,我不一定会接受这个答案,但这里是我自己根据迄今为止发布的评论和THIS发布的数据找出的解决方案。唯一的问题是,我想要的宽度总是约1像素太短(在一些外观和感觉是好的),所以我在末端width += 5附近添加了这条线,这似乎使它工作正常,但对我来说感觉很不好。欢迎对此方法发表任何意见。

import java.awt.Component; 

import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableCellRenderer; 

@SuppressWarnings("serial") 
public class TableScrollTest extends JFrame { 
    public TableScrollTest() { 
     DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"},0); 
     model.addRow(new Object[]{"short", "blah"}); 
     model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"}); 

     JTable table = new JTable(model);   
     table.getColumn("key").setPreferredWidth(60); 
     table.getColumn("key").setMinWidth(60); 
     table.getColumn("key").setMaxWidth(60); 
     table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 

     int width = 0; 
     for (int row = 0; row < table.getRowCount(); row++) { 
      TableCellRenderer renderer = table.getCellRenderer(row, 1); 
      Component comp = table.prepareRenderer(renderer, row, 1); 
      width = Math.max (comp.getPreferredSize().width, width); 
     } 
     width += 5; 
     table.getColumn("value").setPreferredWidth(width); 
     table.getColumn("value").setMinWidth(width); 
     table.getColumn("value").setMaxWidth(width); 

     JScrollPane scrollPane = new JScrollPane(table); 
     getContentPane().add(scrollPane); 
    } 

    public static void main(String[] args) { 
     TableScrollTest frame = new TableScrollTest(); 
     frame.setDefaultCloseOperation(EXIT_ON_CLOSE); 
     frame.pack(); 
     frame.setSize(200, 200); 
     frame.setResizable(false); 
     frame.setVisible(true); 
    } 
} 
+1

使用['JTable.getIntercellSpacing().width'](http://docs.oracle.com/javase/7/docs/api/javax/swing/JTable.html#getIntercellSpacing%28%29)而不是“fudging “像素填充 – MadProgrammer 2013-02-22 01:59:25

+0

@MadProgrammer酷,感谢您的提示和更新您的解决方案。 – The111 2013-02-22 02:41:11