JTable: ToolTipText-Anzeige abhängig von Zellengröße und Textlänge

FetterOtter

Bekanntes Mitglied
Hallo Leute,

nachdem mich die Forensuche und google auch nicht schlauer gemacht haben, belästige ich nun euch mit meinem Problem.

Ich habe in meiner Anwendung eine JTable mit mehreren Spalten und Zeilen. Über meinen eigenen TableCellRenderer weise ich allen Zellen einen ToolTipText zu (ganz profan: der Text der entsprechenden Zelle). Nun das große ABER: Ich möchte, dass der ToolTipText nur dann angezeigt wird, wenn der Text nicht in die Zelle passt (also mit den drei Pünktchen abgekürzt wird).
Dafür habe ich in meinem TableCellRenderer die Methode getToolTipText(MouseEvent event) überschrieben, um hier abzufragen, ob die eigentlich benötigte Breite der Zelle größer ist als die tatsächliche Breite. Funzt aber nicht. Die Table wird wunderbar angezeigt, aber egal, über welcher Zelle meine Maus verweilt, der entsprechende ToolTipText wird immer angezeigt. Über einen println kann ich sehen, dass getPreferredSize().width ganz brav die benötigte Breite abhängig vom Text liefert. Aber getWidth(), mit dem ich die tatsächliche Zellenbreite in Erfahrung bringen wollte, liefert immer 0.

Ich habe ein kleines KSKB zusammengestrickt; vielleicht mag da ja mal jemand reinschauen. Die Methode getToolTipText ist ganz am Ende (und ich auch...).

Java:
public class Tabelle
{
  private static final String[] COLUMN_HEADER = {"A", "B"};
  
  private JTable            table;
  private DefaultTableModel tableModel;
  
  private String[][]        data = new String[][]{{"1", "Kurzer Text"},
                                                  {"2", "Laaaaaaaaaaaaaaaaaaaaaaaaanger Text"}};

  public static void main(final String[] args)
  {
    new Tabelle();
  }
  
  public Tabelle()
  {
    JFrame frame = new JFrame( "Demo" );
    frame.getContentPane().add(new JScrollPane(getTable()));
    frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
    frame.pack();
    frame.setVisible(true);
  }

  public JTable getTable()
  {
    if (table == null)
    {
      table = new JTable(getTableModel())
      {
        private static final long serialVersionUID = 1L;
        public boolean isCellEditable(int x, int y)
        {
          return false;
        }
      };
      table.getColumnModel().getColumn(0).setCellRenderer(new MyTableCellRenderer());
      table.getColumnModel().getColumn(1).setCellRenderer(new MyTableCellRenderer());
    }
    
    return table;
  }

  public DefaultTableModel getTableModel()
  {
    if (tableModel == null)
    {
      tableModel = new DefaultTableModel(data, COLUMN_HEADER);
    }    
    return tableModel;
  }

  private static class MyTableCellRenderer extends JLabel implements TableCellRenderer
  {    
    private static final long serialVersionUID = 1L;

    public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus,
                                                   final int row, final int column)
    {
      setText(value.toString());
      
      setOpaque(true);
      
      setToolTipText(value.toString());      
  
      return this;
    }
    public String getToolTipText(MouseEvent event)
    {
      System.out.println("PrefSize: " + getPreferredSize().width + ", width: " + getWidth());
      
      return getPreferredSize().width > getWidth() ? super.getToolTipText(event) : null;
    }
  } 
}
 
Ok, hierfür muss ich ein wenig improvisieren... Ich habe hier etwas ähnliches.
Ich überschreibe auch
Code:
getTableCellRendererComponent
, rufe dann dort meine Methode
Code:
renderToolTip

Die schaut aus wie folgt :

Java:
    public void renderToolTip(JTable table, Object value, int column){
        String tooltip = getTooltipFix(table, column);
        if (tooltip == null){
            this.setToolTipText(getToolTipIfNecessary(table, value, column));
        }else{
            this.setToolTipText(tooltip);
        }
    }

