java - 在滚动条的拇指上注册监听器

标签 java swing jscrollpane mouselistener jscrollbar

我正在使用一个滚动 Pane 集来使用自定义滚动条,除此之外,我想在滚动条的拇指上安装一个监听器,以便每当鼠标进入拇指区域时,它都可以更改颜色或边框。我搜索了 BasicScrollBarUI(由我的自定义滚动条 UI 扩展)并找到了 installListeners() 方法,因此我覆盖了它并让它为拇指区域再调用一个监听器。

SSCCE:

public class TestScrollBar extends JFrame {

    public TestScrollBar() {

        JPanel innerPanel = new JPanel();
        innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));
        for (int i=1; i<=10; i++) {
            innerPanel.add(new JLabel("Label "+i));
            innerPanel.add(Box.createRigidArea(new Dimension(0, 20)));
        }

        JScrollPane scrollPane = new JScrollPane(innerPanel);
        scrollPane.setPreferredSize(new Dimension(innerPanel.getPreferredSize().width, innerPanel.getPreferredSize().height/2));

        scrollPane.getVerticalScrollBar().setUI(new CustomScrollBarUI());

        this.setLayout(new BorderLayout());
        this.add(Box.createRigidArea(new Dimension(0, 20)), BorderLayout.NORTH);
        this.add(Box.createRigidArea(new Dimension(0, 20)), BorderLayout.SOUTH);
        this.add(Box.createRigidArea(new Dimension(20, 0)), BorderLayout.EAST);
        this.add(Box.createRigidArea(new Dimension(20, 0)), BorderLayout.WEST);
        this.add(scrollPane, BorderLayout.CENTER);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                TestScrollBar frame = new TestScrollBar();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

还有一个简单的滚动条自定义 UI:

public class CustomScrollBarUI extends BasicScrollBarUI {

    @Override
    protected void installListeners() {
        super.installListeners();
        scrollbar.addMouseListener(new CustomListener());
    }

    protected class CustomListener extends MouseAdapter {
        public void mouseEntered(MouseEvent e) {
            if (getThumbBounds().contains(e.getX(), e.getY())) {
                System.out.println("THUMB");
            }
        }
    }
}

当鼠标从右侧或左侧进入拇指区域时,监听器工作正常,但它并不总是适用于顶部或底部。对于这些侧面,只有当拇指触摸其中一个按钮时,它才起作用。例如,当滚动条位于最顶部时,监听器仅在鼠标从顶部按钮进入拇指区域而不是从下方进入拇指区域时才起作用(左右两侧除外)。当滚动条触摸减少按钮时,会发生相反的情况(仅适用于右/左/下侧,但不适用于顶部)。当拇指位于两者之间且没有触摸任何按钮时,则只有左右两侧可以工作。如果运行代码,您可以看到这一切。

我还尝试使用现有监听器之一(而不是创建自己的监听器)BasicScrollBarUI.TrackListener,其中我添加了一种仅监听拇指区域的方法,但结果完全相同:

public class CustomScrollBarUI extends BasicScrollBarUI {
    @Override
    protected TrackListener createTrackListener(){
        return new TrackListener();
    }

    protected class TrackListener extends BasicScrollBarUI.TrackListener {
        public void mouseEntered(MouseEvent e) {
            currentMouseX = e.getX();
            currentMouseY = e.getY();
            if (getThumbBounds().contains(currentMouseX, currentMouseY)) {
                System.out.println("THUMB");
            }
        }
    }
}

最佳答案

问题在于您监听的是滚动条,这意味着它是 Thumb 以及 Thumb 可以移动的区域(轨道)。因此, mouseEntered 方法中的测试条件并不总是正确。例如,假设拇指位于垂直滚动条的顶部,从底部进入意味着您将首先进入滚动条轨道,然后将鼠标移动到拇指区域 --> 没有鼠标进入事件。

这是您的代码的稍加修改的版本。它的主要作用是监听鼠标“移动”事件(通过将监听器添加为 MouseMotionListener)并且它按预期工作:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicScrollBarUI;

public class TestScrollBar extends JFrame {

    public static class CustomScrollBarUI extends BasicScrollBarUI {

        @Override
        protected void installListeners() {
            super.installListeners();
            CustomListener listener = new CustomListener();
            scrollbar.addMouseListener(listener);
            scrollbar.addMouseMotionListener(listener);
        }

        protected class CustomListener extends MouseAdapter {
            boolean isInsideThumb = false;

            @Override
            public void mouseEntered(MouseEvent e) {
                handleMouseEvent(e);
            }

            @Override
            public void mouseMoved(MouseEvent e) {
                handleMouseEvent(e);
            }

            @Override
            public void mouseExited(MouseEvent e) {
                handleMouseEvent(e);
            }

            private void handleMouseEvent(MouseEvent e) {
                if (getThumbBounds().contains(e.getX(), e.getY())) {
                    if (!isInsideThumb) {
                        System.out.println("THUMB");
                        isInsideThumb = true;
                    }
                } else {
                    if (isInsideThumb) {
                        System.out.println("OUT OF THUMB");
                        isInsideThumb = false;
                    }
                }
            }
        }
    }

    public TestScrollBar() {

        JPanel innerPanel = new JPanel();
        innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));
        for (int i = 1; i <= 10; i++) {
            innerPanel.add(new JLabel("Label " + i));
            innerPanel.add(Box.createRigidArea(new Dimension(0, 20)));
        }

        JScrollPane scrollPane = new JScrollPane(innerPanel);
        scrollPane.setPreferredSize(new Dimension(innerPanel.getPreferredSize().width, innerPanel.getPreferredSize().height / 2));

        scrollPane.getVerticalScrollBar().setUI(new CustomScrollBarUI());

        this.setLayout(new BorderLayout());
        this.add(Box.createRigidArea(new Dimension(0, 20)), BorderLayout.NORTH);
        this.add(Box.createRigidArea(new Dimension(0, 20)), BorderLayout.SOUTH);
        this.add(Box.createRigidArea(new Dimension(20, 0)), BorderLayout.EAST);
        this.add(Box.createRigidArea(new Dimension(20, 0)), BorderLayout.WEST);
        this.add(scrollPane, BorderLayout.CENTER);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                TestScrollBar frame = new TestScrollBar();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

关于java - 在滚动条的拇指上注册监听器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12394269/

相关文章:

java - 如果两个类扩展不同的类,避免重复代码的最佳方法

java - 使用 loadClass 方法加载数组

java - 如何改变TextPane的大小?

java - 如何使 JPanel 可滚动?

java - JScrollPane 在 JPanel 内不起作用

java - Selenium 无法通过 moveToElement 在 IE 中单击正确的目标,但可以在 Chrome 中使用

java - ArrayList 未正确更新

java - 多个视口(viewport)到同一个 JTextArea 上?

Java - 将列和数据添加到现有 JTable 中

java - JScrollPane 中的 JList