这是一个Sprite的扩展类,增加了Tab管理机制,允许用户通过Tab键(或方向键的左右),来切换内部显示对象的焦点,按Enter代表确认(触发Touch事件)。一般情况下我们的应用是运行在PC或移动设备上的,但现在随着其它一些平台,比如智能电视的发展,如果我们的代码可以支持无鼠标无touch的环境,就可以进一步增强跨平台的特性。

Tab sprite.jpg

运行实例: 点击这里

首先看一下使用方式,非常方便。Starling的入口类就不说了:

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.utils.setTimeout;
	import starling.core.Starling;
 
	import test.Game;
 
	public class TabSpriteTest extends Sprite
	{
		private var _starling:Starling;
 
		public function TabSpriteTest()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			setTimeout(initUI,100);
		}
 
		private function initUI():void
		{
			_starling = new Starling(Game, stage);
			_starling.start();
		}
	}
}

来看看Game类的实现,我们只需要让Game继承TabSprite(代替Sprite),然后注册需要焦点管理的显示对象:

package test
{
	import com.techmx.component.TabSprite;
 
	import flash.display.Bitmap;
	import flash.geom.Rectangle;
 
	import starling.display.Button;
	import starling.display.DisplayObject;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.text.TextField;
	import starling.textures.Texture;
 
	/**
	 * Some domo code show you for how to use TabSprite
	 * @author <a href="http://weibo.com/guoshaorui">NeoGuo</a>
	 */	
	public class Game extends TabSprite
	{
		/**create a textfield to display message*/
		private var textField:TextField;
 
		[Embed(source="../assets/button_bg.jpg")]
		private var buttonBGClazz:Class;
 
		[Embed(source="../assets/Media_flying.png")]
		private var birdClass:Class;
 
		public function Game()
		{
			//create a textfield to display message
			textField = new TextField(400, 40, "Press Tab (or keyboard arrow left and right) change focus, press enter confirm");
			textField.border = true;
			textField.y = 260;
			addChild(textField);
			//create two buttons
			var btnBg:Bitmap = new buttonBGClazz();
			for (var i:int = 0; i < 2; i++) 
			{
				var button:Button = new Button(Texture.fromBitmap(btnBg),"button"+i);
				button.name = "button"+i;
				button.x = i*100+20;
				button.y = 20;
				addChild(button);
				button.addEventListener(TouchEvent.TOUCH,touchHandler);
				touchedObjects.push(button);
			}
			//a bird image and six range
			var birdBmp:Bitmap = new birdClass();
			var birdImg:Image = new Image(Texture.fromBitmap(birdBmp));
			birdImg.name = "bird";
			birdImg.x = 20;
			birdImg.y = 60;
			addChild(birdImg);
			birdImg.addEventListener(TouchEvent.TOUCH,touchHandler);
			for (var j:int = 0; j < 6; j++) 
			{
				var itemWidth:Number = birdImg.width/3;
				var itemHeight:Number = birdImg.height/2;
				var xIndex:int = j%3;
				var yIndex:int = j/3;
				var rect:Rectangle = new Rectangle(xIndex*itemWidth,yIndex*itemHeight,itemWidth,itemHeight);
				touchedObjects.push({target:birdImg,range:rect});
			}
		}
		/**listener for touch*/
		private function touchHandler(event:TouchEvent):void
		{
			var touch:Touch = event.touches[0];
			if(touch.phase == TouchPhase.ENDED)
			{
				textField.text = (event.currentTarget as Object).name + ":" + touch.globalX + "," + touch.globalY;
			}
		}
	}
}

这样就可以了,您可以通过Tab或方向键的左右,来切换焦点,按下Enter,则代表touch这个对象。

以下是TabSprite类的实现代码:

