Flex 预载界面美化 Flex's preloader is not so flex

Flex 的默认的 Preloader, 平心而论,不是很好看。一个个性化的Preloader,基本上要包括三个部分。png-1743

1. 公司或者网站Logo,或者个性化的预载图片。好似是桌面软件的Splash Screen。

2. 载入数据的进度,文字形式的百分比。

3. 载入进度条。

preloader不像Flex项目中普通的部件可以通过CSS进行设置,是因为当程序初始载入时,CSS文件的设定还未被载入,所以不好通过CSS进行外观的控制。

这里有Ted把SWF,GIF 和 PNG 文件作为 Preloader 的教程。不过这里没有上述元素三合一的例子。

把网上的资料总结一下,这里放个三合一的例子。最终效果预览,

loader

 

设置WEB程序的Preloader为自制的Preloader时,要修改主程序Application标签中的preloader属性,

<?xml version="1.0" encoding="utf-8"?>


...

这里的com.ibsol.view.Preloader是自定义Preloader类的路径。

Preloader要扩展DownloadProgressBar类才能设置自己的Preloader

package com.ibsol.view
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.text.TextField;
	import flash.utils.Timer;
	import mx.events.FlexEvent;
	import mx.preloaders.DownloadProgressBar;
	
	public class Preloader extends DownloadProgressBar
	{
		//显示进度的文字
		private var progressText:TextField;
		//进度条
		public var img:WelcomeScreen;
		//logo页面
		public var logo:WelcomeLogo;
		private var _timer:Timer;
		
		public function Preloader()
		{
			super();
			//加入logo
			logo = new WelcomeLogo();
			this.addChild(logo);
			//加入进度条
			img = new WelcomeScreen();
			this.addChild(img);
			//加入进度文字
			progressText = new TextField();
			progressText.x = 40;
			progressText.y = 90;
			this.addChild(progressText);
			//进度条计时器初始化,我们实现进度条的原理就是不停的刷新进图条图片
			//因为每次进度条的长度不同,所以就有进度条在走的效果
			_timer = new Timer(1);
			_timer.addEventListener(TimerEvent.TIMER, drawTimerHandler);
			_timer.start();
		}
		/**
		 * override这个函数,来实现自己Preloader的设置,而不是用其默认的设置
		 */
		override public function set preloader(value:Sprite):void
		{
			value.addEventListener(ProgressEvent.PROGRESS, progHandler);
			value.addEventListener(FlexEvent.INIT_COMPLETE, initCompleteHandler);
			//在这里设置预载界面居中
			//如果在初始化函数中设置,会有stageWidth和最终界面大小不一致的错误,而导致不能居中
			x = (stageWidth/2) - (300/2);
			y = (stageHeight/2) - (180/2);
		}
		
		private function progHandler(e:ProgressEvent):void
		{
			//计算进度,并且设置文字进度和进度条的进度。
			var prog:Number = e.bytesLoaded/e.bytesTotal*100;
			progressText.text = "已下载 " + String(int(prog)) + "%";
			if(img)
			{
				img.progress = prog;
			}
		}
		
		private function compHandler(e:Event):void
		{
			
		}
		
		private function initCompleteHandler(e:FlexEvent):void
		{
			//如果载入完毕,则停止刷新
			img.ready = true;
			_timer.stop();
			//测试专用。下载完毕后,不马上跳到程序的默认界面。而是停顿10秒后再跳入。
			var timer:Timer = new Timer(10000, 1);
			timer.addEventListener(TimerEvent.TIMER, dispatchComplete);
			timer.start();
		}
		
		private function initProgHandler(e:FlexEvent):void
		{
			
		}
		/**
		 * 一定要分发这个事件,来通知程序已经完全下载,可以进入程序的默认界面了
		 */
		private function dispatchComplete(event:TimerEvent):void
		{
			this.dispatchEvent(new Event(Event.COMPLETE));
		}
        /**
        * 每个时钟周期都刷新进度条图片
        */
        private function drawTimerHandler(event:TimerEvent):void
        {
        	img.refresh();
        }

	}
}

