Page

2010年7月21日水曜日

BlackBerry OS 上でのアプリ開発の基礎 - UI Component の基本 No.2

 どうもです。久しぶりに開発ネタの続きです。

 とりあえず前回は画面に何か出すということで標準で利用出来る UI の部品の一部を紹介してみました。新しい OS のバージョンが出るとそれに合わせて開発者に公開される API のバージョンも上がり、このタイミングでいくつか Component が追加されたりします。例えば OS 5.0 では、

  • 画面遷移を行った際、にゅーんと横から表示したりするアニメーション効果が簡単に付けられたり
  • ブラウザの画面を簡単に組み込めたり
  • ネットワークの接続が簡単になったり
  • Location Service (Map)と簡単に連動したり

してます。
 当分国内では見かけないと思いますが、 OS 6 では特にコンシューマーを意識した API が大量に追加されるみたいです。 OS の機能やデザインが変わると新しい API も多くなります。


 ただし、 BlackBerry の場合は Java アプリということもあり、OSが新しくなっても下位の API で作成されたアプリなんかは結構動きます。当然新機能などは NG ですが、あまり互換性を心配しなくても機能するというのは、企業ユーザーにはプラスです。最も、いつまでも古い設計のアプリや資産は廃れていくだけなので、さっさと淘汰されてしまえば良いのに、とも考えています。

 前置きが長くなってしまいましたが、今回も UI の基本ということで書いていこうかと。どの分野でも同じですが、細かく書いていくと切りがないので、UI 系は今回で終わりにしようかと考えています。

1. Field の拡張
 基本的な UI のコンポーネントは BlackBerry の API では、 xxxField という名前でクラスが定義されています。基本機能だけでは足りない、もしくは簡単にカスタマイズしたい、という場合は、この xxxField を継承する場合が多いと思います。

 以下は LabelField を継承した例です。YouTube の動画の再生時間を表示するためだけのラベルです。ラベルなので、 setText(String)のように文字列を表示するのが主旨ですが、 setDuration(int) を定義して、 Web から取得した動画の再生時間を簡単に適用してラベルとして機能させています。結構いい加減ですが、秒数を 分:秒 の形式で表示する簡単なものです。


import net.rim.device.api.ui.component.LabelField;

public class YTDurationField extends LabelField {
// member

// constructor
public YTDurationField(int duration){
super("", FIELD_LEFT);

int min = duration / 60;
int sec = duration % 60;

if( sec == 0){
super.setText("" + min + ":00");
} else if( sec < 10){
super.setText("" + min + ":0" + sec);
} else {
super.setText("" + min + ":" + sec);
}

}

// set Duration
public void setDuration(int duration){
int min = duration / 60;
int sec = duration % 60;

if( sec == 0){
super.setText("" + min + ":00");
} else if( sec < 10){
super.setText("" + min + ":0" + sec);
} else {
super.setText("" + min + ":" + sec);
}
}
}

 LabelやButton、Bitmap のフィールドは拡張する場合が多いと思います。また、 Field クラス自体を継承する場合もあるでしょう。開発者が実現したい UI コンポーネントになるべく近いものを見つけて、それを継承してやるだけです。


2. Manager の拡張
 ラベルやボタンのシンプルな拡張ではなく、”イメージ付きのラベル”といった複数のコンポーネントをまとめて1つの設計要素にしたい場合があります。この場合は Manager を継承するのが一般的です。
 以下はラベル(LabelField)とイメージ(BitmapField)の両方をセットで表示するために作成したものです。

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.component.LabelField;

public class NVWLabelwithImageField extends Manager {

// member
private LabelField _labelField;
private BitmapField _bitmapField;
// constructor
public NVWLabelwithImageField(String text, String imgpath){
super(FOCUSABLE | USE_ALL_WIDTH);
_labelField = new LabelField( text, FOCUSABLE );
_bitmapField = new BitmapField( Bitmap.getBitmapResource(imgpath));
add(_labelField);
add(_bitmapField);
}

// required methods
protected void sublayout(int width, int height) {
int x = ( width - ( _bitmapField.getWidth() + 5 + _labelField.getWidth() ) ) / 2;
if(x < 0){
x = 0;
}
layoutChild(_bitmapField, width, height);
layoutChild(_labelField, width  - _bitmapField.getWidth() - 5, height);
setPositionChild(_bitmapField, x , 5);
setPositionChild(_labelField, x + _bitmapField.getWidth() + 5, 5);
int actualHeight = Math.max(_bitmapField.getHeight(), _labelField.getHeight());
setExtent(width, actualHeight + 5);
}
}

 sublayout メソッドの実装が必須です。ここで、内部に保持している xxxField をどのように配置するかを記述する必要があります。
 上記の例では、
  • 使用する幅の中心を求める
  • 画像、ラベルをセット
  • 画像の横に 5 ピクセルの空白をつけてラベルを配置し、かつ縦(Y)に5ピクセルの余白を用意する
  • 画像、ラベルの高さから、実際に必要な高さを算出して、 setExtend でこの Manager が使用する画面領域を定義
をやってます。
 親クラスの Manager は、各コンポーネントをスクリーンに配置する VerticalFieldManager (縦積み。デフォルト)や HorizontalFieldManager (横1列並べて配置)の親でもある Manager クラスです。 OS 5.0 では GridFieldManager (格子状)や、 EyelidFieldManager (必要なときだけスクリーンの上下にメニューなどを表示する)なども用意されています。
 また、当然ながら実現する方法は今回の方法だけではないことに留意しましょう。例えば各コンポーネントを横に並べ、かつそれぞれの余白を多めにつける、というだけならば、setBorder でボーダーの定義を行った LabelField を複数用意し、 FlowFieldManager に追加する方法もあります。


まとめ
 Field は個々の UI の部品、 Manager は複数の Field を配置するためのクラスとして使用するのが基本です。

0 コメント:

コメントを投稿