在Java中为jTabbedPane的选项卡设置光标

5

我创建了一个自定义的jTabbedPane类,该类继承了BasicTabbedPaneUI,并成功地创建了我想要的jTabbedPane。但现在的问题是,如何为自定义的jTabbedPane中的每个选项卡设置手形光标?

我尝试使用以下代码设置光标:

tabbedPane.setUI(new CustomMainMenuTabs());
tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));

这会设置整个jTabbedPane的光标,但是我只希望在鼠标悬停在任何选项卡上时设置光标。

如何为我的jTabbedPane中的选项卡设置手型光标?

我的代码是

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.plaf.basic.BasicTabbedPaneUI;


public class HAAMS 
{
  //My Custom class for jTabbedPane
  public static class CustomMainMenuTabs extends BasicTabbedPaneUI
  {
    protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)
    {
        Graphics2D g2 = (Graphics2D) g;

        Color color;

        if (isSelected) { color = new Color(74, 175, 211); } 
        else if (getRolloverTab() == tabIndex) {  color = new Color(45, 145, 180); } 
        else {color = new Color(68, 67, 67);}

        g2.setPaint(color);
        g2.fill(new RoundRectangle2D.Double(x, y, w, h, 30, 30));

        g2.fill(new Rectangle2D.Double(x + 100,y,w,h));
    }
  }

   public static void main(String[] args) 
   {
     JFrame MainScreen = new JFrame("Custom JTabbedPane");
     MainScreen.setExtendedState(MainScreen.getExtendedState() | JFrame.MAXIMIZED_BOTH);

     //Setting UI for my jTabbedPane implementing my custom class CustomMainMenuTabs
     JTabbedPane jtpane = new JTabbedPane(2);
     jtpane.setUI(new CustomMainMenuTabs());
     jtpane.add("1st Tabe", new JPanel());
     jtpane.add("2nd Tabe", new JPanel());
     jtpane.add("3rd Tabe", new JPanel());

     MainScreen.getContentPane().add(jtpane);
     MainScreen.setVisible(true);
  }
}

如何在鼠标悬停在任何选项卡上时将光标设置为HAND_CURSOR光标,而不是JPanel或任何其他组件。如果可以不使用鼠标监听器实现,那就太好了。


你需要在你的用户界面中提供鼠标支持。只需添加一个鼠标监听器,当鼠标进入选项卡标题组件时将光标设置为手形。 - Sergiy Medvynskyy
@SergiyMedvynskyy,有什么方法可以检测JTabbedPane的选项卡标题组件,以便我可以添加鼠标监听器? - Airy
为什么你要发布悬赏,你已经得到了答案?当你添加MouseMotionListener并使用tabForCoordinate(...)方法时,代码对我来说运行良好。或者你因为我的原始答案建议使用MouseListener而遇到问题了吗?我希望你知道区别,但由于你没有发布你的SSCCE,我不确定那是否是你的问题。 - camickr
@camickr 我发布了我所拥有的全部内容。我也尝试了你的建议,但对我没有用。这就是为什么我问你是否可以给我一个代码示例,以便我可以理解它。而且我还在寻找是否可能在没有鼠标监听器的情况下完成。 - Airy
@AbdulJabbarWebBestow,我也尝试了你的建议,但对我没有用。你已经被要求两次发布显示你尝试过的SSCCE。向我们证明你已经努力听取了建议。基本代码很简单。如果tabForCoordinate()方法返回-1,则将光标设置为null,否则将光标设置为手形光标。我会离开几天,所以在回来之前我无法发表评论。我还有一个关于你其他问题的想法,但由于你还没有发布可编译的SSCCE,所以我不打算花时间让你的代码编译。 - camickr
7个回答

3

我看到这里有很多过于复杂的答案(自定义UI,额外的监听器,图形等)。

基本上,camickr已经为你指明了方向。这是一个简单的演示:

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

public class JTabbedPaneCursorDemo implements Runnable
{
  JTabbedPane tabbedPane;

  public static void main(String[] args)
  {
    SwingUtilities.invokeLater(new JTabbedPaneCursorDemo());
  }

