カテゴリー : プログラミング

【actionscript 3.0】テキストフィールドの入力で半角、全角区別して文字数制限

actionscript 3.0のテキストフィールドの入力で文字数でなく半角、全角で文字数制限したいという事で作りました。重要なのはByteArray.writeMultiByte(value:String, charSet:String)だけで、charSetを”shift-jis”にすると(確証はないが)半角と全角を判別できるみたい。私はFlashのIDE上に配置されたオブジェクトでなるべく解決したいのでProxyクラスを継承するやり方で実装しました。なのでTextFieldクラスを継承してnewでインスタンスを作ってステージ上に配置してやった方がスマートだとは思います。内部でイベントも持ってしまっているのであまり良いやり方ではないのも分かっていますがやりたい事は実現できているので一応クラス載せます。
※.maxCharsと同様スクリプトでの文字数などの制限はしていません。テキスト入力があった時のみの制御しかしていません。
TextFieldProxyクラス

package {

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	import flash.text.TextField;
	import flash.utils.ByteArray;
	import flash.events.Event;
	import flash.events.TextEvent;

	public dynamic class TextFieldProxy extends Proxy {

		private const _charset:String = "shift-jis";
		private var _textField:TextField;
		private var _maxBytes:uint;
		private var _oldCaretIndex:uint;
		private var _oldText:String;

		public function set maxBytes(value:uint):void {
			_maxBytes = value;
			_textField.addEventListener(TextEvent.TEXT_INPUT, onInput);
			_textField.addEventListener(Event.CHANGE, onChange);
		}

		public function get maxBytes():uint {
			return _maxBytes;
		}

		public function get bytes():uint {
			var byteArray:ByteArray = new ByteArray();
			byteArray.writeMultiByte(_textField.text, _charset);
			var length:uint = byteArray.length;
			byteArray.clear();
			return length;
		}

	    public function TextFieldProxy(textField:TextField) {
	       _textField = textField;
	    }

	    override flash_proxy function callProperty(methodName:*, ... args):* {
	        return _textField[methodName].apply(_textField, args);
	    }

	    override flash_proxy function getProperty(name:*):* {
	        return _textField[name];
	    }

	    override flash_proxy function setProperty(name:*, value:*):void {
	        _textField[name] = value;
	    }

		public function onInput(event:TextEvent):void {
			(event.target as TextField).replaceSelectedText("");
			_oldText = (event.target as TextField).text;
			_oldCaretIndex = (event.target as TextField).caretIndex;
		}

		public function onChange(event:Event):void {
			var newText:String = (event.target as TextField).text;
			var newByteArray:ByteArray = new ByteArray();
			newByteArray.writeMultiByte(newText, _charset);
			if(newByteArray.length > _maxBytes) {
				var addStr:String = newText.substr(_oldCaretIndex, newText.length - _oldText.length);
				var addByteArray:ByteArray = new ByteArray();
				addByteArray.writeMultiByte(addStr, _charset);
				addByteArray.position = 0;
				addStr = addByteArray.readMultiByte(_maxBytes - (newByteArray.length - addByteArray.length), _charset);
				(event.target as TextField).text = _oldText.slice(0, _oldCaretIndex) + addStr + _oldText.slice(_oldCaretIndex, _oldText.length);
				_oldCaretIndex += addStr.length;
				if(!(event.target as TextField).multiline) {
					(event.target as TextField).setSelection(0, 0);
				}
				(event.target as TextField).setSelection(_oldCaretIndex, _oldCaretIndex);
				addByteArray.clear();
			}
			newByteArray.clear();
		}
	}
}
実装
//TextField txt1;
//TextField label1;
//TextField txt2;
//TextField label2;

txt1.text = "";
txt1.background = true;
txt1.backgroundColor = 0xF2F2F2;
txt2.text = "";
txt2.background = true;
txt2.backgroundColor = 0xF2F2F2;

var txtProxy1:TextFieldProxy = new TextFieldProxy(txt1);
txtProxy1.maxBytes = 10;
txtProxy1.addEventListener(Event.CHANGE, onChange);
label1.text = txtProxy1.bytes + "/" + txtProxy1.maxBytes;

var txtProxy2:TextFieldProxy = new TextFieldProxy(txt2);
txtProxy2.maxBytes = 30;
txtProxy2.addEventListener(Event.CHANGE, onChange);
label2.text = txtProxy2.bytes + "/" + txtProxy2.maxBytes;

function onChange(event:Event):void {
	switch(event.target) {
		case txt1:
			label1.text = txtProxy1.bytes + "/" + txtProxy1.maxBytes;
			break;
		case txt2:
			label2.text = txtProxy2.bytes + "/" + txtProxy2.maxBytes;
			break;
	}	
}
サンプル
This movie requires Flash Player 10

データ一式(Flash CS4以上)
ダウンロード

Objective-C 覚え書き

Objective-Cをはじめたので、忘れないうちにここに覚え書き。
ActionScriptは仕事で一番良く触れる言語なので、それと比較しながら書いていこうかと思います。
少しずつ、気長にやっていこうかと思う。

ますは開発環境。
WindowsでもObjective-Cコンパイルできる環境にしたい。
と言う事でインターネット検索したところ良い記事がありました。
長いものに巻かれて行こうぜ! 1巻き目を参考に環境を構築した。
環境が整った所でエディタを開き、開発を開始した。

以下ActionScriptとObjective-Cの比較です。
そして記事の後半で理解した範囲内での簡単なプログラムの作成と総括を書きました。

