uzullaの日記

本家:about等:Twitter

2008-06-15

validateNow()とかLabelとかについて、常識なのかしらないけど…

| 09:57

ASで文字列を表示させるとき、

var _label:Label = new Label();
_label.text = 'ほげほげ';
this.addChildren(_label);

とかやりますやん、でもコレだと表示できませんよね。

これは当然ですね、横幅がないんです。

Widthとheightを指定してあげなきゃいけないですね。

指定してみた

var _label:Label = new Label();
_label.text = 'ほげほげ';
_label.width = 40;
_label.height = 20;
this.addChildren(_label);

こうやると表示できますね、うん。

でもこのテキストの内容が可変の時、過不足ないサイズに広げるにはどうすればいいのか。

Mxmlとかでやると、自動的にサイズが広がるから値は取れるはず。

リファレンスみたら1分で解りました。


textWidth,textHeightを使うね

var _label:Label = new Label();
_label.text = 'ほげほげ';
_label.width = _label.textWidth;
_label.height = _label.textHeight;
this.addChildren(_label);

textWidthとtextHeightは入力されたテキストの(仮想的な)横幅と縦幅を取り出せます。

こうすると、過不足ないサイズのLabelができそうですよね。


でも、やると解りますが、これはダメなコードなんですよね。_label.widthは0になっちゃう。

これは_label.textWidthの返値がNaNであるからなんですね(だじゃれ)

最初上の問題がなんで出るのかわかんなかったんですけど、なんか別の関数に飛んだりして、ちょっと経つと取得できるようになる、ってことはImageとかと同様に、非同期でやらなきゃいけない?そんなのイヤだなーとかおもってたら、そんなことはなかった。


validateNow()を使う。

ドキュメント*1を読んだら、validateNow()を読んだらその瞬間に様々な計算がされて、textWidthとかが取得できるよーって書いてあった、なるほどー!

var _label:Label = new Label();
_label.text = 'ほげほげ';
_label.validateNow();
_label.width = _label.textWidth;
_label.height = _label.textHeight;
this.addChildren(_label);

こうですか!


でもなぜかうごきませんでした!

相変わらずNaNが帰ってきます、なんでだよこのヤロー!(だじゃれ)


オチは、addChildされてなかったから。

結論からいえば、

var _label:Label = new Label();
this.addChildren(_label);
_label.text = 'ほげほげ';
_label.validateNow();
_label.width = _label.textWidth;
_label.height = _label.textHeight;

こう書いたら動くよ!addchildrenしてからじゃないとちゃんと計算してくれないらしい。

めでたしめでたし!


個人的には全部設定してからaddChildしたかったけど、そういう書き方は普通じゃないんだね。

まずaddChildrenがAS的なんだろうか。


しかし文字が欠ける

でも上のをやってみると完璧ではなくて、文字の下pxと、右が数px欠けます。

なんでだろーとおもったけど、多分これはPaddingがあるんだろうなー。

(多分これのWidthはLabelの子のUITextFieldの幅なんだよね)

だから、多分StyleのpaddingLeftとか、もしくはTextLineMetricsを取得して、適当に足し算してあげればいいんだろう。


でもこれは試してません、安易に適当なpx数足してにげちゃったから(苦笑)


こういう超基本的な事は

どこで学ぶべきなんだろうか。

ググったけど、ぱっとはこの問題みつけられなかったんだよね。

(だから、このエントリを書いた)

言語を勉強するときはリファレンスだけ読めばいいやでやっているけど、Flex(Flash)は複雑なだけに結構暗黙の了解的な事が多く、こういう皆ならわかっている様な所でいちいち突っかかるよ…。

2008-06-11

単なるflatなボタンが欲しいんだけど

| 07:49

ただ単に、一色で、ボーダーとかなく、グラデとかなく、プレーンなFlatボタンがほしいんだけど…。

(カッコイイとかじゃなく、デザイナーの指示がそうだったので…)