  public void run()
  {
    JPanel panelA = new JPanel();
    JPanel panelB = new JPanel();

    tabbedPane = new JTabbedPane();
    tabbedPane.addTab("A", panelA);
    tabbedPane.addTab("B", panelB);

    tabbedPane.addMouseMotionListener(new MouseMotionListener()
    {
      public void mouseDragged(MouseEvent e) {}

      public void mouseMoved(MouseEvent e)
      {
        adjustCursor(e);
      }
    });

    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 200);
    frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  private void adjustCursor(MouseEvent e)
  {
    TabbedPaneUI ui = tabbedPane.getUI();
    int index = ui.tabForCoordinate(tabbedPane, e.getX(), e.getY());

    if (index >= 0)
    {
      tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
    }
    else
    {
      tabbedPane.setCursor(null);
    }
  }
}

+1,这基本上就是我的建议,除了我会在监听器中使用JTabbedPane tabbedPane = (JTabbedPane)e.getSource();,这样你就不需要使用实例变量。 - camickr

2
我希望在鼠标移动到任何一个选项卡时能设置光标。
我猜您需要向选项卡窗格添加MouseMotionListener。然后,当生成mouseMoved(...)事件时,您可以检查鼠标是否在选项卡上。
您应该能够使用BasicTabbePaneUI的tabForCoordinate(...)方法来确定鼠标是否在选项卡上。

抱歉,我该如何使用tabForCoordinate检测鼠标是否在选项卡上?你能给一个代码示例吗? - Airy
@AbdulJabbarWebBestow,我不明白这个问题?你可以使用MouseEvent中的鼠标指针并调用该方法。 - camickr
Camickr建议我使用tabForCoordinate(...)来确定鼠标是否在选项卡上,tabForCoordinate(...)会给出鼠标的坐标,那么如何检测鼠标是否在选项卡上呢?请问您能否提供一些代码示例,以便我理解您的意思? - Airy
3
tabForCoordinate(...)提供鼠标所在位置的标签页,而不是坐标。你有读过API吗?如果需要更多帮助,请发布 SSCCE 并展示您如何尝试使用此方法。 - camickr
3
@AbdulJabbarWebBestow,我不在的5天里,你仍然没有使用我提出的建议发布一个合适的SSCCE。我想你对一个答案不感兴趣。为什么你希望我们所有人都为你发布代码,而你甚至不愿意为我们发布代码呢?你是一个寻求帮助的人,所以你应该努力一下。因果报应,你没有给予我们任何东西,所以我们也无法给予你帮助。 - camickr
显示剩余4条评论

0

步骤:

  • 创建一个MouseMotionListener并将其添加到您的JTabbedPane
  • 在监听器内部-> mouseMoved方法中,检查鼠标当前位置是否在选项卡的边界内
    • 如果是,则将光标更改为手形光标
    • 否则显示默认光标

1.检查鼠标是否在选项卡边界内的方法:

private static int findTabPaneIndex(Point p, JTabbedPane tabbedPane) {
    for (int i = 0; i < tabbedPane.getTabCount(); i++) {
        if (tabbedPane.getBoundsAt(i).contains(p.x, p.y)) {
            return i;
        }
    }
    return -1;
}

2.鼠标监听器:

    MouseMotionListener listener = new MouseMotionAdapter() {
        public void mouseMoved(MouseEvent e) {
            JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
            if (findTabPaneIndex(e.getPoint(), tabbedPane) > -1) {
                tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
            } else {
                tabbedPane.setCursor(new Cursor((Cursor.DEFAULT_CURSOR)));
            }
        }
    };

3. 将监听器添加到 JTabbedPane:

    jtpane.addMouseMotionListener(listener);

相关文档:

最终代码:

将所有部分组合在一起,您将得到以下代码:

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

public class HAAMS {
    // My Custom class for jTabbedPane
    public static class CustomMainMenuTabs extends BasicTabbedPaneUI {
        protected void paintTabBackground(Graphics g, int tabPlacement,
                int tabIndex, int x, int y, int w, int h, boolean isSelected) {
            Graphics2D g2 = (Graphics2D) g;
            Color color;
            if (isSelected) {
                color = new Color(74, 175, 211);
            } else if (getRolloverTab() == tabIndex) {
                color = new Color(45, 145, 180);
            } else {
                color = new Color(68, 67, 67);
            }
            g2.setPaint(color);
            g2.fill(new RoundRectangle2D.Double(x, y, w, h, 30, 30));
            g2.fill(new Rectangle2D.Double(x + 100, y, w, h));
        }
    }

