路地裏牧場

技術系ブログだったけどごった煮になった謎ブログ

Javatterβでショートカットキー

別にJavatterβじゃなくてもいいです。

メソッド

public void addShortCut(int keyCode, int modifies, ActionListener... listeners) {
    JFrame frame = getMainView().getMainFrame();
    JMenuBar menuBar;
    if ((menuBar = frame.getJMenuBar()) == null) {
        menuBar = new JMenuBar();
        menuBar.setPreferredSize(new Dimension(0, 0));
        frame.setJMenuBar(menuBar);
    }
    JMenu menu;
    if (menuBar.getMenuCount() < 1) {
        menu = new JMenu();
        menuBar.add(menu);
    } else {
        menu = menuBar.getMenu(0);
    }
    JMenuItem item = new JMenuItem();
    item.setAccelerator(KeyStroke.getKeyStroke(keyCode, modifies));
    for (ActionListener listener : listeners)
        item.addActionListener(listener);
    menu.add(item);
}

使用

// Ctrl+Enter
addShortCut(KeyEvent.VK_Enter, KeyEvent.KeyEvent.CTRL_DOWN_MASK, listener0);
// A (non-modifies key)
addShortCut(KeyEvent.VK_A, 0, listener1);

Swingでショートカットキー

ソースコードを見て察してください。

JMenuBar menuBar = new JMenuBar();
menuBar.setPreferredSize(new Dimension(0, 0));
JMenu menu1 = new JMenu("File");
JMenuItem item1 = new JMenuItem("Open");
item1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, KeyEvent.CTRL_DOWN_MASK));
item1.addActionListener(new ActionListener() {
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("ok");
    }
});
menu1.add(item1);
menuBar.add(menu1);
dialog.setJMenuBar(menuBar);

JMenuが必要なら項目のサイズでも変更しとけばいいんじゃないですかね(適当)

Java switchの各caseのスコープ

条件分岐に使うswitch文。
Java7で文字列にも対応してより一層使い所が増えました。

が、こんな落とし穴が。

間違った例

switch (value) {
    case "yes":
        int val = 0;
        resultModel.setValue(val);
        logger.info("val" + val);
        break;
    case "no":
        int val = 1;
        resultModel.setValue(val);
        logger.info("val" + val);
        break;
    case "cancel":
        logger.info("cancelled");
        break;
    default:
        logger.info("invalid value" + value);
        break;
}

実はこれ、エラーが出ます。

case "yes":
    int val = 0; // これ

case "no":
    int val = 1; // これ

が競合してる、というのです。
ぱっと見では違うスコープなので競合は起こらないように見えます。
しかも競合してるくせしてno側ではyes側のvalは使えないのです。なんじゃそりゃ。

解決策

switch (value) {
    case "yes": {
        int val = 0;
        resultModel.setValue(val);
        logger.info("val" + val);
        break;
    }
    case "no": {
        int val = 1;
        resultModel.setValue(val);
        logger.info("val" + val);
        break;
    }
    case "cancel": {
        logger.info("cancelled");
        break;
    }
    default: {
        logger.info("invalid value" + value);
        break;
    }
}

( ^ω^)こうじゃ

これで別のスコープと判断され、競合は起こらなくなります。
非常に面倒です。
スコープは波括弧内、というのは基本中の基本なのですが忘れがちです。気をつけましょう。

やめてそんなこともわからないのかハゲって言いながら石を投げないで

参考:java - Variable's scope in a switch case - Stack Overflow

JavatterFXでウィンドウ閉じてもVMが終了しない問題あれこれ

JavatterFXの終了処理はウィンドウを閉じた時にJavaFXのスレッドを終了させるだけのものなので、Swingとかでウィンドウを表示させてるとスレッドが残り続けて、VMが終了しません。
ウィンドウが一回限りの表示なら

setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

でOKなのですが、再利用する場合はそうもいきません。
なのでメインウィンドウの終了イベントにハンドラをぶち込んでMainWindowCloseEventを発火させました。
このイベントは自作です。発火時のDateのみを持ちます。

メインクラス
EventHandler<WindowEvent> handler = new EventHandler<WindowEvent>() {
    @Override
    public void handle(WindowEvent windowEvent) {
        Platform.exit();
        windowEvent.consume();
        EventManager.INSTANCE.eventFire(new MainWindowCloseEvent());
    }
};
Main.getStage().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, handler);
Windowを継承したクラス
@EventHandler
public void onWindowClose(MainWindowCloseEvent event) {
    this.dispose();
}

JavaFX Image to Swing Image

というおはなし。
ちょっとググったらすぐ見つかったので備忘録として。
変換するメソッドを置いとくのでご自由にどうぞどうぞ。

/**
 * JavaFXのImageをSwingのImageIconに変換します.
 * @param fximage JavaFXのImage
 * @return SwingのImageIcon
 */
public static ImageIcon fromFXImage(javafx.scene.image.Image fximage) {
    BufferedImage icon = SwingFXUtils.fromFXImage(fximage, null);
    return new ImageIcon(icon);
}