« ◆新聞、雑誌の束ね方、結び方、縛り方 | トップページ | ◇ブラックホールに光速で落ち込む物質はブラックホールより重くなるか? »

▼Swingでボタンイベントを"CLICK"から"DOWN"に変える

JAVAのGUIライブラリSwingにはボタン'JButton'があります。ところがこのボタンは

  • 押しても機能せず
  • 離すと機能する
という馬鹿げたものとなってます。(残念なことにWEBなど、多くの システムも同様の愚か極まりない仕様を採用しています)

沢山のボタンをすばやく操作しようとすると、押したあと離すまで の間にマウスが動いてしまい、うまく機能しないことが良くあります。

これを「押すと機能する」という当たり前のボタンにします。

(以前の記事◇クリック廃止論のアプリケーション側での実践法のJAVA版です)

 JButtonの派生クラスを作る

既存のアプリケーションを簡単に「押したら動く」ボタン化する方法を考えます。
JButtonの派生クラスを作り、JButtonの代わりにそのクラスを使う形に変える以外 の変更が無い様にします。

JButtonは外部インターフェースとしては「ボタンを押した」ことを無視しますが、 内部的には「ボタンを押した」イベントと「ボタンを離した」イベントを取得して います。

そこで、作成する派生クラスでは

  • 内部的に検出される「ボタンを押した」イベント に対し「ボタンを離した」というイベントを擬似的に発生させる
ようにします。

オーバーロードする関数はマウスのボタンイベントを処理するproseccMouseEvent()です。
構築子は継承されませんので全ての構築子を作ります。

コード(HiJButton.java)を示します。

// 押せば機能するSwingのJButton派生
//package xx.yy.zz.hi;
import javax.swing.JButton;
import javax.swing.Action;
import javax.swing.Icon;
import java.awt.event.MouseEvent;
/**
 * 押せば機能するSwingのJButton派生クラス<br>
 * インターフェース詳細はJButtonを参照のこと
 */
public class HiJButton extends javax.swing.JButton {
   //----- 構築子群 ------
   HiJButton()                 { super(); }
   HiJButton(Action a_)        { super(a_); }
   HiJButton(Icon i_)          { super(i_); }
   HiJButton(String t_)        { super(t_); }
   HiJButton(String t_,Icon i_){ super(t_,i_); }
   //----- マウスボタンイベント処理
   protected void processMouseEvent(MouseEvent e_){
      if(   (e_.getID() != MouseEvent.MOUSE_RELEASED)
          ||(e_.getID() != MouseEvent.MOUSE_CLICKED ) ){
         // 不要となるイベント以外はベースクラスに通知
         super.processMouseEvent(e_);
         }
      if( e_.getID() == MouseEvent.MOUSE_PRESSED ){
         // 「押した」イベントで「離した」イベントを擬似発生
         super.processMouseEvent(
            new MouseEvent(
                e_.getComponent()
               ,MouseEvent.MOUSE_RELEASED
               ,e_.getWhen()
               ,e_.getModifiers()
               ,e_.getX()
               ,e_.getY()
               ,e_.getClickCount()
               ,e_.isPopupTrigger()
               )
            );
         }
      }
   };

アプリケーションの修正はJButtonの代わりにHiJButtonを使うように 変更するのみですみます。

 アプリケーションの例

「離すと動く」JButtonと「押すと動く」HiJButtonを使った単純な例を 示します。
ボタン2つと数字が並び、ボタンを押す/離すとビープ音を出し 数字がカウントupされます。
2種のボタンのクラス名が違う以外アプリケーション・コーディングは同じ であり置き換え可能です。

(プログラムは実機能部とmain部に分けてあります)
// BtnTstEngn.java:押すと動くボタンテスト、実機能クラス
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class BtnTstEngn implements ActionListener {
   int    count;
   JLabel label;
   public void actionPerformed(ActionEvent e){    // ボタンが機能したら
      label.setText(Integer.toString(++count));   //   カウントupする
      java.awt.Toolkit.getDefaultToolkit().beep();//   ビープ音を出す
      }
   BtnTstEngn(JPanel pane_){
      pane_.setLayout(new FlowLayout());
      JButton _btn;
      pane_.add(_btn = new JButton("click-button"));  // 「離す」ボタン
      _btn.addActionListener(this);
      pane_.add(_btn = new HiJButton("press-button"));// 「押す」ボタン
      _btn.addActionListener(this);
      pane_.add(label= new JLabel("0"));
      label.setPreferredSize(new Dimension(80,20));
      label.setHorizontalAlignment(JLabel.RIGHT);
      }
   }

// BtnTst:押すと動くボタンテストのmainプログラム
import javax.swing.*;
public class BtnTst extends JFrame {
   BtnTstEngn engn;
   BtnTst(String name_){
      super(name_);
      engn=new BtnTstEngn((JPanel)getContentPane());
      }
   public static void main(String[] args_){
      BtnTst _btnTst= new BtnTst("Button-test");
      _btnTst.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//必須!
      _btnTst.pack();           // 内部要素のサイズにする
      _btnTst.setVisible(true); // 表示する
      }
   }

Applet版の「mainプログラム」を以下に示します。(実機能部は前出の JAVA版と共通です)

// BtnTst:押すと動くボタンテストのAppletプログラム
import javax.swing.*;
public class BtnTstAplt extends JApplet {
   BtnTstEngn engn;
   public void init(){
      engn=new BtnTstEngn((JPanel)getContentPane());
      }
   }

 アプリケーション(Applet版)