图片logo层的实现方式是

package com.ibsol.view
{
	import flash.display.Loader;
	import flash.utils.ByteArray;
	
	public class WelcomeLogo extends Loader
	{
		[Embed(source="assets/preloader.png", mimeType="application/octet-stream")]
		public var WelcomeScreenGraphic:Class;
		public function WelcomeLogo()
		{
			this.loadBytes(new WelcomeScreenGraphic() as ByteArray);
		}

	}
}

进度条和图片可以放在一层,但是考虑到个时钟周期,都要刷新进度条的图片。

毕竟LOGO不用每次都刷新,那么我们就分开放置了。进度条层的代码如下,

package com.ibsol.view
{
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.utils.ByteArray;
	
	import mx.graphics.codec.PNGEncoder;
	
	public class WelcomeScreen extends Loader
	{
		//辅助属性,帮助进行进度条的定位
		private static var _LogoWidth:int = 300;
		private static var _LogoHeight:int = 180;
		private static var _LeftMargin:int = 5;
        private static var _RightMargin:int = 5;
        private static var _TopMargin:int = 1;
        private static var _BottomMargin:int = 1;
        private static var _Padding:int = -60;
		//Progress bar settings
		//进度条的设定,比如显色边框等
		private static var _BarWidth:int = 200;
		private static var _BarHeight:int = 12;
		private static var _BarBackground:uint  = 0xFFFFFF;
		private static var _BarOuterBorder:uint = 0x737373;
		private static var _BarColor:uint = 0x6F9FD5;
        private static var _BarInnerColor:uint = 0xFFFFFF;
        
		private var isReady:Boolean = false;
		public  var progress:Number;
		private var _logoData : BitmapData;
		
		public function WelcomeScreen()
		{
			//this.loadBytes(new WelcomeScreenGraphic() as ByteArray);
		}
		
		override public function get width() : Number
        {
        	return Math.max(_BarWidth, _LogoWidth) + _LeftMargin + _RightMargin;
        }
        
        override public function get height() : Number
        {
        	return _LogoHeight + _BarHeight + _Padding + _TopMargin + _BottomMargin;
        }
        /**
        * 进度载入完毕后,隐藏进度条
        */
        public function set ready(value : Boolean) : void
        {
        	this.isReady = value;
        	this.visible = !this.isReady;
        }
        
        public function get ready() : Boolean { return this.isReady; }
        /**
        * 刷新函数,每个时钟周期都被调用的函数
        */
        public function refresh():void
        {
        	_logoData = this.drawProgressBar();
        	var encoder:PNGEncoder = new PNGEncoder();
        	this.loadBytes(encoder.encode(_logoData));
        }
        /**
        * 进度条生成函数
        */
		private function drawProgressBar():BitmapData
		{
			// create bitmap data to create the data
        	var data : BitmapData = new BitmapData(this.width, this.height, true, 0);
        	// draw the progress bar
        	var s:Sprite = new Sprite();
        	var g:Graphics = s.graphics;
        	// draw the bar background
        	g.beginFill(_BarBackground);
        	g.lineStyle(2, _BarOuterBorder, 1, true);
        	var px : int = (this.width - _BarWidth) / 2;
        	var py : int = _TopMargin + _LogoHeight + _Padding;
        	g.drawRoundRect(px, py, _BarWidth, _BarHeight, 2);
        	var containerWidth : Number = _BarWidth - 4;
        	var progWidth:Number = containerWidth * this.progress / 100;
        	g.beginFill(_BarColor);
        	g.lineStyle(1, _BarInnerColor, 1, true);
        	//根据progress进度,来画出进度条的长度
        	g.drawRect(px + 1, py + 1, progWidth, _BarHeight - 3);
        	data.draw(s);
        	return data;
		}

	}
}

部分代码非原创,不过注释是原创,呵呵,可以参考 Josh's Blog