NumberInput_IMKit_Sample
以下は、Mac OS X 10.5.5 上の Xcode 3.1 で説明しています。このサンプルは、NumberInput 0 から NumberInput 4 までの 5 段階に分かれていて、何もしないものから次第に高度な機能をもつものへと拡張されていきます。
2 NumberInput 1
2.1 ReadMe の日本語訳
NumberInput のこのバージョンは、変換エンジンに機能を追加し、コントローラークラスにおける単純なバッファ管理を実例で示します。
commitComposition メソッドが、コントローラークラスに追加されています。特定のユーザー行動にもとづいて、アクティブな入力セッションを確定させるために、このメソッドを実装することが必要です。
さらに、InputEngine クラスが追加されています。このデモでは、特定の「リソース」が入力メソッドコントローラーすべての間でどのように共有できるかを再び示し、モジュール化を強化するために、入力エンジンを管理する別のクラスを使っています。
2.2 変換機能
ビルドとインストール方法は、前回と同じです。このサンプルでは、変換機能が追加されています。後で、ソースを見ればわかりますが、これは数字を書式設定オブジェクトを使って書式設定された形式へと変換するものです。テキストエディットアプリケーションを起動させ、NumberInput を選択します。そして「123456789」という文字を入力してみます。
まだ確定されていない文字として、下線が付けられていることに注意してください。そして、変換させるためにスペースキーを押すと下のようになります。
3 桁ごとにコンマが入れられています。これでリターンキーを押すと確定されます。また、再変換させようとスペースキーを押せば、確定されて、さらにその後に空白が挿入されます。
先ほどのサンプルと同じように、コンソールを起動させていれば、エラーが起こったのを目にするでしょう。これは、委任の menu コマンドで起っているようです。サンプルの実装が中間的で発生しているようなので、とりあえず無視することにします。
2.3 グループとファイル
プロジェクトの構成は、以下のようになっています。
クラスとして、ConversionEngine と NumberInputApplicationDelegate が追加されていることがわかります。InputMethodKeysDictionary.dict が赤色で表示されているのに注意してください。これはサンプルの完成版でも含まれていません。サンプル作成途中で、独自のキーバインディングを実装しようとした時のものでしょう。サンプルフォルダ内に画像ファイルなどが追加されていますが、これらは後の段階で使うもので、プロジェクトには入っていないことに注意してください。
Info.plist ファイルの内容は、先ほどと同じですが、MainMenu.nib の内容は変わっています。ConversionEngine と NumberInputApplicationDelegate クラスが、このなかでインスタンス化されています。これにより、変換エンジンとアプリケーション委任がどちらも読み込み時に 1 つ作成されるようになっています。接続とともに見てみましょう。以下のように、ファイル所有オブジェクト(main.m でアプリケーションになる)の委任として NumberInputApplicationDelegate インスタンスが接続されています。
また、NumberInputApplicationDelegate インスタンスのアウトレットに、ConversionEngine インスタンスが接続されています。
main.m の内容は前と同じです。そのため、main 関数内の loadNibNamed: owner: メソッドのところで、上に書いた 2 つのインスタンスが作成され、それぞれ接続されることになります。
では、クラスファイルを順番に見ていきます。入力コントローラーは、多数のメソッドが追加されているので後まわしにします。まずは一番簡単な NumberInputApplicationDelegate クラスから見ていきます。
2.4 NumberInputApplicationDelegate クラス
まずヘッダファイルを見てみます。
NumberInputApplicationDelegate.h
// 変換エンジンはすべての入力コントローラーで共有される。
// そのため、それをコントローラーのどれからでもアクセスできるアプリケーション委任に格納する。
// ConversionEngine は、IB 内で指定されているので自動的にインスタンス作成される
@interface NumberInputApplicationDelegate : NSObject {
IBOutlet ConversionEngine* _conversionEngine;
}
-(ConversionEngine*)conversionEngine;
@end
_conversionEngine は、IB 内でアウトレット接続されていたものです。そのため、nib から読み込まれた時に自動的に変換エンジンに接続されることになります。あとは、ただ 1 つのメソッドである conversionEngine を宣言しているだけです。これは以下のようになっています。
NumberInputApplicationDelegate.m > conversionEngine
-(ConversionEngine*)conversionEngine
{
return _conversionEngine;
}
このように、アプリケーション委任を通じて、入力コントローラーが変換エンジンにアクセスできるように、nib 読み込み時に自動的に設定されたインスタンス変数を返しているだけです。ReadMe にも書かれていましたが、このような形で、リソースやオブジェクトをアプリケーション内に準備しておくことで、すべての入力コントローラーで共通に使うことができます。
次に ConversionEngine クラスを見てます。変換エンジンは、さほど高機能でなくソースも複雑ではないため、先に説明しておきます。
2.5 ConversionEngine クラス
まず、ヘッダを見てみます。前のプロジェクトでは省きましたが、インターフェース部の前には、その内容をまとめるコメントが付けられています。今回は、それも示します。
ConversionEngine.h
/*!
@class
@abstract サンプルの変換エンジン
@discussion 別のオブジェクトにする必要はない。
入力メソッドをどのようにモジュール化できるかを示すためここではこうした。
このオブジェクトは入力テキストを書式設定するために NSNumberFormatter を使う。
単一の書式設定オブジェクトがすべての入力セッションで共有される。
ConversionEngine は出力数が書式設定されたほうがいいかを判定するため NSCharacterSet も使う。
*/
@interface ConversionEngine : NSObject {
NSNumberFormatter* formatter;
// 書式設定オブジェクト
NSNumberFormatterStyle conversionMode;
// 現在のスタイル
}
/*
@method
@abstract 入力テキストを変換する。
@discussion 入力として入力テキストバッファをとり、与えられた数値書式で書式設定された文字列を返す。
*/
-(NSString*)convert:(NSString*)string;
/*
@method
@abstract 現在の変換モードを返す。
@discussion (訳者註:上と同じ、間違いだろう)
*/
-(NSNumberFormatterStyle)conversionMode;
@end
コメントでわかると思います。インスタンス変数は 2 つあり、書式設定オブジェクトと現在のスタイルです。メソッドは 2 つあり、変換を行うものと、変換に使うスタイルを返すものです。
つぎに実装ファイルを見ていきます。メソッド以外は定義されていないので、各メソッドを見てみます。まずは、スタイルを返すほうのメソッドです。
ConversionEngine.m > conversionMode
-(NSNumberFormatterStyle)conversionMode {
return NSNumberFormatterDecimalStyle;
}
単に NSNumberFormatterDecimalStyle を返しているだけです。これは通常の十進スタイルの書式です。つぎに変換を行うメソッドについて見てみます。
ConversionEngine.m > convert:
-(NSString*)convert:(NSString*)string
{
// 書式設定オブジェクトを遅延的に割り当てる
// NSNumberFormatter の 10.4 メソッドを使いたい。
// そのため、ここでそれを割り当て、デフォルト動作を 10.4 動作へと設定する。
/*
重要:NSNumberFormatter の 10.4 より前のメソッドは、10.4 で追加されたメソッドと互換性がない。
NSNumberFormatter は、これらの異なる動作グループ内で区別なくメソッドを呼び出さないはず。
書式設定動作を NSNumberFormatterBehavior10_0 に設定したら、古いスタイルのメソッドを使う。
書式設定動作を NSNumberFormatterBehavior10_4 に設定したら、新しいスタイルのメソッドを使う。
IB で作成された数値書式設定オブジェクトが 10.0 動作を使うことにも注意。
10.4 上の NSNumberFormatter を参照。
*/
if ( formatter == nil ) {
// 最新の 10.4 動作を望むことを指定
[NSNumberFormatter setDefaultFormatterBehavior:NSNumberFormatterBehavior10_4];
// 独自の書式設定オブジェクトを割り当て
formatter = [[NSNumberFormatter alloc] init];
}
// 文字列を最初に数値へと変換する
// 変更されるたびに毎回、変換スタイルを設定
[formatter setNumberStyle:[self conversionMode]];
NSNumber* number = [formatter numberFromString:string];
// ここで、数値を正しい書式設定文字列へと変換する
return [formatter stringFromNumber:number];
}
くわしいコメントが付けられているため、それだけで十分わかると思います。最初に書式設定オブジェクトがすでに作られているかどうかを調べます。もし初めてこのメソッドが呼ばれたら、それは存在していないので作成されます。つぎからは、すでにインスタンス変数に設定された既作成のものが使われることになります。
次に、先ほどのメソッドを使って、現在の変換スタイルを取得し、それを数値スタイルとして設定します。それから入力文字列から数値を作成し、その数値から書式設定文字列を作成してそれを返しています。あくまで数値を書式設定するためのものなので、文字列から文字列へと変換できるわけではありません。
さて、これで変換エンジンの機能も理解できました。最後に、入力コントローラークラスがどのように変わっているかを見てみます。
2.6 NumberInputController クラス
まずヘッダファイルを見てみます。メソッドは後で説明するので、インスタンス変数に注目してください。
NumberInputController.h
@interface NumberInputController : IMKInputController {
//_composedBuffer は入力メソッドが変換したテキストを収容
NSMutableString* _composedBuffer;
//_original buffer はユーザー入力から受けとったテキストを収容
NSMutableString* _originalBuffer;
// テキストが _composedBuffer 内のどこに挿入されたかをマークするために使用
NSInteger _insertionIndex;
// このフラグは引き金(スペースキー)に応答して、元のテキストが一度変換済かを示す
// 次に引き金が受けとられたら、構成が確定されることになる
BOOL _didConvert;
}
// 以下は構成と元バッファを管理するための単純なメソッド
// これらはすべて基本的な NSString メソッドの単純なラッパー
-(NSMutableString*)composedBuffer;
-(void)setComposedBuffer:(NSString*)string;
-(NSMutableString*)originalBuffer;
-(void)originalBufferAppend:(NSString*)string client:(id)sender;
-(void)setOriginalBuffer:(NSString*)string;
- (BOOL)convert:(NSString*)trigger client:(id)sender;
@end
コメントで十分わかると思います。このように、入力コントローラー内で入力テキストを格納しておくバッファを用意する必要があります。つぎは、各メソッドを見ていきます。注意すべきなのは、宣言されているメソッドとは別に継承されたメソッドも実装されていることです。前のプロジェクトでは、アクションが見つからない場合に入力が渡される、継承メソッドの inputText: client: だけを実装していました。今回これがどうなっているかを見てみましょう。
NumberInputController.m > inputText: client:
/*!
@method
@abstract 入ってくるテキストを受けとる
@discussion このメソッドは利用アプリケーションからのキーボード入力を受けとる。
メソッドは NSString としてキー入力を受けとる。
文字列は InputMethodKit によってキーダウンイベントから作成されたものになるだろう。
*/
-(BOOL)inputText:(NSString*)string client:(id)sender
{
// キー入力が受けとられ処理されたことを示すには YES を返す。キー処理は、この場合継続されない。
// 別の言い方をすれば、システムはキーダウンイベントをアプリケーションへとは配送しなくなる。
// NO を返せば、元のキーダウンイベントが利用側へと渡されることになる。
BOOL inputHandled = NO;
// 処理済みを示すフラグ
// 構文解析オブジェクトは NSScanner
NSScanner* scanner = [NSScanner scannerWithString:string];
NSDecimal decimalValue;
// 入力をチェック。十進数字の可能性のある部分があるなら、それを記憶
BOOL isDecimal = [scanner scanDecimal:&decimalValue];
// 十進値をスキャン
if ( isDecimal ) {
// 十進値があったら
// 入力テキストが十進数値の一部なら、元のバッファにそれを追加し、処理したことを返す
[self originalBufferAppend:string client:sender];
inputHandled = YES;
}
else {
// 十進値がなかったら
// 入力が十進数字の一部でないなら、以前の入力テキストを変換する必要があるか調べる
inputHandled = [self convert:string client:sender];
}
return inputHandled;
}
コメントを読めば、ほとんど理解できると思います。まず最後に返すことになる処理済みかどうかを示すフラグを初期化します。それから、入力文字をチェックするためのスキャナを作成しています。decimalValue は、チェック時に数値を代入させるためのダミーの変数です。scanDecimal: は有効な数字が見つかったら YES、そうでなければ NO を返します。
数字が見つかったら、originalBufferAppend:string client: で元バッファに文字を追加して、処理済みにします。数字が見つからなかったら、convert:string client: を呼び出し、その結果が処理状態に設定されます。
数字が見かった場合に呼び出されるメソッドを見てみましょう。
NumberInputController.m > originalBufferAppend: client:
// 新しく入力されたテキストを元バッファへと追加
-(void)originalBufferAppend:(NSString*)string client:(id)sender
{
NSMutableString* buffer = [self originalBuffer];
// 元バッファを取得
[buffer appendString: string];
// 引数で渡されたものを追加
_insertionIndex++;
// 挿入番号を増分
[sender setMarkedText:buffer selectionRange:NSMakeRange(0, [buffer length])
replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
// 利用側に挿入を知らせる
}
まず最初に元バッファの参照を取得しています。インスタンス変数なので直接アクセスできますが、アクセサを使っています。
NumberInputController.m > originalBuffer
-(NSMutableString*)originalBuffer
{
if ( _originalBuffer == nil ) {
// 元バッファがなければ
_originalBuffer = [[NSMutableString alloc] init];
// 作成する
}
return _originalBuffer;
}
アクセサ内でチェックすることでバッファの遅延割り当てを行っています。すでに初期化されていれば、そのまま返します。
先のメソッドに戻ると、そうして取得した元バッファに appendString: を使って引数文字列を追加しています。それから、挿入番号を増分して、利用側のメソッドを呼び出しています。
setMarkedText: selectionRange: replacementRange: は、IMKTextInput プロトコルで宣言されているものです。このプロトコルリファレンスでわかるように、Carbon フレームワーク内にあり、利用側が採用するものになっています。「与えられたテキストを挿入し、それがアクティブな入力セッションの一部であることを示すためにマークします。」と説明されています。最初の引数は、挿入される文字列です。ここでは、元バッファを渡しています。次の引数は、選択範囲です。これはバッファ全体が指定されています。次は置換範囲です。これはマーク位置を指定するもので、カーソル位置なら NSNotFound です。これ以外を指定すると、利用側の書類全体で範囲指定をすることができますが、TSMDocumentAccess プロトコルを採用しているアプリケーション以外では無視されてしまいます。
さて、次は、入力が十進数字でない場合に呼ばれるメソッドを見てみます。
NumberInputController.m > convert: client:
// このメソッドは引き金となった文字列にもとづいてバッファ文字列を変換する。
// 以前にテキスト変換済みなら、変換済み文字列に引き金文字列を追加して、変換済み文字列を挿入。
// 以前に変換していなければ、入力文字列が空白かどうかを調べる。
// そうなら、利用側のテキストマークをそれに移し、変換を行ったことを記憶する。
// 入力テキストが空白でないなら、構成を確定し、それから入力文字列を挿入する。
- (BOOL)convert:(NSString*)trigger client:(id)sender
{
NSString* originalText = [self originalBuffer];
// 元バッファを取得
NSString* convertedString;
BOOL handled = NO;
// 処理済みを示すフラグ
if ( _didConvert ) {
// すでに変換されていたら
convertedString = [self composedBuffer];
if ( convertedString && [convertedString length] > 0 ) {
// 変換済み文字列が存在して、空でなければ
NSString* completeString = [convertedString stringByAppendingString:trigger];
// 変換済み文字列に今回入力された文字を追加
[sender insertText:completeString
replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
// 変換済み文字列を利用側に挿入
[self setComposedBuffer:@""];
// 変換済み文字列を空に
[self setOriginalBuffer:@""];
// 元バッファを空に
_insertionIndex = 0;
// 挿入位置を 0 に
_didConvert = NO;
// 未変換状態に戻す
handled = YES;
// 処理済み
}
}
else {
// まだ変換されてなかったら
if ( originalText && [originalText length] > 0 ) {
// 元バッファが存在して、空でなければ
convertedString = [[[NSApp delegate] conversionEngine] convert:originalText];
// 今回入力された文字は追加しないで、すでにあるものを変換
[self setComposedBuffer:convertedString];
// 変換済み文字列に変換した後の文字列を設定
if ( [trigger isEqual: @" "] ) {
// 今回入力されたのが空白なら
[sender setMarkedText:convertedString
selectionRange:NSMakeRange(_insertionIndex, 0)
replacementRange:NSMakeRange(NSNotFound,NSNotFound)];
// 今回入力を含まない変換済み文字列を利用側に挿入
_didConvert = YES;
// 変換済み
}
else {
// 空白でなければ
[self commitComposition:sender];
// まず確定しておく
[sender insertText:trigger
replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
// それから今回入力されたものをさらに挿入
}
handled = YES;
// 処理済み
}
}
return handled;
}
少し長いですが、大きく 3 つの部分に分けられます。変換されていた場合と、そうでなかった場合と、それなら空白文字かそうでないかです。
すでに変換が行われていた場合は、変換済み文字列を取得します。これは元バッファ取得のためのメソッドと全く同じ構造をしていますので、ここでは示しません。作成してすぐのものが返る可能性があるということです。次で、それが存在して、空でないかどうかがチェックされます。大丈夫なら、今回入力された文字を追加します。それを利用側へと挿入します。この insertText: replacementRange: は、リファレンスによれば、「完全に変換済みのテキストを入力セッションに送信します。」ということです。NSNotFound を指定することで現在の選択範囲(テキスト入力のやりとりの上での選択範囲です)を置き換えます。それから変換済みテキスト等を空にして、後片付けをしています。setComposedBuffer: は、このクラスで定義されていて、次のようになっています。
NumberInputController.m > setComposedBuffer:
// 変換済みバッファを変更
-(void)setComposedBuffer:(NSString*)string
{
NSMutableString* buffer = [self composedBuffer];
[buffer setString:string];
}
単に NSMutableString のラッパーです。setOriginalBuffer: も同様です。
つぎに、まだ変換されていない場合です。元バッファが存在して空かどうかを調べます。空なら、そのまま戻ってしまいますが、ここに入ってくるのは、数字以外の文字であり、そのまま利用側に送られて確定されるので問題ありません。空でなければ、数字以外が入ってきたので、すでにある分を変換してしまいます。アプリケーション委任から変換エンジンを取得し、それに文字列を変換させています。それを変換済み文字列へと設定します。空白が入力(すなわち変換キー)なら、そのまま変換されたものを利用側へと知らせます。この時点では、まだ確定されないことになります。
空白でなかったら、変換されたものを確定します。それから、今回の文字をさらに利用側へと送ります。ここで確定のためのメソッド commitComposition: が出てきました。これは IKMServerInput プロトコルで宣言されているメソッドです。「構成が確定されたほうがいいことをコントローラーに知らせます。」とあり、通常、何らかの理由で利用側が確定を強制したい場合に使います。ここでは、自らそれを呼んでいるわけです。このなかで、上の変換済みが存在した場合のような後片付けも行います。
NumberInputController.m > commitComposition:
/*!
@method
@abstract 入力セッションを終了させるユーザー行動がとられた時、呼び出される。
通常、ユーザーが新しい入力メソッドまたはキーボードレイアウトを選択した時に発動される。
@discussion このメソッドが呼び出された時、コントローラーは insertText:replacementRange:
の呼び出しを通じて、現在の入力バッファを利用側に送ったほうがいい。
さらに、必要なら、後片付けを行う時でもある。
*/
-(void)commitComposition:(id)sender
{
NSString* text = [self composedBuffer];
// 変換済みを取得
if ( text == nil || [text length] == 0 ) {
// 変換済みが存在しないか、空なら
text = [self originalBuffer];
// 元バッファを使用
}
[sender insertText:text replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
// 利用側にテキストを送る
[self setComposedBuffer:@""];
// 変換済みを空に
[self setOriginalBuffer:@""];
// 元バッファを空に
_insertionIndex = 0;
// 挿入位置を 0 に
}
まず、変換された文字列を取得します。それがなかったり、空だったら、かわりに元バッファを取得します。そして、取得した文字列を利用側へと送ります。その後で、後片付けをします。このように単純なメソッドになっています。
さて、まず前のプロジェクトでも実装されていた inputText: client: と、それに関連してくるメソッドを調べました。前のプロジェクトでも触れたように、このサンプルでは、キーバィンディングを使った方法でテキスト処理をしています。この方法の場合、まずアクションメソッドに対応付けられるかが調べられ、そうなら didCommandBySelector: client: が呼ばれます。対応付けられなかった場合、inputText: client: が呼ばれることになります。この NumberInput 1 では、先の方のメソッドも実装されています。その実装を見てみます。
NumberInputController.m > didCommandBySelector: client:
// このメソッドは入力メソッドが NSResponder アクションを処理するかどうかを調べるために呼ばれる
-(BOOL)didCommandBySelector:(SEL)aSelector client:(id)sender
{
if ([self respondsToSelector:aSelector]) {
// insertNewline: や deleteBackward: のような NSResponder メソッドは void を返す
// didCommandBySelector メソッドは、コマンドが処理されたら YES、
// そうでないなら NO を返すことを要求する。
// これは未処理コマンドを利用側アプリに渡せるようにするため必要になる。
// このため、コマンドが処理されなかったかもしれない場合をテストする必要がある。
// ここでのテストは単純。元のバッファにテキストが追加されたかを調べる。
NSString* bufferedText = [self originalBuffer];
// 元バッファを取得
if ( bufferedText && [bufferedText length] > 0 ) {
// 元バッファが存在して空でないなら
if (aSelector == @selector(insertNewline:) ||
aSelector == @selector(deleteBackward:) ) {
// 定義しているセレクタの場合だけ実行
[self performSelector:aSelector withObject:sender];
return YES;
}
}
}
return NO;
}
セレクタが存在して実行された場合は YES を返します。それ以外は NO が返ることになります。コメントでいろいろ書かれていますが、実装されているのは、もっと単純なものです。このサンプルでは、2 つのメソッドだげが実装されています。これらは、入力バッファ管理の関連上、そのまま利用側へと渡すわけにはいかないものです。キーバインディングについてあまり知らない人は、『Cocoa イベント処理ガイド』の「テキストシステムデフォルトとキーバインディング」の「キーバインディング」を見てください。各キーがどういうセレクタにバインドされているかを調べるには、/System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict を見てください。そこに insertNewline: と deleteBackward: も含まれています。これらを見ていきます。
NumberInputController.m > insertNewline:
// 改行が入力されたとき、構成を確定する。
- (void)insertNewline:(id)sender
{
[self commitComposition:sender];
}
単に確定メソッドを呼び出しているだけです。
NumberInputController.m > insertNewline:
// バックスペースが入力されたら、直前の文字を除去し、マークされたテキストを更新。
- (void)deleteBackward:(id)sender
{
NSMutableString* originalText = [self originalBuffer];
// 元バッファを取得
NSString* convertedString;
if ( _insertionIndex > 0 && _insertionIndex <= [originalText length] ) {
// 挿入位置が 0 より大きく、テキスト範囲に含まれていたら
--_insertionIndex;
// 挿入位置を減分
[originalText deleteCharactersInRange:NSMakeRange(_insertionIndex,1)];
// 1文字削除
convertedString = [[[NSApp delegate] conversionEngine] convert:originalText];
// 元バッファから再変換
[self setComposedBuffer:convertedString];
// 変換済みバッファを再設定
[sender setMarkedText:convertedString
selectionRange:NSMakeRange(_insertionIndex, 0)
replacementRange:NSMakeRange(NSNotFound,NSNotFound)];
// 変換済み文字列を利用側に知らせる
}
}
コメントでわかると思います。直前の文字がある場合だけコマンドを実行します。
残るメソッドは、menu と dealloc だけです。後者はわかると思います。プロジェクト、もしくはターゲットのビルド設定で、「GCC 4.0 Code Generation」設定で、「Objective-C Gabage Collection」が「Unsupported」になっていることに注意してください。このため、dealloc メソッドが実装されています。さて、menu を調べます。これは IMKInputController から継承されたメソッドです。
NumberInputController.m > menu:
-(NSMenu *)menu
{
// アプリケーションコントローラーオブジェクトは
// awakeFromNib 内でグローバルにされている
// そのため、このメソッドはアプリケーションコントローラー内
// の IBOutlet であるメニューにアクセスできる。
NSLog(@"in menu. menu = %@", [[NSApp delegate] menu] );
return [[NSApp delegate] menu];
}
ここでは、アプリケーションの委任からメニューを取得しています。最初の「変換機能」で書いたように、このサンプルを実際に動かした時に、コンソールにエラーが出力されます。これは、アプリケーションの委任クラスがまだ menu メソッドを実装しておらず、このメニューが取得できないからです。この段階では、このメソッドは実装しないほうがいいものです。
さて、これでこのサンプルの説明は終わりました。次のサンプルでは、さらに機能が追加されることになります。「変換機能」の所で触れませんでしたが、このサンプルを実行すると、さまざまなエラーが起こります。数字を入力して変換した後で、数字を入力すると、前の入力に追加されるものの、再変換または確定すれば、追加された数字は消失します。他にもいくつか動作の変な点があり、それはこのサンプルのロジックがきちんとしておらず、すべての場合に対応できていないことを意味します。あくまで入力メソッドの実装例なので、気にしないほうがいいと思います。(以下、次ファイルへと続く。)
管理人:神吉 秀典 E-mail: