Как добавить всплывающую подсказку, сгенерированную из заголовка таблицы, в мои ячейки JTable?

У меня есть JTable, и я пытаюсь добавить подсказку заголовка, которая будет отображать строку, которая находится в заголовке таблицы для каждой из ячеек заголовка. Я могу установить значение ячейки заголовка:

colModel.getColumn(currentColumn).setHeaderValue(time + "(s)");

где colModel:

TableColumnModel colModel = audioTable.getColumnModel();

введите описание изображения здесь введите здесь описание изображения

Для этого я добавил этот слушатель:

audioTable = new JTable(modelAudio);
    JTableHeader header = audioTable.getTableHeader();
    header.addMouseMotionListener(new MouseMotionListener() {

        @Override
        public void mouseMoved(MouseEvent e) {
            System.out.println("mouseMoved");
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseDragged(MouseEvent e) {
            System.out.println("mouseDragged");
            // TODO Auto-generated method stub

        }
    });

И я вижу, что mouseMoved печатается снова и снова между этим NPE:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.synth.SynthTableHeaderUI$HeaderRenderer.getTableCellRendererComponent(Unknown Source)
at javax.swing.table.JTableHeader.getToolTipText(Unknown Source)
at javax.swing.ToolTipManager$insideTimerAction.actionPerformed(Unknown Source)
at javax.swing.Timer.fireActionPerformed(Unknown Source)
at javax.swing.Timer$DoPostEvent.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$400(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Я не уверен, как получить более длинную трассировку стека, вот как я создал таблицу:

modelAudio = new DefaultTableModel();
modelAudio.addColumn(" ");
audioTable = new JTable(modelAudio);

Сложная вещь, которую я пытаюсь сделать, это динамически добавлять столбцы, поэтому я добавляю столбец каждый раз, когда пользователь заполняет текущий столбец следующим образом:

TableColumn tc = new TableColumn(modelAudio.getColumnCount());
tc.setHeaderValue(" ");
audioTable.addColumn(tc);
modelAudio.addColumn(tc);

Является ли способ добавления столбцов проблемой, вызывающей этот NPE?


person georges    schedule 09.09.2015    source источник


Ответы (1)


Во-первых, вы не включили достаточно кода, чтобы я мог понять, почему NullPointerException, но, к счастью, это нормально, потому что вы подходите к проблеме с неправильной стороны.

Вам вообще не нужно использовать MouseMotionListener. Swing уже поддерживает поведение ToolTip, все, что вам нужно сделать, это получить доступ к самому средству визуализации ячеек. Просто установите средство визуализации на значение, которое установит свойство ToolTipText перед визуализацией компонента.

Вы не включили достаточно кода, чтобы я мог показать вам, как это сделать в вашем приложении, но я могу показать вам, как это сделать в обучающее приложение от Oracle: при начальной настройке сделайте следующее:

final TableCellRenderer renderer = table.getDefaultRenderer(Object.class);
table.setDefaultRenderer(Object.class, new TableCellRenderer() {
  @Override
  public Component getTableCellRendererComponent(JTable table,
      Object value, boolean isSelected, boolean hasFocus, int row,
      int column) {
    Component component = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    String columnHeader = table.getColumnModel().getColumn(column).getHeaderValue().toString();
    ((JComponent) component).setToolTipText(columnHeader);
    return component;
  }
});

Вот полный код демо с удаленным ненужным кодом:

import javax.swing.*;
import javax.swing.table.TableCellRenderer;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;

public class SimpleTableDemo extends JPanel {

  public SimpleTableDemo() {
    super(new GridLayout(1, 0));

    String[] columnNames = { "First Name", "Last Name", "Sport", "# of Years",
        "Vegetarian" };

    Object[][] data = {
        { "Kathy", "Smith", "Snowboarding", new Integer(5), new Boolean(false) },
        { "John", "Doe", "Rowing", new Integer(3), new Boolean(true) },
        { "Sue", "Black", "Knitting", new Integer(2), new Boolean(false) },
        { "Jane", "White", "Speed reading", new Integer(20), new Boolean(true) },
        { "Joe", "Brown", "Pool", new Integer(10), new Boolean(false) } };

    final JTable table = new JTable(data, columnNames);
    table.setPreferredScrollableViewportSize(new Dimension(500, 70));
    table.setFillsViewportHeight(true);

    final TableCellRenderer renderer = table.getDefaultRenderer(Object.class);
    table.setDefaultRenderer(Object.class, new TableCellRenderer() {
      @Override
      public Component getTableCellRendererComponent(JTable table,
          Object value, boolean isSelected, boolean hasFocus, int row,
          int column) {
        Component component = renderer.getTableCellRendererComponent(table,
            value, isSelected, hasFocus, row, column);
        String columnHeader = table.getColumnModel().getColumn(column)
            .getHeaderValue().toString();
        ((JComponent) component).setToolTipText(columnHeader);
        return component;
      }
    });

    // Create the scroll pane and add the table to it.
    JScrollPane scrollPane = new JScrollPane(table);

    // Add the scroll pane to this panel.
    add(scrollPane);
  }

  private static void createAndShowGUI() {
    // Create and set up the window.
    JFrame frame = new JFrame("SimpleTableDemo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // Create and set up the content pane.
    SimpleTableDemo newContentPane = new SimpleTableDemo();
    newContentPane.setOpaque(true); // content panes must be opaque
    frame.setContentPane(newContentPane);

    // Display the window.
    frame.pack();
    frame.setVisible(true);
  }

  public static void main(String[] args) {
    // Schedule a job for the event-dispatching thread:
    // creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      }
    });
  }
}

И скриншот (очевидно, моя мышь не захвачена на скриншоте):

введите здесь описание изображения


Вы можете сделать что-то подобное для ColumnHeader, используя table.getTableHeader().getDefaultRenderer():

final TableCellRenderer header = table.getTableHeader().getDefaultRenderer();
table.getTableHeader().setDefaultRenderer(new TableCellRenderer() {
  @Override
  public Component getTableCellRendererComponent(JTable table,
      Object value, boolean isSelected, boolean hasFocus, int row,
      int column) {
    Component tableCellRendererComponent = header.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    String columnHeader = table.getColumnModel().getColumn(column)
        .getHeaderValue().toString();
    ((JComponent) tableCellRendererComponent).setToolTipText(columnHeader);
    return tableCellRendererComponent;
  }
});
person durron597    schedule 09.09.2015
comment
@georges Не смогут ли пользователи дважды щелкнуть границу ячейки заголовка, чтобы автоматически изменить размер столбца, чтобы он соответствовал наибольшему имени ячейки? Или просто нажмите и перетащите? - person TylerH; 09.09.2015
comment
@durron597 смог увидеть подсказку для заголовка, когда я добавил ее в свой код, хотя я все еще получаю NPE. Может быть, это не правильный способ добавить столбец? TableColumn tc = новый TableColumn(modelAudio.getColumnCount()); tc.setHeaderValue(); audioTable.addColumn(tc); модельАудио.добавитьКолонку(tc); - person georges; 09.09.2015
comment
@georges Вы не разместили достаточно кода для отладки NPE. Этот конкретный фрагмент кода, вероятно, не вызовет этого. Пожалуйста, отредактируйте свой вопрос, указав дополнительную информацию. - person durron597; 09.09.2015
comment
@durron597 durron597, пытаясь воспроизвести проблему с подмножеством кода, который я считал уместным, я не могу дублировать NPE. Спасибо за всю вашу помощь, подсказка для заголовка работает в моем подмножестве, я просто буду добавлять, пока не найду проблему. Еще раз спасибо - я должен вам пиво. - person georges; 09.09.2015