Der erste Konstrukt sorgt dafür das man das setzen eines konkreten TT erzwingen kann. Wichtiger für Dich ist wohl eher meine
Code:
getToolTipIfNecessary
.

Java:
    public String getToolTipIfNecessary(JTable table, Object value, int column){
        String tooltip = null;
        if (value != null) {
            Graphics graf = table.getGraphics();
            if (graf != null) {
                TableColumnModel tcm = table.getColumnModel();
                TableColumn tc = tcm.getColumn(column);
                int tcWidth = tc.getWidth();
                FontMetrics fm = graf.getFontMetrics();
                if (fm != null) {
                    int sWidth;
                    sWidth = fm.stringWidth(value.toString()) + TOOLTIP_WIDTH_KORREKTUR;
                    if (tcWidth < sWidth) {
                        tooltip = value.toString();
                    }
                }
            }
        }
        return tooltip;
    }

die eben prüft ob der Tooltip angezeigt werden muss da das Label nicht in die Zelle passt.
Ich weiß nicht mehr wieso ich da
Code:
private final int TOOLTIP_WIDTH_KORREKTUR = 5;
eingebaut habe aber irgendwie war diese Korrektur noch notwendig.
Lang lang ist's her...
 
Hallo Voltaire81,

vielen Dank für deine Hilfe!
Ich konnte deine Lösung nicht so recht auf mein Proggi übertragen, da ich an der Stelle, an der ich micht festgefressen hatte, keine JTable mehr zur Verfügung hatte.
Und genau damit hast du mir den richtigen Denkanstoß verpasst!
Zur Lösung: Ich habe nun das Überschreiben der Methode
Code:
getToolTipText(MouseEvent event
wieder rausgeschmissen; stattdessen entscheidet der Renderer in der Methode
Code:
getTableCellRendererComponent
, ob er einen ToolTipText hinzufügen soll oder nicht. Und das klappt tadellos, auch wenn man anfängt, in der JTable die Spaltenbreiten hin- und herzuschieben.
Mit dem anderen Weg hatte ich wohl versucht, den Gaul von hinten aufzuzäumen...

Für alle, die es interessiert, poste ich noch einmal den vollständigen Code.

Java:
public class Tabelle
{
  private static final long     serialVersionUID = 1L;
  private static final String[] COLUMN_HEADER = {"A", "B"};
  
  private JTable            table;
  private DefaultTableModel tableModel;
  
  private String[][]        data = new String[][]{{"1", "Kurzer Text"},
                                                  {"2", "Laaaaaaaaaaaaaaaaaaaaaaaaanger Text"}};

  public static void main(final String[] args)
  {
    new Tabelle();
  }
  
  public Tabelle()
  {
    JFrame frame = new JFrame( "Demo" );
    frame.getContentPane().add(new JScrollPane(getTable()));
    frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
    frame.pack();
    frame.setVisible(true);
  }

  public JTable getTable()
  {
    if (table == null)
    {
      table = new JTable(getTableModel())
      {
        private static final long serialVersionUID = 1L;
        public boolean isCellEditable(int x, int y)
        {
          return false;
        }
      };
      table.getColumnModel().getColumn(0).setCellRenderer(new MyTableCellRenderer());
      table.getColumnModel().getColumn(1).setCellRenderer(new MyTableCellRenderer());
    }
    
    return table;
  }

  public DefaultTableModel getTableModel()
  {
    if (tableModel == null)
    {
      tableModel = new DefaultTableModel(data, COLUMN_HEADER);
    }    
    return tableModel;
  }
  private static class MyTableCellRenderer extends JLabel implements TableCellRenderer
  {    
    private static final long serialVersionUID = 1L;

    public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus,
                                                   final int row, final int column)
    {
      setText(value.toString());
      
      setOpaque(true);
      
      if (table.getColumnModel().getColumn(column).getWidth() < getPreferredSize().width)
      {
        setToolTipText(value.toString());
      }
      else
      {
        setToolTipText(null);
      }
  
      return this;
    }
  } 
}

Schönen Tag und noch Mal danke !!
 

Zurück
Oben