Buttonクラスは色々な設定ができるのだが(プロパティが数えられないほど)、

	<mx:Button label="text"   
		fillAlphas="[1.0, 1.0]" 
		fillColors="[#990000, #990000, #FF0000, #FF0000]" 
		color="0xFFFFFF"
		textRollOverColor="0xFFFFFF"
        	textSelectedColor="0xFFFFFF"
        	highlightAlphas="[0.0, 0.0]"
        	borderColor="0x00990000"
 		themeColor="#FF0000"/>

とか色々やっても完全にプレーンなものがつくれなかった。

何となくソレっぽいから俺的にはもうこれでいいんじゃね?とおもうけど、くおりてぃちぇっく通らないとめんどくさいな…。


…簡単なコンポーネント書くか…。


mx.skins.halo.ButtonSkin?

mx.skins.halo.ButtonSkin とかいうのと同等な物を用意すればできるんかなあ、

でもよくわからんし、自分で書いたほうが速いな…間違いなく。


よく考えれば(考えなくても)

静的な画像を用意して、RollOverで差し替えるっつうHTML的な手法でもいいよなー…w

デザイナーから上がってきた画像にできるだけ似せる、というだけの話だし。


書いた

Enable/Disable辺りが適当すぐる、単に消すだけってww

package jp.cfe.widget
{
	
	import flash.events.MouseEvent;
	
	import mx.controls.Label;
	import mx.core.UIComponent;
	import mx.events.ResizeEvent;
/*
 フラットなボタンを作成する

	btn = new FlatButton();
	btn.setupByText(genImg, 0x990000, 0xFF0000, "ボタンテキスト", 0xFFFFFF,10,10);
	btn.move(250, 390);
	this.addChild(btn);

	btn.enable = false; //ボタンを隠す
*/

	public class FlatButton extends UIComponent
	{
		private var elipseWidth:uint = 1;
		private var elipseHeight:uint = 1;
		private var color:uint = 0;
		private var roColor:uint = 0;
		private var text:Label = new Label();
		
		private var vPadding:uint = 3 * 2;
		private var hPadding:uint = 40 * 2;
				
		private var onClickFunction:Function;
		
		public function FlatButton()
		{
			super();
			this.addChild(text);
			//text.text = '';
		}
		
		public function setupByText(_onClickFunction:Function, //クリックされたときの関数
				_color:uint=0x990000, //ボタンボディの色
				_rocolor:uint=0xFF0000, //マウスオーバー時のボタンボディの色
				_text:String="", //テキスト
				_textColor:uint=0x000000, //テキストの色
				_ew:uint=10, _eh:uint=10 //カドの丸さ
				):void
		{
			color = _color;
			roColor = _rocolor;

			text.text = _text;
			text.setStyle("color", _textColor);
			text.setStyle("textAlign", "center");
			text.setStyle("fontWeight", "bold");
			text.setStyle("fontSize", 13 );
			elipseWidth = _ew;
			elipseHeight = _eh;
											
			onClickFunction = _onClickFunction;
			
			this.addEventListener(ResizeEvent.RESIZE, onResize);
			this.addEventListener(MouseEvent.CLICK, onClick);
			this.addEventListener(MouseEvent.MOUSE_OVER, onMouseIn);
			this.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
			
			draw();
		}
		
		//
		// ボタンを押せないように、消す
		//
		public function set enable(_enable:Boolean):void{
			if(_enable){
				this.visible = true;
				this.addEventListener(MouseEvent.CLICK, onClick);
			}else{
				this.visible = false;
				this.removeEventListener(MouseEvent.CLICK, onClick);				
			}
		}
		

		//
		//  ボタンの色を切り替える
		//
		private function drawBase(roFlag:Boolean):void{
				//再描画する
				this.graphics.clear();
				
				var _color:uint = (roFlag == true)? roColor: color;
				this.graphics.beginFill(_color);
				this.graphics.drawRoundRect(0,0,this.width,this.height,elipseWidth,elipseHeight);
		}

		//
		// 描画する
		//		
		private function draw():void
		{
			text.validateNow();

			this.width  = text.textWidth      + hPadding;
			this.height = text.textHeight + 1 + vPadding; //なぜか文字の下1ドットが切れるので

			drawBase(false);
			text.width = this.width;
			text.height = text.textHeight + 1;
			text.y = this.height/2 - text.height/2 - 2;

		}
		
		
		private function onClick(e:MouseEvent):void{
			onClickFunction();
		}	
		private function onResize(e:ResizeEvent):void{
			draw();
		}
		private function onMouseIn(e:MouseEvent):void{
			drawBase(true);
		}
		private function onMouseOut(e:MouseEvent):void
		{
			drawBase(false);
		}
		
	}
}

