如何使用键盘滚动 JPanel,而其他组件具有焦点?

问题描述

我一直在寻找解决方案,我认为这是 Java Swing 非常常见的问题,但找不到具体的答案。

我有这个 Swing JPanel,顶部有一个可滚动的 JPanel显示文档的图像。

下面有更多的 Swing 组件,例如另一个面板中的 JTable 和一些文本框、JButton 控件等。 (同一个 JWindow 中有更多面板。)

用户在下面的框中编辑数据等时,这些当然会获得应有的关注。现在,当用户想要滚动文档中的某些特定数据时,他/她需要先将焦点(使用鼠标/选项卡)设置在其上,然后再滚动。

所以用户想使用例如向上/向下箭头键盘,而不会离开他正在使用的其他组件的焦点。

我已经尝试设置键盘处理程序,但如果不将焦点移到顶部就无法使其工作。

如果没有这个组件(和另一个)有焦点,就没有办法滚动可滚动的 Swing 组件吗?

解决方法

如果组件是滚动窗格中的文本字段,似乎 Page Down 可以在(至少第一个)文本字段获得焦点时起作用。这是运行此类代码并多次点击 Page Down 的结果。

enter image description here

如果在聚焦滚动窗格外部的组件时需要此行为,请查看key bindings

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

public class KeyboardScroll {

    private JComponent ui = null;

    KeyboardScroll() {
        initUI();
    }

    public void initUI() {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new BorderLayout(4,4));
        ui.setBorder(new EmptyBorder(4,4,4));

        JPanel textPanel = new JPanel(new GridLayout(0,1));
        for (int ii = 1; ii < 101; ii++) {
            textPanel.add(new JTextField("Field " + ii,30));
        }
        JScrollPane scrollPane = new JScrollPane(textPanel,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        ui.add(scrollPane);
        Dimension d = textPanel.getPreferredSize();
        scrollPane.getViewport().setPreferredSize(
                new Dimension((int)d.getWidth(),100));
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(
                            UIManager.getSystemLookAndFeelClassName());
                } catch (Exception useDefault) {
                }
                KeyboardScroll o = new KeyboardScroll();

                JFrame f = new JFrame("Use 'Page Down'");
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                f.setLocationByPlatform(true);

                f.setContentPane(o.getUI());
                f.pack();
                f.setMinimumSize(f.getSize());

                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}
,

如果没有这个组件(和另一个)有焦点,就没有办法滚动可滚动的 Swing 组件吗?

那么一般的解决方案是使用Key Bindings

然而,问题在于每个组件都可以为 up/down 事件实现 Key Bindings 本身,在这种情况下,会调用该组件的相关 Action

本教程演示了如何从组件中删除键绑定。

一个 JTable 和一些文本框

所以这些是实现向上/向下键绑定的组件示例。

JButton 控件

是没有默认向上/向下键绑定的组件的示例。

因此您可以使用如下代码添加自己的代码:

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

public class SSCCE extends JPanel
{
    public SSCCE()
    {
        setLayout( new BorderLayout() );

        JPanel top = new JPanel();
        top.add( new JTextField(10) );
        top.add( new JButton("Button") );
        add(top,BorderLayout.PAGE_START);

        JLabel label = new JLabel( new ImageIcon("mong.jpg") );
        JScrollPane scrollPane = new JScrollPane( label );
        add(scrollPane,BorderLayout.CENTER);

        JScrollBar scrollBar = scrollPane.getVerticalScrollBar();

        InputMap im = getInputMap(JScrollBar.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        ActionMap am = getActionMap();
        im.put(KeyStroke.getKeyStroke("pressed UP"),"up");
        am.put("up",new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                scrollBar.setValue(scrollBar.getValue() - scrollBar.getUnitIncrement(-1));
            }
        });

        im.put(KeyStroke.getKeyStroke("pressed DOWN"),"down");
        am.put("down",new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                scrollBar.setValue(scrollBar.getValue() + scrollBar.getUnitIncrement(1));
            }
        });

    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SSCCE());
        frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

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

在上面的例子中,当焦点在按钮上时图像会滚动,而不是在文本字段上。