简体   繁体   中英

Tooltips don't display for JLabels in a JPanel in a JTable cell

The following shows JTable cells each containing a panel that in turn contains two labels. I've set tooltips on the JLabel s, but they don't show up. (And thanks to @MadProgrammer for the original stripped-down code to display a JTable and illustrate setToolTipLocation() .)

I override getToolTipText() for one label, and call setToolTipText() for another; if I set a breakpoint in the former one, it is called but no tooltip appears. So I tried overriding setToolTipLocation() , though I'm not sure what appropriate values are for it; I'm not sure what coordinate system it would use, figured 10,10 would leave it in range of most things.

It's not shown here, I've also tried creating the labels outside the getTableCellRendererComponent() call, and setting their dimensions, background colors, and tooltips within getTableCellRendererComponent() . That doesn't work either.

What else do I need to do to see those tooltips?

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.border.Border;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
    
    public class Test
    {
      Border labelBorder = BorderFactory.createLineBorder(Color.black);
    
      public static void main(String[] args)
      {
        new Test();
      }
    
      public Test()
      {
        EventQueue.invokeLater
        (new Runnable()
          {
            @Override
            public void run()
            {
              JFrame frame = new JFrame("Testing");
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.add(new TestPane());
              frame.pack();
              frame.setLocationRelativeTo(null);
              frame.setVisible(true);
            }
          }
        );
      }
    
      public class TestPane extends JPanel
      {
        public TestPane()
        {
          setLayout(new BorderLayout());
          int rows = 4;
          int cols = 3;
          DefaultTableModel model = new DefaultTableModel(0, cols);
          for (int row = 0; row < rows; row++)
          {
            Object[] data = new Object[cols];
            for (int col = 0; col < cols; col++)
            {
              data[col] = row + "x" + col;
            }
            model.addRow(data);
          }
    
          JTable table = new JTable(model);
//          table.setToolTipText("table text");     // this works, but would block tooltips from UI children
          table.setDefaultRenderer(Object.class, new TestCellRenderer());
    
          add(new JScrollPane(table));
        }
    
        public class TestCellRenderer extends DefaultTableCellRenderer
        {
          @Override
          public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
              int row, int column)
          {
            // create two labels, put them side-by-side in a panel, and return
            // the panel as the component being rendered.
            JLabel label1 = new JLabel("a Banana @" + value.toString())
            {
              @Override
              public String getToolTipText() 
              { 
                return "AA"; 
              }
              @Override public Point getToolTipLocation(MouseEvent mouseEvent) { return new Point(10,10); }
            };
            label1.setBorder(labelBorder);
            label1.setToolTipText("AA");            // this tooltip text does not show up.
    
            JLabel label2 = new JLabel("b Banana @" + value.toString());
            label2.setBorder(labelBorder);
            label2.setToolTipText("BB");            // this tooltip text does not show up.
            
            JPanel panel = new JPanel();
            BoxLayout layout = new BoxLayout(panel, BoxLayout.X_AXIS);
            panel.setLayout(layout);
//            panel.setToolTipText("panel text");   //this works, but would block tooltips on the JLabels
            panel.add(label1);
            panel.add(label2);
            
            return panel;
          }
        }
      }
    }

I override getToolTipText() for one label

How about trying to override the JTable#getToolTipText(MouseEvent) method instead of JLabel :

import java.awt.*;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.table.*;

public class Test2 {
  public static void main(String[] args) {
    new Test2();
  }

  public Test2() {
    EventQueue.invokeLater(() -> {
      JFrame frame = new JFrame("Testing");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.add(new TestPane());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
    });
  }
}

class TestPane extends JPanel {
  public TestPane() {
    super(new BorderLayout());
    int rows = 4;
    int cols = 3;
    DefaultTableModel model = new DefaultTableModel(0, cols);
    for (int row = 0; row < rows; row++) {
      Object[] data = new Object[cols];
      for (int col = 0; col < cols; col++) {
        data[col] = row + "x" + col;
      }
      model.addRow(data);
    }

    JTable table = new JTable(model) {
      @Override public String getToolTipText(MouseEvent e) {
        Point pt = e.getPoint();
        int vrow = rowAtPoint(pt);
        int vcol = columnAtPoint(pt);
        TableCellRenderer tcr = getCellRenderer(vrow, vcol);
        Component c = prepareRenderer(tcr, vrow, vcol);
        if (c instanceof JPanel) {
          Rectangle r = getCellRect(vrow, vcol, true);
          c.setBounds(r);
          c.doLayout();
          pt.translate(-r.x, -r.y);
          Component l = SwingUtilities.getDeepestComponentAt(c, pt.x, pt.y);
          if (l instanceof JLabel) {
            return ((JLabel) l).getToolTipText();
          }
        }
        return super.getToolTipText(e);
      }
    };
    table.setDefaultRenderer(Object.class, new TestCellRenderer());
    add(new JScrollPane(table));
  }
}

class TestCellRenderer extends DefaultTableCellRenderer {
  @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    JLabel label1 = new JLabel("a Banana @" + value.toString());
    label1.setBorder(BorderFactory.createLineBorder(Color.BLACK));
    label1.setToolTipText("AA");

    JLabel label2 = new JLabel("b Banana @" + value.toString());
    label2.setBorder(BorderFactory.createLineBorder(Color.BLACK));
    label2.setToolTipText("BB");

    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
    panel.add(label1);
    panel.add(label2);

