【问题标题】:JTabbedPane TabComponent default tab highlightingJTabbedPane TabComponent 默认选项卡突出显示
【发布时间】:2016-07-22 20:03:02
【问题描述】:

我对 swing 中的自定义选项卡组件有疑问。

以下代码将添加3个自定义标签组件:

public class TabbedExample extends JPanel {
  public static void main(String... args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createUI();
      }
    }
  }
  public static void createUI() {
    try {
      for(LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
        if("Nimbus".equals(info.getName())) {
          UIManager.setLookAndFeel(info.getClassName());
          break;
        }
      }
    } catch(Exception e) {}
    JFrame frame = new JFrame("Tab Test");
    frame.setMinimumSize(new Dimension(256,200));
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new TabbedExample());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public TabbedExample() {
    super(new BorderLayout());

    JTabbedPane pane = new JTabbedPane();
    pane.addTab("tmp", new JTextField());
    pane.addTab("tmp", new JTextField());
    pane.addTab("tmp", new JTextField());
    for(int i = 0; i < 3; i++) {
      JPanel tabPanel = new JPanel();
      tabPanel.setBackground(new Color(0,0,0,0));
      tabPanel.setLayout(new BoxLayout(tabPanel, BoxLayout.X_AXIS));
      JTextField textField = new JTextField("Tab " + i);
      textField.setOpaque(false);
      textField.setBackground(new Color(0,0,0,0));
      textField.setBorder(new EmptyBorder(0,0,0,0));
      tabPanel.add(label);
      tabPanel.add(new JButton(Integer.toString(i)));
      pane.setTabComponentAt(i, tabPanel);
    }
    add(pane, BorderLayout.CENTER);
  }
}

现在的问题是默认选项卡行为停止工作。通常,当您将鼠标移到选项卡上时,它会通过更改背景颜色自动突出显示。但是一旦JTextField 被击中,选项卡很可能会注册一个mouseExited Event 并停止突出显示选项卡。因此,当您将鼠标移到选项卡上时,选项卡会闪烁。

我现在的问题是: 有没有办法(不实现新的突出显示机制)突出显示自定义 tabComponent 所在的选项卡?

【问题讨论】:

  • 突出显示是指获得焦点?
  • @TamasRev :不。它可能特定于 Nimbus LAF。我不确定其他 LAF 在这个问题上的表现如何。选项卡可以处于不同的状态(至少这看起来像是简单的用户)。标签区域外的鼠标聚焦(颜色 1),标签区域内的鼠标聚焦(颜色 2),标签区域外的未聚焦鼠标(颜色 3),标签区域内的未聚焦鼠标(颜色 4)。高亮显示的是当用户将光标移动到更改选项卡颜色的选项卡上时。使用自定义 tabComponent 将鼠标移到组件上时会有一些闪烁。

标签: java swing tabs jtextfield jtabbedpane


【解决方案1】:
  • 这是我的尝试:
    • 使用JLayerMouseMotionEvent 从选项卡分派到父JTabbedPane
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.*;

public class TabbedExample2 extends JPanel {
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      createUI();
    });
  }
  public static void createUI() {
    try {
      for (UIManager.LookAndFeelInfo laf: UIManager.getInstalledLookAndFeels()) {
        if ("Nimbus".equals(laf.getName())) {
          UIManager.setLookAndFeel(laf.getClassName());
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    JFrame frame = new JFrame("Tab Test");
    frame.setMinimumSize(new Dimension(256, 200));
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new TabbedExample2());
    frame.setSize(320, 240);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public TabbedExample2() {
    super(new BorderLayout());
    JTabbedPane pane = new JTabbedPane();
    pane.addTab("tmp", new JTextField(16));
    pane.addTab("tmp", new JTextField(16));
    pane.addTab("tmp", new JTextField(16));
    for (int i = 0; i < 3; i++) {
      JPanel tabPanel = new JPanel();
      tabPanel.setOpaque(false);
      //tabPanel.setBackground(new Color(0,0,0,0));
      tabPanel.setLayout(new BoxLayout(tabPanel, BoxLayout.X_AXIS));
      JTextField textField = new JTextField("Tab " + i);
      //textField.setBackground(new Color(0,0,0,0));
      //textField.setBorder(new EmptyBorder(0,0,0,0));
      //tabPanel.add(label); //???
      tabPanel.add(textField);
      tabPanel.add(new JButton(Integer.toString(i)));
      pane.setTabComponentAt(
          i, new JLayer<JPanel>(tabPanel, new DispatchEventLayerUI()));
    }
    add(pane);
  }
}

class DispatchEventLayerUI extends LayerUI<JPanel> {
  @Override
  public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK);
      //TEST:
      //((JLayer) c).setLayerEventMask(
      //    AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }
  @Override
  public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }
//   //TEST:
//   @Override
//   protected void processMouseEvent(MouseEvent e, JLayer<? extends JPanel> l) {
//     dispatchEvent(e);
//   }
  @Override
  protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends JPanel> l) {
    dispatchEvent(e);
  }
  private void dispatchEvent(MouseEvent e) {
    Component src = e.getComponent();
    Container tgt = SwingUtilities.getAncestorOfClass(JTabbedPane.class, src);
    tgt.dispatchEvent(SwingUtilities.convertMouseEvent(src, e, tgt));
  }
}

【讨论】:

  • 感谢您的推荐。它在示例中有效。不幸的是,我正在使用一种更复杂的应用程序,它不能那么容易地集成。无论如何,我正在寻找的魔法部分在你的方法dispatchEvent(MouseEvent e) 中。如果我忽略整个 JLayer 部分并简单地使用 dispatchEvent 方法,我可以轻松地使用 JTextField。不幸的是,我有一个自定义组件(而不是 JButton),当将自定义组件 MouseEvent 传递给 dispatchEvent? 方法时,它不能与代码一起使用
  • 第 2 部分:我能看到的唯一区别(当使用 system.out.printlntgtsrcevent 时,tgt 包含的值略有不同:javax.swing.JTabbedPane[,0,0,1024x800, invalid, layout=...。区别在于关键字invalid。但我必须承认,我完全不知道出了什么问题以及为什么它不起作用。有什么建议吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2014-02-17
  • 1970-01-01
相关资源
最近更新 更多