まあ、これくらいだと俺みたいなダサ坊でも数十分で書ける訳で、Flexは楽でイイですね。



FlexにJPEG画像を読み込ませる為に、一度サーバーで反射させるやり方

| 05:35

最近なんにも書いてないので。

Flexで画像を編集するツールを書いているが、Flexだと当然ローカルファイルがよめないので、一度サーバーにULして、それをDLしてローディングする必要がある。


リフレクター

サーバーPHPとかでこんなのを設置する


<?php
/*
Filedataというnameでファイルをポストすると、
./images/{GUID}_filename.jpg
に保存し、URLの文字列をレスポンスする。
*/

$MAXIMUM_FILESIZE = 1024 * 2000; // 2000KB
$MAXIMUM_FILE_COUNT = 10; // keep maximum 10 files on server

require("Guid.php");
$Guid = new Guid();
$filename = $Guid->toString() . "_" .$_FILES['Filedata']['name'];

if ($_FILES['Filedata']['size'] <= $MAXIMUM_FILESIZE)
{
  move_uploaded_file($_FILES['Filedata']['tmp_name'], "./temporary/".$filename);
  $type = exif_imagetype("./temporary/".$filename);

  if ($type == 1 || $type == 2 || $type == 3){ // GIF or JPG or PNG.
    rename("./temporary/".$filename, "./images/".$filename);
  } else{ // Not popular image format.
    unlink("./temporary/".$filename);
    echo "NG";
    exit;
  }
}else{
  echo "OVERSIZE";
  exit;
}

$directory = opendir('./images/');
$files = array();

while ($file = readdir($directory))
{
  array_push($files, array('./images/'.$file, filectime('./images/'.$file)));
}

usort($files, sorter);

if (count($files) > $MAXIMUM_FILE_COUNT)
{
  $files_to_delete = array_splice($files, 0, count($files) - $MAXIMUM_FILE_COUNT);
  for ($i = 0; $i < count($files_to_delete); $i++)
    {
      @unlink($files_to_delete[$i][0]);
    }
}

closedir($directory);
print("http://localhost/images/".$filename);

function sorter($a, $b)
{
  if ($a[1] == $b[1])
    {
      return 0;
    }
  else
    {
      return ($a[1] < $b[1]) ? -1 : 1;
    }
}
?>

上のコードはかなり適当、Adobeのサンプルコード*1を多少弄った物。

guid.php*2は適当なランダムファイル名を付ける為の物。

処理としては、POSTされたファイルをサーバーに保存し、そのファイルをHTTPでGETできるURLを返す。

途中でファイルが画像であるかチェックしているが、この辺り省けば何のファイルでも使える。


ライブラリ

次はメインとなるライブラリ