    public static void main(String[] args) {
        JFrame MainScreen = new JFrame("Custom JTabbedPane");
        MainScreen.setExtendedState(MainScreen.getExtendedState()
                | JFrame.MAXIMIZED_BOTH);
        JTabbedPane jtpane = new JTabbedPane(2);
        jtpane.setUI(new CustomMainMenuTabs());
        MouseMotionListener listener = new MouseMotionAdapter() {
            public void mouseMoved(MouseEvent e) {
                JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
                if (findTabPaneIndex(e.getPoint(), tabbedPane) > -1) {
                    tabbedPane.setCursor(new Cursor((Cursor.HAND_CURSOR)));
                } else {
                    tabbedPane.setCursor(new Cursor((Cursor.DEFAULT_CURSOR)));
                }
            }
        };
        jtpane.add("1st Tabe", new JPanel());
        jtpane.add("2nd Tabe", new JPanel());
        jtpane.add("3rd Tabe", new JPanel());
        jtpane.addMouseMotionListener(listener);
        MainScreen.getContentPane().add(jtpane);
        MainScreen.setVisible(true);
    }

    private static int findTabPaneIndex(Point p, JTabbedPane tabbedPane) {
        for (int i = 0; i < tabbedPane.getTabCount(); i++) {
            if (tabbedPane.getBoundsAt(i).contains(p.x, p.y)) {
                return i;
            }
        }
        return -1;
    }
}

我已经尝试了你建议的代码,但是问题在于即使光标已经从选项卡标题移动到其面板,它仍然保持为HAND_CURSOR。 - Airy
你可以尝试将鼠标移动到选项卡上并单击它。然后,当你将鼠标移动到选项卡面板内部时,你会发现它仍然是HAND_CURSOR。这就是我在问题的最后两行中所问的。 - Airy
只有当光标在选项卡标题中而不是其面板或任何其他组件中时,我才希望将光标更改为HAND_CURSOR。 - Airy

0

您可以使用:

public void setTabComponentAt(int index,
                     Component component)

然后你这样做

component.addMouseListener(yourListener)

正如对无限递归所说,这种方式将光标设置在整个jtabbedpane上,而不仅仅是选项卡头部。 - Airy
不行,因为你只为选项卡设置了一个新组件。而且你在新组件上设置了监听器,例如标签。 - Alexander Campos
这个无法按预期工作,因为MouseEvents源将是选项卡的渲染器组件,当鼠标悬停在选项卡上时,不会传递鼠标事件到选项卡面板。因此,由于这个鼠标监听器覆盖了默认行为,当鼠标被点击时,选项卡面板不会在选项卡之间切换。 - dic19
如果发生这种情况,您可以使用MouseListener模拟标签中的选项卡单击(假设我们放置的组件是标签),从而实现选项卡行为。 - Alexander Campos
不,这不是一个标签,而是纯粹的选项卡头和选项卡面板。 - Airy

0

我已根据您的需求更改了主方法,使手形光标仅在选项卡标题上可见。请检查是否解决了您的问题。

工作代码

public static void main(String[] args)
{
    JFrame MainScreen = new JFrame("Custom JTabbedPane");
    MainScreen.setExtendedState(MainScreen.getExtendedState() | JFrame.MAXIMIZED_BOTH);

    MouseListener listener = new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            JTabbedPane jp=(JTabbedPane)(e.getComponent().getParent().getParent());
            jp.setSelectedIndex(jp.indexAtLocation(e.getComponent().getX(),e.getComponent().getY()));
       }

        @Override
        public void mouseEntered(MouseEvent e) {
            e.getComponent().setCursor(new Cursor((Cursor.HAND_CURSOR)));
        }


    };

    JLabel jlabel1=new JLabel("1st Tabe");
    jlabel1.addMouseListener(listener);
    JLabel jlabel2=new JLabel("2nd Tabe");
   jlabel2.addMouseListener(listener);
    JLabel jlabel3=new JLabel("3rd Tabe");
   jlabel3.addMouseListener(listener);

    //Setting UI for my jTabbedPane implementing my custom class CustomMainMenuTabs
    JTabbedPane jtpane = new JTabbedPane(2);

   jtpane.setUI(new CustomMainMenuTabs());
    jtpane.add("1st Tabe", new JPanel());
    jtpane.setTabComponentAt( 0, jlabel1);
    jtpane.add("2nd  Tabe", new JPanel());
    jtpane.setTabComponentAt(1, jlabel2);
    jtpane.add("3rd  Tabe", new JPanel());
    jtpane.setTabComponentAt( 2, jlabel3);




    MainScreen.getContentPane().add(jtpane);
    MainScreen.setVisible(true);
}