コメントアウト

ActionScript 3.0
trace("Hello");
Objective-C
printf("Hello");

コンソール出力

ActionScript 3.0
trace("Hello");
Objective-C
printf("Hello");

汎用変数の宣言

ActionScript 3.0
var obj:*;
Objective-C
id obj;

インターフェースとクラス

ActionScript 3.0 TestInterface.as
package {
	public interface TestInterface {
		function test():void;
	}
}
ActionScript 3.0 TestClass.as
package {
	public class TestClass implements TestInterface {
		public function test():void {
			trace("Hello");
		}
	}
}
Objective-C
//@interface クラス名:スーパークラス
@interface TestClass:Object
	//- (戻り値の型) メソッド名
	- (void) test;
@end

//@implementation クラス名
@implementation TestClass
	// (戻り値の型) メソッド名
	- (void) test {
		printf("Hello");
	}
@end

クラスのインスタンス生成

ActionScript 3.0
var obj:TestClass = new TestClass();
Objective-C
id obj = [TestClass alloc];

クラスメソッドの呼出し

ActionScript 3.0
obj.test();
Objective-C
[obj test];

実行

ActionScript 3.0 (ドキュメントクラスを指定した場合)
package {
	import flash.display.MovieClip;
	public class Main extends MovieClip {
		public function Main():void {
		}
	}
}
Objective-C (引数なし、戻り値なし)
main() {
}
Objective-C (引数あり、戻り値あり)
int main(int argc, char *argv[]) {
	return 0;
}

理解した範囲内での簡単なプログラム

#import <objc/Object.h>

@interface TestClass:Object
	- (void) test;
@end

@implementation TestClass
	- (void) test {
		printf("Hello");
	}
@end

main() {
	id obj = [TestClass alloc];
	[obj test];
}

総括

[]はObjective-Cの世界ではメッセージ式といい関数の呼び出しに使われるようだった。
コンパイル時にプログラム末尾に改行がない場合、警告が表示された。

>>追記:2010/05/16
上記の”理解した範囲内での簡単なプログラム”はMac(Snow Leopard)のXcodeでコンパイルしたところ大量の警告が発生した。先に修正したプログラムを載せておきます。

修正したプログラム

#import <Foundation/Foundation.h>

/*
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // insert code here...
    NSLog(@"Hello, World!");
    [pool drain];
    return 0;
}
*/

@interface TestClass:NSObject {
}
-test;
@end

@implementation TestClass
-test {
	NSLog(@"Hello World!");
	return 0;
}
@end

int main(int argc, char *argv[]) {
	id obj;
	obj = [[TestClass alloc] init];
	[obj test];
	return 0;
}
修正内容は以下の通り。
  • import文
  • インターフェースのスーパークラスをNSObjectに変更
  • インターフェースとクラスの関数の戻り値の型指定をなくした(警告が出たので)
  • インターフェースとクラスの関数名の”-”と”test”の間のスペースを消去し、この行のタブインデントをなくした
  • printfをNSLogに変更
  • main関数の戻り値の型をint型に変更(警告が出たので)
  • 変数宣言してから次の行で代入することにした
一番気になるのがクラスとインターフェースの戻り値の型宣言の部分。何かの設定の違いか、バージョンの違いなのか良く分からない。とりあえずそういうものと理解し先に進む事にします。以下はXcodeでObjective-Cのテスト用に新規プロジェクトを作った時のスクリーンショットです。

1.Xcodeメニュー「ファイル」→「新規プロジェクトを作成」をクリックすると以下のようなウィンドウが出てくる。左のタブで「Application」を選択、右のリスト内の「Command Line Tool」を選択、「Type」というコンボボックスに「Fundation」を選択肢、右下の選択をクリックした。


新規プロジェクト名を適宜入力し、「保存」をクリック。
このような画面が出てくるので、「.m」の拡張子になっているファイルを選択。

画面が狭いので広げた。上記の修正したプログラムに変更した。デフォルトのコードは意味が分からないのでとりあえずコメントアウト。Xcodeのメニュー「実行」→「コンソール」よりコンソールウィンドウを表示させた。ツールバーの「ビルドと実行」でコンソール出力されました。

再総括
printfとNSLogの違いに関してはhttp://vivacocoa.jp/objective-c3e/chapter2.htmlで詳しく書かれていた。今度はwindowsに戻って修正したプログラムを再コンパイルしてみようと思うが、修正したプログラムに警告やエラーになったら何かしら解決方法探さないといけない。先は長いです。

>>追記 その2:2010/05/16

早とちりしすぎた。上記 でインターフェースとクラス内の戻り値の型宣言の部分ではずしたら直ったが、解決方法として間違っていた。原因はtestとかvalueとかはキーワードとして予約されていて、変数名や関数名として使用すると警告されるようだった。ってことで再修正し関数名testをmyTestとした。ちなみに警告は「Also found “-(void)test”」と「Multiple methods named “-test” found」。とりあえず解決して良かった。以下再修正したプログラム。先は長過ぎる。。。

再修正したプログラム

#import <Foundation/Foundation.h>

@interface TestClass:NSObject {
}
-(void)myTest;
@end

@implementation TestClass
-(void)myTest {
	NSLog(@"Hello World!");
}
@end

int main (int argc, const char * argv[]) {
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	id obj;
	obj = [[TestClass alloc] init];
	[obj myTest];

	[pool drain];
	return 0;
}