TweenButton

Progression4での話。
最近、微妙に振る舞いの違うボタンをいくつか作る必要があった。
トグルボタン、ラジオボタン、それらをハイブリッドみたいなボタン。
なるべく共通化してたんだけど、やっぱり作り進むうちに、違いが出て来てしまって、差し替え時にミスをしがちになった。
汎用的なボタンを作るか、インターフェイスを用いるべきだったんだと思った。
とりあえず、普通、トグル、ラジオに対応するボタンを作ってみた。

▼Wonderfl

▼Wonderfl
http://wonderfl.net/code/1f13993add15deb3ecf8c65398c0ace623345a6c

▼ActionScript AS3(FP9)
[sourcecode language=”as3″]
/*
* Progression4での話。
*
* 最近、微妙に振る舞いの違うボタンをいくつか作る必要があった。
* トグルボタン、ラジオボタン、それらをハイブリッドみたいなボタン。
* なるべく共通化してたんだけど、やっぱり作り進むうちに、
* 違いが出て来てしまって、差し替え時にミスをしがちになった。
*
* 汎用的なボタンを作るか、インターフェイスを用いるべきだったんだと思った。
*
* とりあえず、普通、トグル、ラジオに対応するボタンを作ってみた。
*/

// forked from nium’s Progression 4 BasicAppConfig
package {
import flash.display.*;
import jp.progression.config.*;
import jp.progression.debug.*;
import jp.progression.*;

public class Main extends Sprite {

public var manager:Progression;

public function Main() {
Progression.initialize( new BasicAppConfig() );
manager = new Progression( "index", stage, IndexScene );

//Debugger.addTarget( manager );

manager.goto( manager.root.sceneId );
}
}
}

import flash.display.*;
import flash.net.URLRequest;

import jp.progression.casts.*;
import jp.progression.commands.*;
import jp.progression.commands.display.*;
import jp.progression.commands.lists.*;
import jp.progression.commands.net.*;
import jp.progression.commands.tweens.*;
import jp.progression.data.*;
import jp.progression.events.*;
import jp.progression.scenes.*;

class IndexScene extends SceneObject {

public function IndexScene() {
//デフォルト
new TweenButton( { id:"tweenButton0", x:50, y:50 } );

//デフォルト+文字+ダウンイメージを使わない
new TweenButton( { id:"tweenButton1", x:50, y:100 },{text:"Button",isUseDownImage:false} );

//トグル+tweenの時間設定+文字のせ
new TweenButton( { id:"tweenButton2", x:50, y:200 }, { isToggle:true, time:0.3, text:"トグル" } );

//on/offで違う文字のせ+初期状態でon
new TweenButton( { id:"tweenButton3", x:50, y:250 }, { isToggle:true, onText:"ON", offText:"OFF", isOn:true } );

//ラジオボタンの設定+tween無+selected
var group:Array = ["radio0", "radio1", "radio2"];
new TweenButton( { id:"radio0", x:50, y:350 }, { text:"Radio0", radioGroup:group, time:0 , selected:true } );
new TweenButton( { id:"radio1", x:150, y:350 }, { text:"Radio1", radioGroup:group, time:0 } );
new TweenButton( { id:"radio2", x:250, y:350 }, { text:"Radio2", radioGroup:group, time:0 } );

//画像のボタン
var offUpImg:CastImageLoader = new CastImageLoader();
offUpImg.load(new URLRequest("http://farm3.static.flickr.com/2499/3828917483_8948414d57_o.jpg"));
var offOverImg:CastImageLoader = new CastImageLoader();
offOverImg.load(new URLRequest("http://farm3.static.flickr.com/2664/3828917495_8e21ea52c1_o.jpg"));
var offDownImg:CastImageLoader = new CastImageLoader();
offDownImg.load(new URLRequest("http://farm4.static.flickr.com/3542/3829716284_3f77a81e73_o.jpg"));

new TweenButton( { id:"tweenButton4", x:200, y:50 }, { offUp:offUpImg, offOver:offOverImg,offDown:offDownImg } );

}

protected override function atSceneLoad():void {
addCommand(
new AddChild(container, "tweenButton0"),
new AddChild(container, "tweenButton1"),
new AddChild(container, "tweenButton2"),
new AddChild(container, "tweenButton3"),
new AddChild(container, "radio0"),
new AddChild(container, "radio1"),
new AddChild(container, "radio2"),
new AddChild(container, "tweenButton4"),
"Progression 4"
);
}

protected override function atSceneInit():void {
addCommand(
"BasicAppConfig Test"
);
}

protected override function atSceneGoto():void {
addCommand(
);
}
}

import flash.display.Shape;
import flash.geom.Matrix;
import flash.text.TextField;
import flash.display.Bitmap;