package com.techmx.component
{
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;
 
	import starling.core.Starling;
	import starling.core.starling_internal;
	import starling.display.DisplayObject;
	import starling.display.DisplayObjectContainer;
	import starling.display.Image;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.events.KeyboardEvent;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.textures.Texture;
 
	use namespace starling_internal;
 
	/**
	 * 一个容器,允许用户通过Tab键(或方向键的左右),来切换内部显示对象的焦点,按Enter代表确认(触发Touch事件)。
	 * 适用于一些不能Touch的场合(比如智能TV)。
	 * @author <a href="http://weibo.com/guoshaorui">NeoGuo</a>
	 */	
	public class TabSprite extends Sprite
	{
		/**一个数组,包含可被焦点管理器管理的显示对象。
		 * 可接受类型为显示对象,或一个对象:{target:myMC,range:new Rectangle(0,0,100,100)},通过后面的方法,可以指定一个对象的某个区域为焦点响应区域。
		 * 您可以把需要被焦点管理器管理的对象添加到这个数组,当显示对象处于显示列表之后,他们就可以用Tab键(或方向键的左右)进行控制。
		 */	
		public var touchedObjects:Vector.<Object>;
		/**用户必须通过键盘先激活Tab模式*/
		protected var enabled:Boolean = false;
		/**处于焦点的索引*/
		protected var tabIndex:int = 0;
		/**处于焦点的显示对象*/
		protected var tabChild:DisplayObject;
		/**处于焦点的显示对象区域*/
		protected var tabChildRect:Rectangle;
		/**容纳四边形显示的容器*/
		protected var rectHostContainer:DisplayObjectContainer;
		/**显示四边形的Image*/
		protected var rectImage:Image;
		/**用于修正滚动容器中造成的计算误差,比如用了Foxhole库。如果没有用到这个类库,请忽略这个属性。*/
		protected var touchPointOffset:Point;
		/**
		 * CONSTRATOR
		 */		
		public function TabSprite()
		{
			super();
			this.addEventListener(Event.ADDED_TO_STAGE,initTabSprite);
			this.addEventListener(Event.REMOVED_FROM_STAGE,clearTabSprite);
			touchedObjects = new Vector.<Object>();
			rectHostContainer = this;
		}
		/**
		 * 初始化容器
		 */		
		protected function initTabSprite(...args):void
		{
			stage.addEventListener(KeyboardEvent.KEY_DOWN,onSpriteKeyDown);
		}
		/**
		 * 临时清理容器
		 */		
		protected function clearTabSprite(...args):void
		{
			stage.removeEventListener(KeyboardEvent.KEY_DOWN,onSpriteKeyDown);
			clearTabRect();
		}
		/**
		 * 侦听键盘按下,切换焦点
		 * @param event KeyboardEvent
		 */		
		protected function onSpriteKeyDown(event:KeyboardEvent):void
		{
			if(!visible || !touchable || touchedObjects == null || touchedObjects.length == 0 )
				return;
			if(event.keyCode == Keyboard.ENTER)
			{
				if(tabChild != null)
					touchCurrentChild();
				clearTabRect();
				return;
			}
			if(event.keyCode != Keyboard.TAB && event.keyCode != Keyboard.LEFT && event.keyCode != Keyboard.RIGHT)
				return;
			if(!enabled)
			{
				enabled = true;
				tabIndex = 0;
			}
			else if(event.keyCode == Keyboard.TAB || event.keyCode == Keyboard.RIGHT)
			{
				tabIndex++;
				if(tabIndex == touchedObjects.length)
					tabIndex = 0;
				onTabKeyPress();
			}
			else if(event.keyCode == Keyboard.LEFT)
			{
				tabIndex--;
				if(tabIndex < 0)
					tabIndex = touchedObjects.length-1;
				onTabKeyPress();
			}
			if(touchedObjects[tabIndex] is DisplayObject)
			{
				tabChild = touchedObjects[tabIndex] as DisplayObject;
				tabChildRect = null;
			}
			else
			{
				tabChild = touchedObjects[tabIndex]["target"];
				tabChildRect = touchedObjects[tabIndex]["range"];
			}
			if(tabChild == null || !tabChild.visible || !tabChild.touchable)
				return;
			showTabRect();
		}
		/**供继承类来实现*/
		protected function onTabKeyPress():void
		{
 
		}
		/**
		 * 当用户按下Enter键,模拟Touch
		 */		
		private function touchCurrentChild():void
		{
			var position:Point;
			if(tabChildRect != null)
				position = new Point(tabChild.x+tabChildRect.x+tabChildRect.width/2,tabChild.y+tabChildRect.y+tabChildRect.height/2);
			else
				position = new Point(tabChild.x+tabChild.width/2,tabChild.y+tabChild.height/2);
			if(touchPointOffset != null)
			{
				position.x -= touchPointOffset.x;
				position.y -= touchPointOffset.y;
			}
			var touch:Touch = new Touch(0,position.x,position.y,TouchPhase.ENDED,this);
			touch.setTimestamp(Starling.current.juggler.elapsedTime);
			//touch.
			var touchs:Vector.<Touch> = new Vector.<Touch>();
			touchs.push(touch);
			var touchEvent:TouchEvent = new TouchEvent(TouchEvent.TOUCH,touchs,true,true,true);
			tabChild.dispatchEvent(touchEvent);
		}
		/**清理焦点显示*/
		protected function clearTabRect():void
		{
			if(rectImage == null)
				return;
			if(rectImage.parent != null)
				rectImage.removeFromParent();
			if(rectImage != null || rectImage.texture != null)
			{
				rectImage.texture.dispose();
				rectImage.dispose();
				rectImage = null;
			}
		}
		/**显示当前焦点区域*/
		protected function showTabRect():void
		{
			clearTabRect();
			//get rect
			var rect:Rectangle = tabChild.getBounds(this);
			if(tabChildRect != null)
			{
				rect.x += tabChildRect.x;
				rect.y += tabChildRect.y;
				rect.width = tabChildRect.width;
				rect.height = tabChildRect.height;
			}
			//draw
			var shape:Shape = new Shape();
			shape.graphics.lineStyle(12,0xD9D919,1);
			shape.graphics.moveTo(0,0);
			shape.graphics.lineTo(rect.width,0);
			shape.graphics.lineTo(rect.width,rect.height);
			shape.graphics.lineTo(0,rect.height);
			shape.graphics.lineTo(0,0);
			var bmd:BitmapData = new BitmapData(rect.width,rect.height,true,0x000000);
			bmd.draw(shape);
			var texture:Texture = Texture.fromBitmapData(bmd,false);
			rectImage = new Image(texture);
			rectImage.touchable = false;
			rectImage.x = rect.x;
			rectImage.y = rect.y;
			rectHostContainer.addChild(rectImage);
			bmd.dispose();
		}
		/**
		 * 取消这个容器的Tab管理,和普通容器一致
		 */		
		public function cancelTabManagement():void
		{
			touchedObjects = null;
			removeEventListener(Event.ADDED_TO_STAGE,initTabSprite);
			removeEventListener(Event.REMOVED_FROM_STAGE,clearTabSprite);
			if(stage != null)
				stage.removeEventListener(KeyboardEvent.KEY_DOWN,onSpriteKeyDown);
			tabChild = null;
		}
		/**@private*/
		override public function dispose():void
		{
			touchedObjects = null;
			tabChild = null;
			removeEventListener(Event.ADDED_TO_STAGE,initTabSprite);
			removeEventListener(Event.REMOVED_FROM_STAGE,clearTabSprite);
			clearTabRect();
			super.dispose();
		}
	}
}
点击打开链接
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