package jp.cfe.file
{
	import flash.events.DataEvent;
	import flash.events.Event;
	import flash.net.FileFilter;
	import flash.net.FileReference;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	
	import mx.managers.CursorManager;

/**
 * サーバーにファイルをアップロードして、アップロードされたファイルのURLを取得する。
 * var rl:ReflectLoad = new ReflectLoad(callbackFunction);
 * rl.setFileFilter("Images", "*.jpg;*.gif;*.png");
 * rl.browse();
 * 
 * アップロードが終わったらコールバック関数が呼ばれる。
 * 
 */
	public class ReflectLoad
	{
		
		private var _fr:FileReference;
		private var _ff:FileFilter = null;
		
		public var fileUrl:String;
		public var status:Boolean = false;
	
		public var reflectorUrl:String = "http://localhost/ul.php";
		public var uploadParamName:String = "Filedata";

		public var responseHandlerFunction:Function;

		public function ReflectLoad(_func:Function)
		{
			_fr = new FileReference();
			_fr.addEventListener(Event.SELECT, imgSelectHandler);
			_fr.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,imgUploadCompleteHandler);
			responseHandlerFunction = _func;
		}

		public function browse():void{
			_fr.browse([_ff]);
		}

		public function setFileFilter(desc:String, suffix:String):void{
			_ff = new FileFilter(desc, suffix);
		}

		private function imgSelectHandler(event:Event):void{
			//マウスカーソルをBusyに
			CursorManager.setBusyCursor();
			
		    var request:URLRequest = new URLRequest(reflectorUrl);
		    var params:URLVariables = new URLVariables();

		    request.method = URLRequestMethod.POST;
		    request.data = params;

		    _fr.upload(request,uploadParamName);
		}
		
		private function imgUploadCompleteHandler(event:DataEvent):void{
			 if(event.data.toString().substr(0,4) == 'http'){
		 		//Alert.show(event.data.toString());
				fileUrl = event.data.toString();
				status = true;
				responseHandlerFunction(this);
			 }else{
			 	// maybe, server was failed.
				status = false;
				responseHandlerFunction(this);
			 	//Alert.show("something wrong");
			 }
			 
 			//マウスカーソルをnomalに
			CursorManager.removeBusyCursor();
		}


	}
  
}


利用例

で、上のライブラリはこんな風に使う。

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import mx.controls.Image;
import mx.managers.CursorManager;
	
import jp.cfe.file.ReflectLoad;
	
private _loader:Loader;

//
// reflectLoadを用いて画像をロード 1
// ファイル選択画面を出す
//
private function loadNewImg():void{
	var _rl:ReflectLoad = new ReflectLoad(rlCallback);
	_rl.setFileFilter("Images", "*.jpg;*.gif;*.png");
	_rl.browse();
}
//
// reflectLoadを用いて画像をロード 2
// ULした画像をLoaderでDLロードする
//
private function rlCallback(_rl:ReflectLoad):void{
	if(_rl.status){
		//マウスカーソルをBusyに
		CursorManager.setBusyCursor();
		_loader = new Loader();
		_loader.load( new URLRequest(_rl.fileUrl) );
		_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, completedata );
	}else{
		//ULにミスっているのでエラー表示とか

	}
}
//
// reflectLoadを用いて画像をロード 3
// ロードが終わったloaderのデータをBitmapDataに展開し、諸々の処理をする。
//
public function completedata(e:Event):void{
	bmd = new BitmapData(_loader.content.width, _loader.content.height);
	var _image:Image = new Image();
	_image.source = new Bitmap(bmd);
	this.addChild(_image);
	CursorManager.removeBusyCursor();
}

こんな感じでロードする。


こんな面倒くさい事を本当はしたくないので

セキュリティ的な問題で直接ロードが出来ないようになっていると思うのだが、やる側がやったらこんな風にできるような事なので、fileReferenceから直接ByteArrayとかにロードできる仕組みが欲しいとおもった。

AIRとかだとできるんだけどなー。


後、Flexはイベント駆動なので、単にULしてDLしてロードするのに三個も関数をまたがねばならない。

実際、上の例だと_imageがロードされるイベントも待たなければならないので…めんどくさい。

もっとすっきり書ければいいのにな。

2008-05-26

Flexerになりつつある

| 02:58

あらためて、Flex Builderってスゲーなーとおもいつつ仕事をしている。

だって3万円しかしないんだぜ?これで。

同じ*1swfができるのに、Flashは8万とかありえないよねー(Flashも持ってるけど…つかってないwwww)。


そして、ローカルファイルが読み込めれば!いいのに!と思いつつ仕事している。*2

AIR?…お客さんがAIRとかいれてくれるのは何年後になるんだろう…。


3日ばかりがんばってloaderとかUIComponentとかBitmapDataとか、RectangleとかMatrixとか、addEventListerとか、そこらへんが解ってきた。

イベント駆動型のコーディングテラ解りづらす、単に画像のサイズ知りたいだけなんに、いちいちイベントつかうとか、なんぞー。


