読者です 読者をやめる 読者になる 読者になる

キーボードショートカットを作る(Java)

こんな感じのやつ

f:id:voidy21:20091229182832p:image
キーボードのjかkで上下に移動させるやつを実装する

何も考えずにKeyListenerを使ってやってみる

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
 
@SuppressWarnings( "serial" )
public class TreeSample extends JFrame implements KeyListener {
 
  public TreeSample(MutableTreeNode treeNode) {
    JTree tree = new JTree(treeNode);
    add(tree);
    tree.addKeyListener(this);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setTitle("新世界");
    setBounds(100, 100, 250, 200);
    setVisible(true);
  }
 
  public static void main(String[] args) {
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("世界の国々");
    for ( String child : new String[]{ "日本", "アメリカ", "ドイツ", "おそロシア" } )
      root.add(new DefaultMutableTreeNode(child));
 
    for ( String leaf : new String[]{ "アメリカ村", "ドイツ村" } )
      root.getNextNode().add(new DefaultMutableTreeNode(leaf));
 
    new TreeSample(root);
  }
 
  /**
  * jまたはkが押されたらtreeの選択を移動する
  * @param e
  */
  @Override
  public void keyTyped(KeyEvent e) {
    if( tree.isSelectionEmpty() ) return;
 
    char getKey = e.getKeyChar();
    int index = tree.getSelectionRows()[0];
    switch ( getKey ) {
      case 'j': {
        if ( index != tree.getRowCount() - 1 )
          tree.setSelectionRow(++index);
        break;
      }
      case 'k': {
        if ( index != 0 )
          tree.setSelectionRow(--index);
        break;
      }
    }
  }
 
  @Override
  public void keyPressed(KeyEvent e) {
  }
 
  @Override
  public void keyReleased(KeyEvent e) {
  }
}

結構めんどくさい!

上のコードと等価なやつをActionMapとInputMapを使って書いてみる

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
 
@SuppressWarnings( "serial" )
public class TreeSample extends JFrame {

  public TreeSample(MutableTreeNode treeNode) {
    JTree tree = new JTree(treeNode);
    add(tree);
 
    Action treeDown = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        int index = tree.getSelectionRows()[0];
        if ( index != tree.getRowCount() - 1 )
          tree.setSelectionRow(++index);
      }
    };
 
    Action treeUp = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        int index = tree.getSelectionRows()[0];
        if ( index != 0 )
          tree.setSelectionRow(--index);
      }
    };
    ActionMap amc = tree.getActionMap();
    amc.put("treeUp",treeUp);
    amc.put("treeDown", treeDown);
    InputMap imc = tree.getInputMap();
    imc.put(KeyStroke.getKeyStroke('j'), "treeDown");
    imc.put(KeyStroke.getKeyStroke('k'), "treeUp");
 
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setTitle("新世界");
    setBounds(100, 100, 250, 200);
    setVisible(true);
  }
 
  public static void main(String[] args) {
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("世界の国々");
    for ( String child : new String[]{ "日本", "アメリカ", "ドイツ", "おそロシア" } )
      root.add(new DefaultMutableTreeNode(child));
 
    for ( String leaf : new String[]{ "アメリカ村", "ドイツ村" } )
      root.getNextNode().add(new DefaultMutableTreeNode(leaf));
 
    new TreeSample(root);
  }
}

ちょっとだけ短くなった
でも、キーボードの↑ボタンとかを押しても移動できるみたいなので、
今度はその挙動をパクってみます

KeyStrokeを使ってキーボードの↑ボタンと↓ボタンの挙動をそっくりそのまま拝借してみる

import java.awt.event.KeyEvent;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
 
@SuppressWarnings( "serial" )
public class TreeSample extends JFrame {
 
  public TreeSample(MutableTreeNode treeNode) {
    JTree tree = new JTree(treeNode);
    add(tree);
    //↓キーと↑キーの挙動を取得する
    KeyStroke downKey = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
    KeyStroke upKey = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
    InputMap imc = tree.getInputMap();
    imc.put(KeyStroke.getKeyStroke('j'), imc.get(downKey));
    imc.put(KeyStroke.getKeyStroke('k'), imc.get(upKey));
    
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setTitle("新世界");
    setBounds(100, 100, 250, 200);
    setVisible(true);
  }
 
  public static void main(String[] args) {
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("世界の国々");
    for ( String child : new String[]{ "日本", "アメリカ", "ドイツ", "おそロシア" } )
      root.add(new DefaultMutableTreeNode(child));
 
    for ( String leaf : new String[]{ "アメリカ村", "ドイツ村" } )
      root.getNextNode().add(new DefaultMutableTreeNode(leaf));
 
    new TreeSample(root);
  }
}

結構短くなった!
もともとどんなキーが登録されているかは

System.out.println(java.util.Arrays.toString(tree.getRegisteredKeyStrokes()));

をすれば見えるはず

最後に

ActionMapとInputMapとKeyStrokeは結構便利なんじゃないかと思った