Applet版を載せます。
単に押すだけだとあまり差が分からないかもしれませんが、 マウスをすばやく動かしながら押すと、「click-button」の 方は時々機能しないことがあるはずです。
また、「タン・タタン」といったリズムですばやく押すと、 「click-button」の不自然さが分かるはずです。

次をクリックするとApplet版が動きます。

Applet版起動
 

 TIPs

実際のプログラムは色々な要素が絡みます。
今回の本筋ではない小技を説明しておきます。

  • ウィンドウの[×]ボタン処理(必須)
    [X]ボタンでexitする処理を入れないと、javaが資源を 持ったままに画面から消えてしまう場合があります。
    通常、簡単なプログラムでは主画面のJFrameには次の設定 をする必要があります。
    (JFrame).setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
  • ウィンドウのサイズを数値指定しない方法
    setSize(横サイズ,縦サイズ)で指定する代わりにpack()を使えば ウィンドウの内部要素によって大きさが決まります。
    単純な試験プログラムの場合お勧めです。
    (JFrame).pack();
    
  • 試験用の単純レイアウトのFrameにする
    SwingのJFrameのデフォルトのレイアウトはBorderLayoutと なっています。
    BorderLayoutでは要素をどんどんと追加することはできず、 試験向きではありません。(本番向きでもありませんけど)
    試験用にはFlowLayoutを使います。
     (JPanel).setLayout(new FlowLayout());
    
  • JLabelの大きさ・右寄せ/左寄せ
    内容の変更があるJLabelは大きさを指定しておかないと、内容が 変わるたびに画面レイアウトが変わってしまいます。
    大きさは次のように設定します。
      Dimension d= new Dimension(横サイズ,縦サイズ);
      (JLabel).setPreferredSize(d); // これだけでよい場合もある
      (JLabel).setMaximumSize(d);
      (JLabel).setMinimumSize(d);
    
    本来は文字数指定ができるといいのですけどね。
    なお、JLabelは大概の場合setPreferredSize(d)だけでよいのですが、 JPanelその他MaximunSize/MimimumSizeも指定しないとサイズ が変わらないものもありますので、通常この手続きをとった 方が安全です。
    右寄せにするにはsetHorizontalAlignment(JLabel.RIGHT)を 使います。setAlignmentXといった関数やALIGNMENT_RIGHTなど 色々無効な指定が沢山ありますので間違わないよう

  • ビープ音をならす
    単純なビープ音(クリック音?)は次の1文で出すことができます。
     java.awt.Toolkit.getDefaultToolkit().beep();
    
  • スタンドアロンJAVAとApplet
    ほぼ絶滅状態のAppletですが、今回のような単純 サンプルを示すためには重宝します。
    スタンドアロンJAVAプログラムをApplet化するには次の変更を 施します。
    • 主窓をJFrameでなくJAppletの派生にする
    • 主窓でsuper()は呼ばない
    • main()を無くしinit()で初期化する
    • destroy()で終了処理
    JPanelに内容をセットする部分をmainプログラムから分離しておけば スタンドアロンでもAppletでも使用できます。
    スレッド起動などを行っている場合destroyで停止する必要があります。 (本記事の例では不必要なため記述していません)

  • HTMLにAppletを組み込む
    jarに纏められたappletをHTMLに組み込むには次のような記述 をします。
       <applet 
       code="BtnTstAplt"
       archive="BtnTst.jar"
           width=350 height=40>
       </applet>
    
    ただ、Applet版は直接組み込まず、別HTMLにして、クリック時に 参照/起動される形にしておく必要があります。HTMLに直接記述 すると環境によってはブラウザが異常を起こします。
    本記事も当初は直接記述していましたが、別HTML化しました。

  • 拡張for文
    今回は使っていませんが、ついでなのでメモしておきます。
    拡張for文によるコンテナ要素アクセスです。実用的で優れた機能ですが センスの悪い記述形式です。iterator()メソッドでIterator<T>が 得られるコンテナクラスであればどんなクラスでも適用可能です。
       String[]                a = new String[0];
       ArrayList<String>       al= new ArrayList<String>();
       HashSet<String>         hs= new HashSet<String>();
       HashMap<String,Integer> hm= new HashMap<String,Integer>();
       // なんだかんだ値セット
       for(String s:a)  System.out.println(s);
       for(String s:al) System.out.println(s);
       for(String s:hs) System.out.println(s);
       for(String s:hm.keySet()) {
          System.out.println(s+":"+hm.get(s));
          }
    


 ダウンロード

プログラムを纏めて、BtnTstPrjct.lzhに置きました。

展開するとBtnTstPrjctフォルダと、その下にプログラムファイル および幾つかのバッチファイルができます。
BtnTst_mk.batでコンパイルされ、BtnTst.jarが作成されます。 BtnTst.jarは実行可能なjarファイルです。
BtnTstAplt.htmでappletを動かすことができます。
clean.batで*.classが消去されます。
cleanAll.batでjarファイルも含む二次生成ファイルが削除されます。

|

« ◆新聞、雑誌の束ね方、結び方、縛り方 | トップページ | ◇ブラックホールに光速で落ち込む物質はブラックホールより重くなるか? »

トラックバック


この記事へのトラックバック一覧です: ▼Swingでボタンイベントを"CLICK"から"DOWN"に変える:

« ◆新聞、雑誌の束ね方、結び方、縛り方 | トップページ | ◇ブラックホールに光速で落ち込む物質はブラックホールより重くなるか? »