不,那也没什么帮助。 - Airy
能否提供详细信息出了什么问题..我会尝试改进代码。 - VaibJ
正如所说,我已经为TabbedPane创建了自己的自定义L&F类,我的jtabbedPane实现了该类,我的自定义L&F类有一个适用于jTabbedPane的长代码,因此我不能再为JLabels做所有工作。请注意,我希望它在选项卡标题本身上工作,而不使用jlabels,因为这样我将不得不在l&F中为jLabels做很多工作。 - Airy
我认为如果您可以控制选项卡UI,则在鼠标移动监听器上,比较鼠标移动到的像素颜色,并且由于选项卡标题已定义颜色,请进行比较并相应更改光标。 - VaibJ
颜色比较不是一个好的解决方案,所以我考虑比较选项卡的x和y坐标,但这也不起作用。 - Airy

0

简短

只需将此代码添加到您的CustomMainMenuTabs中:

  public static class CustomMainMenuTabs extends BasicTabbedPaneUI
  {
    protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected)
    {
    // ...
    }
    private static final Cursor DEFAULT_CURSOR = Cursor.getDefaultCursor();
    private static final Cursor HAND_CURSOR = new Cursor((Cursor.HAND_CURSOR));
    protected void setRolloverTab(int index) {
        tabPane.setCursor((index != -1) ? HAND_CURSOR : DEFAULT_CURSOR);
        super.setRolloverTab(index);
    }
  }

解释

由于您已经扩展了BasicTabbedPaneUI,因此您可以简单地扩展绘制悬停选项卡的机制,该机制已在其中实现,无需使用更多的侦听器或自己计算坐标。

滚动是自Java 5以来存在于组件中的一种机制,这是一个适当的扩展,只需要覆盖和扩展该方法。每当鼠标在选项卡组件中移动时(它影响选项卡区域但不影响子元素),就会调用此方法,并且保持更新。

我已经尝试过您的代码片段并添加了这个功能,效果很好。


0

实际上,安装自定义UI委托要比这容易得多。

您可以将自己的标签安装为选项卡组件(选项卡句柄内部的组件),这些标签将具有自己的光标。以下是一个简单的示例,其中包含3个选项卡,以及选项卡面板和每个选项卡正文的不同光标:

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

public class TestTabCursor extends JFrame {

    private JTabbedPane contentPane;

    public TestTabCursor() {
        super("Test tab cursor");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(640, 480);
        setLocation(100, 100);
        createContentPane();        
        setCursors();
    }

    private void createContentPane() {
        contentPane = new JTabbedPane();

        addTab(contentPane);
        addTab(contentPane);
        addTab(contentPane);

        setContentPane(contentPane);
    }

    private void addTab(JTabbedPane tabbedPane) {
        int index = tabbedPane.getTabCount() + 1;

        JLabel label = new JLabel("Panel #" + index);
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setFont(label.getFont().deriveFont(72f));

        JPanel panel = new JPanel(new BorderLayout());
        panel.setBackground(Color.white);
        panel.add(label, BorderLayout.CENTER);

        JLabel title = new JLabel("Tab " + index);

        tabbedPane.add(panel);
        tabbedPane.setTabComponentAt(index - 1, title);
    }

    private void setCursors() {
        contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

        contentPane.getTabComponentAt(0).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        contentPane.getTabComponentAt(1).setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        contentPane.getTabComponentAt(2).setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));     
    }

    public static void main(String... args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new TestTabCursor();
                frame.setVisible(true);
            }
        });
    }
}

为什么要点踩?这是可行的代码,解决了问题。 - ethanfar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接