« △フォルマント合成・時間差差分加算式 | トップページ | ▲イントネーションから迫る音声合成-基礎試験 »

メモ:swingでサイズ指定&縦横レイアウト

Java/SWINGで

  • 画面要素を縦横に並べる
  • 画面要素にサイズを指定する
という当たり前の要求にこたえる方法をここにメモしておきます。

 GroupLayoutを用いてサイズの異なる要素を縦横に並べる。

GroupLayoutを用います。 GridLayoutは全ての要素を同じサイズにしてしまいますので、殆ど実用性はありません。 BoxLayoutを階層化して縦横に並べる場合、なかなか位置がそろいません。 デフォルトのBorderLayoutは5個の要素を並べるという不自然なものでこれも実用性はありません。

GroupLayoutは [[横方向の並び]の並び]  と [[縦方向の並び]の並び]  をいちいち指定しなくてはならないというとんでもないインターフェースを持ちます。

毎回同じことを書くことは現実的ではないので、配列(または引数ならび) で要素を指定すれば縦横に並べるプログラムコードを示します。クラスの形に していますが、関数だけをコピペして使うことも可能です。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class L {
   /** 縦横に要素を整列させるpanel */ public static JPanel
   groupPanel(int rows_,int columns_,JComponent ... elements_){
      JPanel      _ret    = new JPanel();
      GroupLayout _layout = new GroupLayout(_ret);
      _ret.setLayout(_layout);
      if( (rows_<=0 && columns_<=0) || elements_==null ) return _ret;
      if( rows_<=0    ) rows_   = elements_.length/columns_;
      if( columns_<=0 ) columns_= elements_.length/rows_;
      //if( (rows_*columns_)>elements_.length ) return _ret;
      GroupLayout.SequentialGroup _vgroup = _layout.createSequentialGroup();
      for(int _r=0;_r<rows_;++_r){ // 水平方向に要素並んだグループ
         int _rr= _r*columns_;
         GroupLayout.ParallelGroup _group
                = _layout.createParallelGroup(GroupLayout.Alignment.BASELINE);
         for(int _c=0;_c<columns_;++_c){
            _group.addComponent(elements_[_rr+_c]);//[1]
            }
         _vgroup.addGroup(_group);//[2]
         }
      _layout.setVerticalGroup(_vgroup);//[3]
      GroupLayout.SequentialGroup _hgroup = _layout.createSequentialGroup();
      for(int _c=0;_c<columns_;++_c){ // 垂直方向に要素が並んだグループ
         GroupLayout.ParallelGroup _group
                = _layout.createParallelGroup();
         for(int _r=0;_r<rows_;++_r){
            int _rr= _r*columns_;
            _group.addComponent(elements_[_rr+_c]);//[4]
            }
         _hgroup.addGroup(_group);//[5]
         }
      _layout.setHorizontalGroup(_hgroup);//[6]
      return _ret;
      }
   //...
   }
基本的には
  1. 横の並びをParallelGroupに入れ、
  2. その縦並びをSequentialGroupに入れる。
  3. SequentialGroupをsetVerticalGroupでセットする
  4. 縦の並びをParallelGroupに入れ、
  5. その並びをSequentialGroupに入れる。
  6. SequentialGroupをsetHorizontalGroupでセットする

 この関数の使い方

要素の並びを第3引数に一次元配列、または引数並びで与えます。
次の2つのプログラム断片はどちらも同じ画面を生成します。

   // 配列で与える
   void disp(JPanel pane_){
      JComponent _elms[]={
         new JLabel("あいうえお")
        ,new JButton("ボタンです")
        ,new JTextField("わをん")
        ,new JLabel("ABCDEF")
        ,new JButton("button")
        ,new JTextField("XYZ")
         };
      pane_.add(L.groupPanel(2,3,_elms));
      }
   // 引数並びで与える。
   void disp(JPanel pane_){
      pane_.add(L.groupPanel(2,3
        ,new JLabel("あいうえお")
        ,new JButton("ボタンです")
        ,new JTextField("わをん")
        ,new JLabel("ABCDEF")
        ,new JButton("button")
        ,new JTextField("XYZ")));
      }
第1引数と第2引数で何行何列かを指定します。
例えば、3行5列(縦3、横5)の場合は3,5と指定します。
-1を指定することにより片方を可変にできます。例えば横が5に定まって いて縦が不定の場合は-1,5と指定します。

引数の並びで要素を与える場合、階層構造をそのまま表すことができます。
次の例では、画面は2階層になっており、縦に1個ずつ並ぶ 階層となっており、その1個が3×5の要素になっています。


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class LayoutTst extends JFrame {
   LayoutTstEngn engn;
   LayoutTst(String name_){
      super(name_);
      try{
         engn=new LayoutTstEngn((JPanel)getContentPane());
         }
      catch(Exception e){}
      }
   public static void main(String[] args_){
      LayoutTst _LayoutTst= new LayoutTst("LayoutTst");
      _LayoutTst.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      _LayoutTst.pack();           // 内部要素のサイズにする
      _LayoutTst.setVisible(true); // 表示する
      }
   }