    return panel;
  }
}

Had some old code lying around that allowed you to override the getToolTipText(...) method of the panel being used as the renderer.

Unfortunately it only seemed to work when the GridLayout was used as the layout manager of the panel.

Using the "layout" code provided by aterai I was able to get the code working for a FlowLayout as well. (didn't try other layout managers):

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class TableRendererPanelHelp
{
    static class MultiLabelRenderer implements TableCellRenderer
    {
        private JTable table;
        private int row;
        private int column;

        private JPanel panel;
        private JLabel red;
        private JLabel blue;

        public MultiLabelRenderer()
        {
            red = new JLabel();
            red.setForeground(Color.RED);
            blue = new JLabel();
            blue.setForeground(Color.BLUE);

            panel = new JPanel( new FlowLayout(0, 0, FlowLayout.LEFT) )
            {
                public String getToolTipText(MouseEvent e)
                {
                    String tooltip = null;

                    Rectangle r = table.getCellRect(row, column, true);
                    setBounds(r);
                    doLayout();

                    Component c = getComponentAt( e.getPoint() );

                    if (c instanceof JLabel)
                    {
                        JLabel label = (JLabel)c;
                        tooltip = label.getToolTipText();
                    }

                    return tooltip;
                }
            };

            panel.add(red);
            panel.add(blue);
        }

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, final int row, final int column)
        {
            this.row = row;
            this.column= column;
            this.table = table;

            panel.setBackground( isSelected ? table.getSelectionBackground() : table.getBackground() );

            if (value == null || value.toString().isEmpty())
            {
                red.setText("");
                blue.setText("");
            }
            else
            {
                String text = value.toString();
                red.setText( text.substring(0, 3) );
                red.setToolTipText( red.getText() );
                blue.setText( text.substring(3) );
                blue.setToolTipText( blue.getText() );
            }

            return panel;
        }
    }

    public static void createAndShowGUI()
    {
        JTable table = new JTable(5, 3);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        table.getColumnModel().getColumn(0).setCellRenderer( new MultiLabelRenderer());
        JScrollPane scrollPane = new JScrollPane( table );

        table.setValueAt("abcde", 0, 0);
        table.setValueAt("123456789", 1, 0);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.add( scrollPane );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception
    {
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    }

}

A benefit of this approach is that all the logic is contained in the renderer class and you don't need to override the JTable.

Also, this approach tries to reuse the JPanel and JLabels so you don't continually create new components every time a cell is rendered.

Maybe late, but here a similar approach, should not depend on the layout manager.

All code is in the CellRenderer that directly extends JPanel, then it rethrows the getToolTip request to the subcomponent adapting the MouseEvent.

package test;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class TestTooltipTable {

    Border labelBorder = BorderFactory.createLineBorder(Color.black);

    public static void main(String[] args) {
        new TestTooltipTable();
    }

    public TestTooltipTable() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {
        public TestPane() {
            setLayout(new BorderLayout());
            int rows = 4;
            int cols = 3;
            DefaultTableModel model = new DefaultTableModel(0, cols);
            for (int row = 0; row < rows; row++) {
                Object[] data = new Object[cols];
                for (int col = 0; col < cols; col++) {
                    data[col] = row + "x" + col;
                }
                model.addRow(data);
            }

            JTable table = new JTable(model);
//          table.setToolTipText("table text");     // this works, but would block tooltips from UI children
            table.setDefaultRenderer(Object.class, new TestCellRenderer());

            add(new JScrollPane(table));
        }

        public class TestCellRenderer extends JPanel implements TableCellRenderer {
            protected JLabel label1;
            protected JLabel label2;
            
            protected Rectangle cellRect;

            public TestCellRenderer() {
                setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
                label1 = new JLabel(" ");
                label2 = new JLabel(" ");

                label1.setBorder(labelBorder);
                label1.setToolTipText("AA"); // this tooltip text does not show up.

                label2.setBorder(labelBorder);
                label2.setToolTipText("BB"); // this tooltip text does not show up.

                add(label1);
                add(label2);

            }

            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                    boolean hasFocus, int row, int column) {
                // create two labels, put them side-by-side in a panel, and return
                // the panel as the component being rendered.
                label1.setText("a Banana @" + value.toString());
                label1.setToolTipText("AA "+row+"x"+column);

                label2.setText("b Banana @" + value.toString());
                label2.setToolTipText("BB "+row+"x"+column); // this tooltip text does not show up.

                cellRect=table.getCellRect(row, column, true);
                
                return this;
            }

            @Override
            public String getToolTipText(MouseEvent event) {
                if (cellRect==null) {
                    return null;
                }
                
                setBounds(cellRect);
                doLayout();
                Point p=event.getPoint();
                Component c=SwingUtilities.getDeepestComponentAt(this, p.x, p.y);

                if (!(c instanceof JComponent)) {
                    return null;
                }
                p=SwingUtilities.convertPoint(this, p, c);
                MouseEvent newEvent = new MouseEvent(c, event.getID(),
                        event.getWhen(), event.getModifiersEx(),
                        p.x, p.y,
                        event.getXOnScreen(),
                        event.getYOnScreen(),
                        event.getClickCount(),
                        event.isPopupTrigger(),
                        MouseEvent.NOBUTTON);

                return ((JComponent) c).getToolTipText(newEvent);
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM