JScrollPane scrolling with arrow keys
I've a JTextArea component inside JScrollPane and the text area is not editable. I would like to enable scrolling of the text area with up and down arrow keys (i.e. 开发者_开发百科pressing the arrow keys will scroll the text area by one line). Any ideas how to achieve this?
Yes Key Bindings is the way to go, but you don't always need to create your own actions. Swing components come with default Actions that you can often reuse.
See Key Bindings for a complete list of these Actions.
Now that you know the Action name you can just bind it to a keyStroke:
JScrollBar vertical = scrollPane.getVerticalScrollBar();
InputMap im = vertical.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke("DOWN"), "positiveUnitIncrement");
im.put(KeyStroke.getKeyStroke("UP"), "negativeUnitIncrement");
If the JTextArea is non-editable and non-focuseable, it will not respond to the arrow keys. I'm not sure if there is a canonical way to get around this, but one way to make it respond is to set its key binding to respond to the up and down keys when the JTextArea is in the focusable window. An example of this is as follows:
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.text.JTextComponent;
@SuppressWarnings("serial")
public class TestScrollingArea extends JPanel {
private static final String UP = "Up";
private static final String DOWN = "Down";
private JTextArea area = new JTextArea(20, 40);
private JScrollPane scrollPane = new JScrollPane(area);
public TestScrollingArea() {
// make textarea non-editable and non-focusable
area.setEditable(false);
area.setFocusable(false);
area.setWrapStyleWord(true);
area.setLineWrap(true);
add(scrollPane);
// fill area with letters
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 100; j++) {
area.append("abcdefg ");
}
}
// have JTextArea tell us how tall a line of text is.
int scrollableIncrement = area.getScrollableUnitIncrement(scrollPane.getVisibleRect(),
SwingConstants.VERTICAL, 1);
// add key bindings to the JTextArea
int condition = JTextComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inMap = area.getInputMap(condition);
ActionMap actMap = area.getActionMap();
inMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), UP);
inMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), DOWN);
actMap.put(UP, new UpDownAction(UP, scrollPane.getVerticalScrollBar().getModel(),
scrollableIncrement));
actMap.put(DOWN, new UpDownAction(DOWN, scrollPane.getVerticalScrollBar().getModel(),
scrollableIncrement));
}
// Action for our key binding to perform when bound event occurs
private class UpDownAction extends AbstractAction {
private BoundedRangeModel vScrollBarModel;
private int scrollableIncrement;
public UpDownAction(String name, BoundedRangeModel model, int scrollableIncrement) {
super(name);
this.vScrollBarModel = model;
this.scrollableIncrement = scrollableIncrement;
}
@Override
public void actionPerformed(ActionEvent ae) {
String name = getValue(AbstractAction.NAME).toString();
int value = vScrollBarModel.getValue();
if (name.equals(UP)) {
value -= scrollableIncrement;
vScrollBarModel.setValue(value);
} else if (name.equals(DOWN)) {
value += scrollableIncrement;
vScrollBarModel.setValue(value);
}
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("TestScrollingArea");
frame.getContentPane().add(new TestScrollingArea());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Just came across this problem and while the answers was useful in driving me to the right direction some bits of the solution may have changed since then. It worked for me with he following changes: - it was the InputMap of JScrollPane instance that had to be changed - actionMapKeys had to be: "unitScrollX" and/or "scrollX" (X= Down, Up, Left, Right). They reside in BasicScrollPaneUI.
You should add KeyListener to your JScrollPane.
All I had to do was to make the scroll pane request focus on mouse enter (as explained in this answer).
var scrollPane = new JScrollBar(jPanelCanvas);
scrollPane.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
// this seems to enable key navigation
if ((e.getComponent() instanceof JScrollPane)) {
e.getComponent().requestFocus();
}
}
});
However I'm not sure on how to tweak the actions of these keys. Maybe by tweaking the actions on the JScrollPane
directly as mentioned by tinca's answer.
The call to scrollPane.getActionMap()
are showing the following actions defined
"unitScrollRight" -> {BasicScrollPaneUI$Actions@4310}
"unitScrollDown" -> {BasicScrollPaneUI$Actions@4312}
"scrollDown" -> {BasicScrollPaneUI$Actions@4314}
"scrollHome" -> {BasicScrollPaneUI$Actions@4316}
"scrollRight" -> {BasicScrollPaneUI$Actions@4318}
"scrollUp" -> {BasicScrollPaneUI$Actions@4320}
"unitScrollLeft" -> {BasicScrollPaneUI$Actions@4322}
"unitScrollUp" -> {BasicScrollPaneUI$Actions@4324}
"scrollEnd" -> {BasicScrollPaneUI$Actions@4326}
"scrollLeft" -> {BasicScrollPaneUI$Actions@4328}
精彩评论