class LayoutTstEngn implements ActionListener{
   JButton bQuit;
   LayoutTstEngn(JPanel pane_){
      pane_.add(
         L.groupPanel(-1,1
            ,new JLabel("レイアウト試験")
            ,L.groupPanel(-1,5
               ,new JLabel("ラベル1 ")
               ,new JButton("A")
               ,new JTextField("abcd")
               ,new JButton("BCDEFGHIJK")
               ,new JTextField("efg")

               ,new JLabel("hijk")
               ,new JTextField("lmn")
               ,new JTextField("")
               ,new JTextField("stu")
               ,new JTextField("あいうえお")

               ,new JLabel("L")
               ,new JButton("MN")
               ,new JTextField("か")
               ,new JButton("O")
               ,new JTextField("き"))
            ,bQuit= new JButton("QUIT"))
         );
      bQuit.addActionListener(this);
      }
   public void actionPerformed(ActionEvent e_){
      if( e_.getSource()==bQuit ) System.exit(0);
      }
   }

 ボタンやテキストフィールドのサイズを指定する

Javaの画面はとんでもなくセンスが悪く、標準ではサイズがばらばらでしかも大きくて 間延びしたものとなります。
サイズ指定は

  • PreferredSize
  • MaximumSize
  • MinimumSize
の3つを指定しないと勝手なサイズにされてしまいます。
さらにボタンの場合は
  • マージン
の指定も必要です。

サイズを指定して要素を生成する関数の例を以下に載せます。クラスの 形としていますが、必要な関数のみコピペしても構いません。
なお、この関数群では、イベントリスナーの指定も"ついでに"行ってあります。

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

public class L {
   /* 要素のサイズを設定する */ public static JComponent
   setSize(JComponent elm_,Dimension d_){
      if( d_==null ) return elm_;
      elm_.setPreferredSize(d_);
      elm_.setMaximumSize(d_);
      // elm_.setMinimumSize(d_);
      if( elm_ instanceof AbstractButton ){
         ((AbstractButton)elm_).setMargin(new Insets(0,0,0,0));
         }
      return elm_;
      }
   /** サイズ指定でボタンを得る */ public static JButton
   button(String text_,Dimension d_,ActionListener listen_){
      JButton _ret= new JButton(text_);
      setSize(_ret,d_);
      if( listen_!=null )_ret.addActionListener(listen_);
      return _ret;
      }
   /** サイズ指定でJRadioButtonを得る */ public static JRadioButton
   radioButton(String text_,boolean sel_,Dimension d_,ActionListener listen_){
      JRadioButton _ret= new JRadioButton(text_,sel_);
      setSize(_ret,d_);
      if( listen_!=null )_ret.addActionListener(listen_);
      return _ret;
      }
   /** サイズ指定でJTextFieldを得る */ public static JTextField
   textField(String text_,Dimension d_,ActionListener listen_){
      JTextField _ret= new JTextField(text_);
      setSize(_ret,d_);
      if( listen_!=null )_ret.addActionListener(listen_);
      return _ret;
      }
   /** サイズ指定でJTextFieldを得る(変更不能) */ public static JTextField
   textFieldNoEdit(String text_,Dimension d_){
      JTextField _ret= new JTextField(text_);
      _ret.setEditable(false);
      setSize(_ret,d_);
      return _ret;
      }
   //...
   }
なお、MinimumSizeを設定すると枠が書かれない場合がある(同じコードでも 環境によって動きが違います)ので、設定しないようにしています。
JLabelは不自然なサイズ膨張をしないようなのでここでは関数を用意していませんが、 他と同様の関数を作ってもよいでしょう。

 この関数の使い方

各関数はjava.awt.Dimensionでサイズを指定します。サイズを指定したくない場合は nullを与えてください。 幾つかはjava.awt.event.ActionListenerも設定できます。 ActionListenerを設定したくない場合はnullを与えてください。
下のコードで右の図が得られます。Buttonがスリムになり、空白を与えたTextField も指定幅になっていることが分かります。


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class LayoutTst extends JFrame {
   LayoutTstEngn engn;
   LayoutTst(String name_){
      super(name_);
      try{
         engn=new LayoutTstEngn((JPanel)getContentPane());
         }
      catch(Exception e){}
      }
   public static void main(String[] args_){
      LayoutTst _LayoutTst= new LayoutTst("LayoutTst");
      _LayoutTst.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      _LayoutTst.pack();           // 内部要素のサイズにする
      _LayoutTst.setVisible(true); // 表示する
      }
   }
