こちらのプログラムは画面上部のテキストボックスに名前を入力したら、名前を姓(Last Name)と名(First Name)に分割してそれぞれのテキストボックスに表示するという簡単なプログラムです。
1. 名前を半角空白区切りにすると前が名で後ろが姓
2. 名前をカンマ区切りにすると前が姓で後ろが名
というような分割ルールになっています。
結構簡単なプログラムのはずなんですが、思いのほかはまってしまいました。
はまりポイントは下記の通り- GroupLayout用Panelが用意されていない。
- ラベル(Label)の中央寄せの仕方がわからない。
- BoxLayoutを利用すると何故かテキストボックスのはばがやたらひろがる。
- 複数のボタンにそれぞれアクションをつけたい場合ってどうするの?
- GroupLayout用Panelが用意されていない。
これは別に大した問題でもなかったのですが・・・。
scala.swing パッケージないでは、あらかじめLayoutManagerを登録したPanelを利用するようになっています。別にJPanelとGroupLayoutクラスをそのまま利用しても特に問題はなかったのですが、今回はscala.swingパッケージを利用することにこだわっていたので、仕方なくBoxPanel、GridPanel、FlowPanelを利用して作成するようにしました。
この部分のソースはこちら/** * コンポーネントの配置 */ contents = new BoxPanel(Orientation.Vertical) { preferredSize = new Dimension(300, 200) // 名前入力欄とそのラベルを追加 contents += topLabel contents += topText // 隙間を作るために空のパネルを貼り付ける contents += new Panel { maximumSize = new Dimension(50,20) preferredSize = maximumSize } // 分割した姓名を表示するためのテキストボックスを追加 contents += new GridPanel(2,1) { maximumSize = new Dimension(300, 60) preferredSize = maximumSize vGap = 2 // 名のラベルとテキストボックス contents += new FlowPanel { contents += new Label("First Name:") contents += firstNameText } // 姓のラベルとテキストボックス contents += new FlowPanel { contents += new Label("Last Name:") contents += lastNameText } } // ボタンを配置 contents += new FlowPanel { contents += computeButton contents += clearButton contents += closeButton } } - ラベル(Label)の中央寄せの仕方がわからない。
これはScala関係ないような気もしますが・・・。
swing.scalaのLabelクラスには- horizontalAlignment
- horizontalTextPosition
- xAlignment
- xLayoutAlignment
正解はxLayoutAlignmentでした。
サンプルソースはこんな感じです// トップラベル val topLabel = new Label ("Enter name:") { xLayoutAlignment = Component.CENTER_ALIGNMENT } - BoxLayoutを利用すると何故かテキストボックスのはばがやたらひろがる。
BoxLayoutを利用するとテキストボックスが勝手にmaximumSizeになってまうようです。
なので、BoxLayout上でテキストボックスを配置する場合は
val topText = new TextField (21) { maximumSize = preferredSize }というようにmaximumSizeをpreferredSizeで上書きする必要がありました。 - 複数のボタンにそれぞれアクションをつけたい場合ってどうするの?
Scalaではボタンをクリックしたときはscala.swing.event.ButtonClickedケースクラスに押したボタンのクラスが渡される形でイベントが実行される。
なので
reactions += { case ButtonClicked(b) => b.text match { case "Close" => System.exit(0) case "Clear" => clearFields case "Compute" => { val namer = nFactory.getNamer(topText.text) firstNameText.text = namer.first lastNameText.text = namer.last } } }というようにmatch式でボタン名などを基準に処理を分けることができました。
う~ん、ちょっとショック。こういうはまりポイントってすぐに解決策見つけちゃう人は見つけちゃうんですよね。
自分は、そうではないのでそういう人がうらやましいです。
最後に全部のソースを下記においておきます。
import scala.swing._
import scala.swing.event._
import javax.swing.border._
import java.awt.Dimension
import java.awt.Component
import java.awt.Color
object SimpleFactory extends SimpleGUIApplication {
val nFactory = new NameFactory
def top = new MainFrame {
title = "Simple Factory Sample"
/*
* 使用するコンポーネント
*/
// トップラベル
val topLabel = new Label ("Enter name:") {
xLayoutAlignment = Component.CENTER_ALIGNMENT
}
val topText = new TextField (21) {
maximumSize = preferredSize
}
val firstNameText = new TextField(16) {
maximumSize = preferredSize
}
val lastNameText = new TextField(16) {
maximumSize = preferredSize
}
val computeButton = new Button("Compute")
val clearButton = new Button("Clear")
val closeButton = new Button("Close")
/**
* コンポーネントの配置
*/
contents = new BoxPanel(Orientation.Vertical) {
preferredSize = new Dimension(300, 200)
contents += topLabel
contents += topText
// 隙間を作るために空のパネルを貼り付ける
contents += new Panel {
maximumSize = new Dimension(50,20)
preferredSize = maximumSize
}
// 分割した姓名を表示するためのテキストボックスを追加
contents += new GridPanel(2,1) {
maximumSize = new Dimension(300, 60)
preferredSize = maximumSize
vGap = 2
// 名のラベルとテキストボックス
contents += new FlowPanel {
contents += new Label("First Name:")
contents += firstNameText
}
// 姓のラベルとテキストボックス
contents += new FlowPanel {
contents += new Label("Last Name:")
contents += lastNameText
}
}
// ボタンを配置
contents += new FlowPanel {
contents += computeButton
contents += clearButton
contents += closeButton
}
}
listenTo(computeButton)
listenTo(clearButton)
listenTo(closeButton)
reactions += {
case ButtonClicked(b) => b.text match {
case "Close" => System.exit(0)
case "Clear" => clearFields
case "Compute" => {
val namer = nFactory.getNamer(topText.text)
firstNameText.text = namer.first
lastNameText.text = namer.last
}
}
}
// 文字列をクリアする
def clearFields = {
firstNameText.text = ""
lastNameText.text = ""
topText.text = ""
}
}
}
abstract class Namer {
// 2つのサブクラスによってextendsされる基底クラス
def last: String;
def first: String;
}
class LastFirst(s: String) extends Namer {
private val list = s.split(",").toList
def last = if (list.length > 0) list.head else s
def first = if (list.length > 0) list.tail.mkString("") else ""
}
class FirstFirst(s: String) extends Namer {
private val list = s.split(" ").toList
def last = if (list.length > 0) list.tail.mkString("") else ""
def first = if (list.length > 0) list.head else s
}
class NameFactory {
// コンマの有無によってどちらのクラスを返すべきかを決定する
def getNamer(entry:String) : Namer = {
// コンマの有無によって順序を判定
if (entry.indexOf(",") > 0) new LastFirst(entry) else new FirstFirst(entry)
}
}
またしばらくしたら同じ本からいくつかサンプルを紹介したいと思います。まあするかもしれないし、しないかもしれないですが・・・。 ちなみに、今回の参考文献はこちらです。
ジェイムズ・W. クーパー
ピアソンエデュケーション
売り上げランキング: 270022
ピアソンエデュケーション
売り上げランキング: 270022
読んでみてはいかがでしょうか?
あとSwingのチュートリアルはやったけど、それ以外に何かもう一冊だけ
写経してみたいな~という方にはお薦めです。
※追伸 >- この投稿が役にたったと思った方は、上か下の広告のクリックをお願いいたします。


0 件のコメント:
コメントを投稿