带有鼠标侦听器的 JTabbedPane 选项卡
JTabbedPane Tab with Mouse Listener
我正在尝试创建一个选项卡窗格,用户可以在其中 double-click 在选项卡上编辑其标题。到目前为止,我已经能够创建一个选项卡组件,其中 JPanel
和 JTextField
相互重叠,当您 double-click [=] 时切换到 JTextField
12=] 并在按 Enter:
时返回 JPanel
public class EditablePanel extends JPanel {
private JLabel label;
private JTextField field;
public EditablePanel(String title) {
super();
setLayout(new OverlayLayout(this));
setOpaque(false);
add(label = new JLabel(title));
label.setFocusable(false);
field = new JTextField(title);
field.setBorder(BorderFactory.createEmptyBorder());
field.setVisible(false);
field.addActionListener((e) -> finish(true));
add(field);
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
label.setVisible(false);
field.setVisible(true);
field.requestFocusInWindow();
field.selectAll();
}
}
});
}
private void finish(boolean commit) {
label.setText(field.getText());
label.setVisible(true);
field.setVisible(false);
}
}
当我将 JTabbedPane
中的选项卡组件设置为此 EditablePanel
的实例时,如果我的鼠标是在标签上。此外,如果 LAF 在鼠标悬停在选项卡上时执行某些操作(例如像 Windows 那样更改其颜色),则在鼠标悬停在标签上时停止应用。如果我单击 JLabel
之外的区域,我可以切换标签页,但如果我在 JLabel
上单击 single-click,则不能。我希望它能工作,这样如果我 single-click 在选项卡中的任何位置,它都会切换到该选项卡,但如果我 double-click 在选项卡上,它会开始编辑选项卡标题。
我试过在 EditablePanel
中使用 getMouseListeners
将面板的鼠标事件转发到 JLabel
,但它似乎忽略了它们。有没有一种方法可以将此组件用作选项卡组件,同时保留悬停和单击以更改选项卡的现有功能?如果没有,有没有办法扩展 JTabbedPane
以获得我想要的可编辑标题功能?
这是一个完整的 SCCM 演示我的问题:
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class EditablePanel extends JPanel {
private JLabel label;
private JTextField field;
public EditablePanel(String title) {
super();
setLayout(new OverlayLayout(this));
setOpaque(false);
add(label = new JLabel(title));
label.setFocusable(false);
field = new JTextField(title);
field.setBorder(BorderFactory.createEmptyBorder());
field.setVisible(false);
field.addActionListener((e) -> finish(true));
add(field);
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
label.setVisible(false);
field.setVisible(true);
field.requestFocusInWindow();
field.selectAll();
}
}
});
}
private void finish(boolean commit) {
label.setText(field.getText());
label.setVisible(true);
field.setVisible(false);
field.transferFocusUpCycle();
}
public static void main(String[] args) {
JFrame frame = new JFrame("Editable Tab Headers");
frame.setPreferredSize(new Dimension(400, 300));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTabbedPane pane = new JTabbedPane();
pane.addTab("First Tab", new JLabel("First tab contents"));
pane.addTab("Second Tab", new JLabel("Second tab contents"));
pane.setTabComponentAt(0, new EditablePanel("First Tab"));
pane.setTabComponentAt(1, new EditablePanel("Second Tab"));
frame.add(pane);
frame.pack();
frame.setVisible(true);
}
}
is there a way I could extend JTabbedPane to get the editable title functionality I want?
我会将 MouseListener 添加到选项卡式窗格。
然后在 mouseClicked(...)
事件中,您可以检查双击并在选项卡顶部显示 JTextField。当您在文本字段上按 Enter 键时,您会从选项卡式窗格中删除文本字段。
所以显示文本字段的基础是:
JTabbedPane tabbedPane = (JTabbedPane)e.getComponent();
TabbedPaneUI ui = tabbedPane.getUI();
int tab = ui.tabForCoordinate(tabbedfPane, e.getX(), e.getY());
if (tab != -1) // I believe -1 is returned if you don't click on a tab
{
Rectangle bounds = ui.getTabBounds(tabbedPane, tab);
JTextField textField = new JTextField();
textField.setText(...);
textField.setBounds( bounds );
textField.addActionListener(...);
tabbedPane.add( textField );
tappedPane.repaint();
}
然后在 ActionListener
中您将获取文本并更新选项卡标题,然后从选项卡式窗格中删除文本字段。
现在选项卡应该正常运行,因为文本字段仅在您编辑文本标题时临时显示。
注意,这基本上就是 JTable 编辑器的工作方式。当您双击一个单元格时,一个文本字段会添加到单元格顶部的 table,然后在编辑完成后删除。
作为 camickr 提议的替代方案,您可以使用 MouseListener
将所有事件分派给 JTabbedPane
。这应该达到不影响其他行为的预期效果,并且仍然处理双击的特殊情况。
这是一个 MCVE,我还修复了编辑行为,因为我添加了一个焦点侦听器,当在编辑时选择新选项卡时(即当编辑 not 按 Enter).
确认
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.OverlayLayout;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class EditablePanel extends JPanel
{
private JLabel label;
private JTextField field;
public EditablePanel(String title)
{
super();
setLayout(new OverlayLayout(this));
setOpaque(false);
add(label = new JLabel(title));
label.setFocusable(false);
field = new JTextField(title);
field.setBorder(BorderFactory.createEmptyBorder());
field.setVisible(false);
field.addActionListener((e) -> finish(true));
field.addFocusListener(new FocusListener()
{
@Override
public void focusLost(FocusEvent e)
{
finish(false);
}
@Override
public void focusGained(FocusEvent e)
{
// Nothing to do here
}
});
add(field);
TabMouseAdapter mouseAdapter = new TabMouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
super.mouseClicked(e);
if (e.getClickCount() == 2)
{
label.setVisible(false);
field.setVisible(true);
field.requestFocusInWindow();
field.selectAll();
}
}
@Override
public void mousePressed(MouseEvent e)
{
super.mousePressed(e);
finish(false);
}
};
label.addMouseListener(mouseAdapter);
}
static class TabMouseAdapter implements MouseListener
{
@Override
public void mouseClicked(MouseEvent e)
{
redispatch(e);
}
@Override
public void mousePressed(MouseEvent e)
{
redispatch(e);
}
@Override
public void mouseReleased(MouseEvent e)
{
redispatch(e);
}
@Override
public void mouseEntered(MouseEvent e)
{
redispatch(e);
}
@Override
public void mouseExited(MouseEvent e)
{
redispatch(e);
}
private void redispatch(MouseEvent e)
{
Component source = e.getComponent();
Component target = source.getParent();
while (true)
{
if (target == null)
{
break;
}
if (target instanceof JTabbedPane)
{
break;
}
target = target.getParent();
}
if (target != null)
{
MouseEvent targetEvent =
SwingUtilities.convertMouseEvent(source, e, target);
target.dispatchEvent(targetEvent);
}
}
}
private void finish(boolean commit)
{
if (commit)
{
label.setText(field.getText());
}
label.setVisible(true);
field.setVisible(false);
field.transferFocusUpCycle();
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Editable Tab Headers");
frame.setPreferredSize(new Dimension(400, 300));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTabbedPane pane = new JTabbedPane();
pane.addTab("First Tab", new JLabel("First tab contents"));
pane.addTab("Second Tab", new JLabel("Second tab contents"));
pane.setTabComponentAt(0, new EditablePanel("First Tab"));
pane.setTabComponentAt(1, new EditablePanel("Second Tab"));
frame.add(pane);
frame.pack();
frame.setVisible(true);
}
}
例如,如果您想将弹出菜单添加到第 i 个选项卡
您可以使用所选选项卡和第 i 个选项卡的相等性。
在我的示例中,我想将 popupmenu 添加到我的第一个选项卡中
我应该检查 selectedtab 和第一个 tab(index=0)
是否相等
你可以这样使用:
JTabbedPane tabs=new JTabbedPane();
JPopupMenu popupMenu=new JPopupMenu("Edit");
popupMenu.add(new JMenuItem("Cut"));
popupMenu.add(new JMenuItem("Copy"));
JPanel bodyPanel=new JPanel();
tabs.add("Body",bodyPanel);
tabs.add("DummyTab",new JPanel());
tabs.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(tabs.getSelectedComponent().equals(tabs.getComponentAt(0))){
popupMenu.show(bodyPanel,e.getX(),e.getY());
}
}
});
我正在尝试创建一个选项卡窗格,用户可以在其中 double-click 在选项卡上编辑其标题。到目前为止,我已经能够创建一个选项卡组件,其中 JPanel
和 JTextField
相互重叠,当您 double-click [=] 时切换到 JTextField
12=] 并在按 Enter:
JPanel
public class EditablePanel extends JPanel {
private JLabel label;
private JTextField field;
public EditablePanel(String title) {
super();
setLayout(new OverlayLayout(this));
setOpaque(false);
add(label = new JLabel(title));
label.setFocusable(false);
field = new JTextField(title);
field.setBorder(BorderFactory.createEmptyBorder());
field.setVisible(false);
field.addActionListener((e) -> finish(true));
add(field);
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
label.setVisible(false);
field.setVisible(true);
field.requestFocusInWindow();
field.selectAll();
}
}
});
}
private void finish(boolean commit) {
label.setText(field.getText());
label.setVisible(true);
field.setVisible(false);
}
}
当我将 JTabbedPane
中的选项卡组件设置为此 EditablePanel
的实例时,如果我的鼠标是在标签上。此外,如果 LAF 在鼠标悬停在选项卡上时执行某些操作(例如像 Windows 那样更改其颜色),则在鼠标悬停在标签上时停止应用。如果我单击 JLabel
之外的区域,我可以切换标签页,但如果我在 JLabel
上单击 single-click,则不能。我希望它能工作,这样如果我 single-click 在选项卡中的任何位置,它都会切换到该选项卡,但如果我 double-click 在选项卡上,它会开始编辑选项卡标题。
我试过在 EditablePanel
中使用 getMouseListeners
将面板的鼠标事件转发到 JLabel
,但它似乎忽略了它们。有没有一种方法可以将此组件用作选项卡组件,同时保留悬停和单击以更改选项卡的现有功能?如果没有,有没有办法扩展 JTabbedPane
以获得我想要的可编辑标题功能?
这是一个完整的 SCCM 演示我的问题:
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class EditablePanel extends JPanel {
private JLabel label;
private JTextField field;
public EditablePanel(String title) {
super();
setLayout(new OverlayLayout(this));
setOpaque(false);
add(label = new JLabel(title));
label.setFocusable(false);
field = new JTextField(title);
field.setBorder(BorderFactory.createEmptyBorder());
field.setVisible(false);
field.addActionListener((e) -> finish(true));
add(field);
label.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
label.setVisible(false);
field.setVisible(true);
field.requestFocusInWindow();
field.selectAll();
}
}
});
}
private void finish(boolean commit) {
label.setText(field.getText());
label.setVisible(true);
field.setVisible(false);
field.transferFocusUpCycle();
}
public static void main(String[] args) {
JFrame frame = new JFrame("Editable Tab Headers");
frame.setPreferredSize(new Dimension(400, 300));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTabbedPane pane = new JTabbedPane();
pane.addTab("First Tab", new JLabel("First tab contents"));
pane.addTab("Second Tab", new JLabel("Second tab contents"));
pane.setTabComponentAt(0, new EditablePanel("First Tab"));
pane.setTabComponentAt(1, new EditablePanel("Second Tab"));
frame.add(pane);
frame.pack();
frame.setVisible(true);
}
}
is there a way I could extend JTabbedPane to get the editable title functionality I want?
我会将 MouseListener 添加到选项卡式窗格。
然后在 mouseClicked(...)
事件中,您可以检查双击并在选项卡顶部显示 JTextField。当您在文本字段上按 Enter 键时,您会从选项卡式窗格中删除文本字段。
所以显示文本字段的基础是:
JTabbedPane tabbedPane = (JTabbedPane)e.getComponent();
TabbedPaneUI ui = tabbedPane.getUI();
int tab = ui.tabForCoordinate(tabbedfPane, e.getX(), e.getY());
if (tab != -1) // I believe -1 is returned if you don't click on a tab
{
Rectangle bounds = ui.getTabBounds(tabbedPane, tab);
JTextField textField = new JTextField();
textField.setText(...);
textField.setBounds( bounds );
textField.addActionListener(...);
tabbedPane.add( textField );
tappedPane.repaint();
}
然后在 ActionListener
中您将获取文本并更新选项卡标题,然后从选项卡式窗格中删除文本字段。
现在选项卡应该正常运行,因为文本字段仅在您编辑文本标题时临时显示。
注意,这基本上就是 JTable 编辑器的工作方式。当您双击一个单元格时,一个文本字段会添加到单元格顶部的 table,然后在编辑完成后删除。
作为 camickr 提议的替代方案,您可以使用 MouseListener
将所有事件分派给 JTabbedPane
。这应该达到不影响其他行为的预期效果,并且仍然处理双击的特殊情况。
这是一个 MCVE,我还修复了编辑行为,因为我添加了一个焦点侦听器,当在编辑时选择新选项卡时(即当编辑 not 按 Enter).
确认import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.OverlayLayout;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class EditablePanel extends JPanel
{
private JLabel label;
private JTextField field;
public EditablePanel(String title)
{
super();
setLayout(new OverlayLayout(this));
setOpaque(false);
add(label = new JLabel(title));
label.setFocusable(false);
field = new JTextField(title);
field.setBorder(BorderFactory.createEmptyBorder());
field.setVisible(false);
field.addActionListener((e) -> finish(true));
field.addFocusListener(new FocusListener()
{
@Override
public void focusLost(FocusEvent e)
{
finish(false);
}
@Override
public void focusGained(FocusEvent e)
{
// Nothing to do here
}
});
add(field);
TabMouseAdapter mouseAdapter = new TabMouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
super.mouseClicked(e);
if (e.getClickCount() == 2)
{
label.setVisible(false);
field.setVisible(true);
field.requestFocusInWindow();
field.selectAll();
}
}
@Override
public void mousePressed(MouseEvent e)
{
super.mousePressed(e);
finish(false);
}
};
label.addMouseListener(mouseAdapter);
}
static class TabMouseAdapter implements MouseListener
{
@Override
public void mouseClicked(MouseEvent e)
{
redispatch(e);
}
@Override
public void mousePressed(MouseEvent e)
{
redispatch(e);
}
@Override
public void mouseReleased(MouseEvent e)
{
redispatch(e);
}
@Override
public void mouseEntered(MouseEvent e)
{
redispatch(e);
}
@Override
public void mouseExited(MouseEvent e)
{
redispatch(e);
}
private void redispatch(MouseEvent e)
{
Component source = e.getComponent();
Component target = source.getParent();
while (true)
{
if (target == null)
{
break;
}
if (target instanceof JTabbedPane)
{
break;
}
target = target.getParent();
}
if (target != null)
{
MouseEvent targetEvent =
SwingUtilities.convertMouseEvent(source, e, target);
target.dispatchEvent(targetEvent);
}
}
}
private void finish(boolean commit)
{
if (commit)
{
label.setText(field.getText());
}
label.setVisible(true);
field.setVisible(false);
field.transferFocusUpCycle();
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Editable Tab Headers");
frame.setPreferredSize(new Dimension(400, 300));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JTabbedPane pane = new JTabbedPane();
pane.addTab("First Tab", new JLabel("First tab contents"));
pane.addTab("Second Tab", new JLabel("Second tab contents"));
pane.setTabComponentAt(0, new EditablePanel("First Tab"));
pane.setTabComponentAt(1, new EditablePanel("Second Tab"));
frame.add(pane);
frame.pack();
frame.setVisible(true);
}
}
例如,如果您想将弹出菜单添加到第 i 个选项卡 您可以使用所选选项卡和第 i 个选项卡的相等性。 在我的示例中,我想将 popupmenu 添加到我的第一个选项卡中 我应该检查 selectedtab 和第一个 tab(index=0)
是否相等你可以这样使用:
JTabbedPane tabs=new JTabbedPane();
JPopupMenu popupMenu=new JPopupMenu("Edit");
popupMenu.add(new JMenuItem("Cut"));
popupMenu.add(new JMenuItem("Copy"));
JPanel bodyPanel=new JPanel();
tabs.add("Body",bodyPanel);
tabs.add("DummyTab",new JPanel());
tabs.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(tabs.getSelectedComponent().equals(tabs.getComponentAt(0))){
popupMenu.show(bodyPanel,e.getX(),e.getY());
}
}
});