FileReferenceでjpg選択してサーバーにファイルULして、そのファイルをLoaderでDLしてひらいて、BitmapDataにして、Imageにして、リサイズしたりドラッグ移動したり。BitmapDataをsetPixel getPixelなどして回転させたり。まあそんなことをやっている。

*1:ではないのだが、Flexで出力したswfテラデカス

*2:まあサーバーにULして、DLさせればいいんだけど

2008-04-07

テキストをビットマップに変換し、二次元配列におさめてく

| 14:29

TextFieldでBitmapDataにDrawして、それをgetPixcelで配列に納めていく。

どんなことに使うのかは書かないが(仕事で使うから)、なるほどこうやれば文字列の画像を生成したりできるんだなーと知った。

import flash.display.*;
import mx.core.UIComponent;
import flash.display.Sprite;
import flash.text.*;

private var bmpArray:Array = []
private var _bmd:BitmapData
private var label:TextField
private var format:TextFormat

public function genData(_string:String, _fontsize:int):Array{
	//テキストの書式を指定
    format = new TextFormat();
    format.font ="_等幅";
    format.color=0x000001;
    format.size = _fontsize

	//テキストを生成
    label = new TextField();
    label.autoSize=TextFieldAutoSize.LEFT;
	label.text = _string
	label.setTextFormat(format);

	//テキストをビットマップ描画
	_bmd = new BitmapData(label.width, label.height, true, 0xFFFFFF)
	_bmd.draw(label)
	
	//ビットマップを1ピクセルずつ拾い、配列に格納
	var pixelnum:uint = _bmd.width * _bmd.height
	for(var i:uint=0; i < pixelnum; i++){
		var ty:int = (i / _bmd.width) + 1
		var tx:int = (i % _bmd.width) + 1
		if (bmpArray[ty-1] == null){
			bmpArray[ty-1] = [];
		}
		if (_bmd.getPixel(tx,ty) > 0) {
			bmpArray[ty-1][tx-1] = 1
		}else{
			bmpArray[ty-1][tx-1] = 0
		}
	} 
	// return bmpArray
	

	//ビットマップを実際に表示するには
	//Bitmapオブジェクトを上でつくったデータを引数に生成し、
	var bmp:Bitmap = new Bitmap(_bmd)
	//FlexだとBitmapを直接Canvas等にaddchildできないので、
	//一端UIComponentでラップして
	var ui:UIComponent = new UIComponent();
	ui.addChild(bmp)
	//表示
	addChild(ui)
}


BitmapDataをつかうと、色々生な画像がいじれる。

Loaderなどと連携させれば、普通の画像を画像処理したりとか、色々出来る。

2008-04-03

動画を切り抜く

| 13:05

Flexで、FLVとかを再生し、それを適当な形に切り抜く方法。

DisplayObject.maskをつかうと切り抜きができる。

http://livedocs.adobe.com/flex/2_jp/langref/flash/display/DisplayObject.html#mask

<mx:Script>
<![CDATA[
private function init():void
 {
  cv.graphics.clear();
  cv.graphics.lineStyle(0,0,0);
  cv.graphics.beginFill(0xFF0000);
  cv.graphics.moveTo(100, 100 );
  cv.graphics.lineTo(200, 100 );
  cv.graphics.lineTo(200, 200 );
  cv.graphics.lineTo(100, 200 );
  cv.graphics.endFill();	
  yp.mask = cv
 }
]]>
</mx:Script>
	<mx:VideoDisplay x="0" y="0" width="100%" height="100%"
		 source="http://hoge/hage.flv" id="yp" autoPlay="true"/>
	<mx:Canvas width="100%" height="100%" id="cv">
	</mx:Canvas>
</mx:Canvas>

超かんたんですね。

最初は上の切り抜きマスクではなく、本当のマスク(本当に上にオブジェクトをのせて隠す)で実装してたんですが、丸く切り抜きたいといわれて挫折しましたw

ただ、複雑な図形を書いて、なおかつ解像度が高いとすげえ重い。

特に円とかで切り抜くと、アンチエイリアスが効くのだろうな…。