class TweenButton extends CastButton {
private var _offUp:DisplayObject;
private var _offOver:DisplayObject;
private var _offDown:DisplayObject;
private var _onUp:DisplayObject;
private var _onOver:DisplayObject;
private var _onDown:DisplayObject;

private var _up:CastSprite;
private var _over:CastSprite;
private var _down:CastSprite;

private var _isOn:Boolean;
private var _selected:Boolean;
private var _isImageSwap:Boolean;
//Downボタンを用意しない場合も多いので、
//無くてもダミーが表示しないようにするには
//isUseDownImage = false;にする。
private var _isUseDownImage:Boolean = true;
//tweenの時間。0にするとtweenerの呼び出しもない
private var _time:Number = 0.6;
private var _isToggle:Boolean;
private var _text:String;
private var _onText:String;
private var _offText:String;
private var _radioGroup:Array;

private var _initialized:Boolean;

public function TweenButton( initObject:Object = null , extendsObject:Object = null) {

super(initObject);

if (extendsObject) {
for (var str:String in extendsObject) {
this["_" + str] = extendsObject[str];
}
}

if (!_onText) {
_onText = _text?_text:null;
}
if (!_offText) {
_offText = _text?_text:null;
}

if (_isToggle) {
if (!_onUp) {
_onUp = getImage(0xFFFF00, _onText);
}
if (!_onOver){
_onOver = getImage(0x00FFFF, _onText);
}
if (!_onDown && _isUseDownImage){
_onDown = getImage(0xFF00FF, _onText, 1);
}
}

if (!_offUp) {
_offUp = getImage(0xFF0000, _offText);
}
if (!_offOver) {
_offOver = getImage(0x00FF00, _offText);
}
if (!_offDown && _isUseDownImage) {
_offDown = getImage(0x0000FF, _offText, 1);
}

_isImageSwap = true;
this.buttonMode = true;

_up = new CastSprite();
_over = new CastSprite();

if(_offDown && _onDown){
_down = new CastSprite();
}else if (_offDown) {
if (!_isToggle) {
_down = new CastSprite();
}
}

_up.addChild(_offUp);
_over.addChild(_offOver);

if (_up) {
this.addChild(_up);
_up.visible = true;
}
if (_over) {
this.addChild(_over);
_over.visible = false;
}
if (_down) {
_down.addChild(_offDown);
this.addChild(_down);
_down.visible = false;
}

//初騎状態を設定
this.isOn = _isOn;
this.selected = _selected;
_initialized = true;
}
private function getImage(rgb:int = 0xFF0000, text:String = null, ty:Number = 0):Bitmap {
var shape:Shape = new Shape();
shape.graphics.beginFill(rgb,0.5);
shape.graphics.drawRoundRect(0,0,60,20,8,8);
shape.graphics.endFill();
shape.graphics.beginFill(rgb,0.5);
shape.graphics.drawRoundRect(2,2,56,16,6,6);
shape.graphics.endFill();
var bitmapData:BitmapData = new BitmapData(shape.width, shape.height);
bitmapData.draw(shape);
if(text){
var textField:TextField = new TextField();
textField.text = text;
textField.width = shape.width;
textField.autoSize = "center";
bitmapData.draw(textField, new Matrix(1, 0, 0, 1, 0, ty));
}
return new Bitmap(bitmapData);
}

//選択状態を維持する用
public function get selected():Boolean { return _selected };
public function set selected(value:Boolean):void {
if (_initialized && _selected == value) { return };
_selected = value;
if (_over || _down) {
if(_over){
_over.visible = value;
}
if(_down){
_down.visible = value;
}
_isImageSwap = !value;
}
this.mouseEventEnabled = !value;
this.mouseEnabled = !value;
}

//ボタンとして機能するんだけど、イメージの差し替えはしない時用
public function get isImageSwap():Boolean { return _isImageSwap };
public function set isImageSwap(value:Boolean):void {
if (_isImageSwap == value) { return };
_isImageSwap = value;
}

//トグルが効いているときに画像の差し替えをする
//onを表示しているときには、trueを返す。
public function get isOn():Boolean { return _isOn };
public function set isOn(value:Boolean):void {
if (_initialized && _isOn == value) { return };
_isOn = value;
if (!_isToggle) { return };
_up.removeAllChildren();
_over.removeAllChildren();
if(_down){
_down.removeAllChildren();
}
if (value) {
_up.addChild(_onUp);
_over.addChild(_onOver);
if(_down){
_down.addChild(_onDown);
}
}else {
_up.addChild(_offUp);
_over.addChild(_offOver);
if(_down){
_down.addChild(_offDown);
}
}
}

//トグルを使う場合はtrueにする。
public function get isToggle():Boolean { return _isToggle };
public function set isToggle(value:Boolean):void {
if (_isToggle == value) { return };
_isToggle = value;
}

override protected function atCastMouseUp():void {
if (_isImageSwap) {
isOn = !isOn;
if (_down) {
_down.visible = false;

//downが無いときに連続のクリックの時にわかりやすくするには、
//_overを消すといいかも。
//}else {
//if (_over) {
//_over.visible = false;
//}
}
}
//ラジオボタン用のグループがある場合に、自分以外を非selected表示に
if (_radioGroup) {
var n:int = _radioGroup.length;
for (var i:int = 0; i < n; i++) {
if ((getInstanceById(_radioGroup[i]) as TweenButton) != this) {
(getInstanceById(_radioGroup[i]) as TweenButton).selected = false;
}
}
this.selected = true;
}
}

override protected function atCastMouseDown():void {
if (_isImageSwap) {
if (_down) {
_down.visible = true;
}else {
//連続のクリックの時にはわかりにくいので、表示
if (_over) {
_over.visible = true;
}
}
}
}

override protected function atCastRollOver():void {
if (_isImageSwap) {
if (_over) {
if (_time > 0) {
var sList:SerialList = new SerialList();
sList.addCommand(
function():void {
_over.visible = true;
_over.alpha = 0;
},
new DoTweener(_over,{alpha:1, time:_time, transition:"easeOutQuart"})
);
sList.execute();
}else {
_over.alpha = 1;
_over.visible = true;
}
}
}
}

override protected function atCastRollOut():void {
if (_isImageSwap) {
if (_over) {
if (_time > 0) {
var sList:SerialList = new SerialList();
sList.addCommand(
new DoTweener(_over,{alpha:0, time:_time, transition:"easeOutQuart"}),
function():void {
_over.visible = false;
}
);
sList.execute();
}else {
_over.visible = false;
}
}
}
}
}

[/sourcecode]