class LayoutTstEngn implements ActionListener{
   JButton bQuit;
   LayoutTstEngn(JPanel pane_){
      Dimension _dim_text=new Dimension(50,18);
      Dimension _dim_btn =new Dimension(30,18);
      pane_.add(
         L.groupPanel(-1,1
            ,new JLabel("レイアウト試験")
            ,L.groupPanel(-1,5
               ,new JLabel("ラベル1 ")
               ,L.button("A",_dim_btn ,this)
               ,L.textFieldNoEdit("abcd",_dim_text)
               ,L.button("BCDEF",_dim_btn,this)
               ,L.textFieldNoEdit("",_dim_text)

               ,new JLabel("hijk")
               ,L.textField("lmn",_dim_text,null)
               ,L.textField("",null,null)
               ,L.textField("stu",null,null)
               ,L.textField("あいうえお",null,null)

               ,new JLabel("G")
               ,L.button("HI",_dim_btn,this)
               ,L.textField("か",_dim_text,this)
               ,L.button("J",_dim_btn,this)
               ,L.textField("き",_dim_text,this))
             ,bQuit= L.button("quit",new Dimension(200,18),this))
         );
      }
   public void actionPerformed(ActionEvent e_){
      if( e_.getSource()==bQuit ) System.exit(0);
      }
   }

 要素をまとめて設定できる縦または横並びのパネル

1行または1列のパネルはgroupPanelの第1、第2引数に1,-1または-1,1を指定 することで得られますが、BoxLayoutを使いたい場合もあります。

BoxLayoutでも、配列または引数ならびで要素を設定できる関数を作成しました。 クラスの形をしていますが、必要部だけコピペしても構いません。

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

public class L {
   /** 並べる */ public static JPanel
   boxPanel(int layout_,JComponent ... elements_){
      JPanel _ret= new JPanel();
      _ret.setLayout(new BoxLayout(_ret,layout_));
      if( elements_!=null ){
         for(JComponent _c:elements_)_ret.add(_c);
         }
      return _ret;
      }
   /** 横に並べる */ public static JPanel
   hPanel(JComponent ... elements_){
      return  boxPanel(BoxLayout.X_AXIS,elements_);
      }
   /** 横に並べる */ public static JPanel
   vPanel(JComponent ... elements_){
      return boxPanel(BoxLayout.Y_AXIS,elements_);
      }
   //...
   }

 この関数の使い方

配列または引数並びで要素を指定できます。
hPanelは横並び、vPanelが縦並びです。 引数並びの場合は構造そのままに階層化したコードが書けます。
コードの断片を載せます。縦(V)の中に横(H)を2個並べています。


   void disp(JPanel pane_){
      pane_.add(L.vPanel(
         L.hPanel(
            new JLabel("あいうえお")
           ,new JButton("ボタンです")
           ,new JTextField("わをん"))
        ,L.hPanel(
            new JLabel("ABCDEF")
           ,new JButton("button")
           ,new JTextField("XYZ"))));
      }

 appletに関する補足

例に挙げたLayoutTst.javaはスタンドアローンプログラムですが、内部クラスLayoutTstEngnは アプレットから利用できる形式を採っています。
アプレットとして動かすための、ビルド法を含む関連記述を以下に載せます。

// LayoutTstAplt.java アプレットクラス
import javax.swing.*;
public class LayoutTstAplt extends JApplet {
   LayoutTstEngn engn;
   public void init(){
      try{
         engn=new LayoutTstEngn((JPanel)getContentPane());
         }
      catch(Exception e){}
      }
   }
:: ビルドバッチファイル
javac *.java -encoding utf-8
jar -cmf LayoutTst.mf LayoutTst.jar *.class
; LayoutTst.mf  (注意!!マニフェスト中にはコメントは書けない)
Main-Class: LayoutTst
<!-- LayoutTst.htm アプレットを参照するHTML -->
<html>
<head>
<meta http-equiv="Context-Type" content="text/html; charset=utf-8">
</head>
<body>
<applet
code="LayoutTstAplt.class"
archive="LayoutTst.jar"
 width=300 height=95>
<param name="jar" value="LayoutTst.jar"/>
</applet>
</p>
</body>
</html>

参考までにアプレットを参照するHTMLを ../../../jar_zip/LayoutTst.htmに置いてあります。

|

« △フォルマント合成・時間差差分加算式 | トップページ | ▲イントネーションから迫る音声合成-基礎試験 »

トラックバック


この記事へのトラックバック一覧です: メモ:swingでサイズ指定&縦横レイアウト:

« △フォルマント合成・時間差差分加算式 | トップページ | ▲イントネーションから迫る音声合成-基礎試験 »