首页 > 代码编程 > 关于 TitleWindow/Panel 的拖动

关于 TitleWindow/Panel 的拖动

2010-03-30

实现拖动有很多种方法, 最常见也最简单的是监听事件. 例如对 TitleWindow 设置监听事件, 监听鼠标按下事件, 当鼠标按下时执行拖动. 这样的处理方式有时候并不一定很好, 桌面应用与浏览应用差别就很大. 今天我就来告诉你, 怎样来处理 TitleWindow/Panel 的拖动是比较好的.

当做一些很小的 AIR 工具时, 如便签和待办事项等, 我们很可能不希望出现系统边框和默认的边框, 而是制作一个透明的窗体, 在上面放置 TitleWindow 或 Panel 或直接放置其它控件.又或者是希望页面上的 TitleWindow 能够随意拖动.

而 Flex 并没有提供可以直接拖动的控件, 这样我们不得不想着法实现想要的功能. 首先来看看基于事件监听的拖动.

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
	initialize="init();" width="500" height="300">
	<mx:Script>
	   <![CDATA[
 
		public function init():void
		{
			mainWindow.addEventListener(MouseEvent.MOUSE_DOWN,moveHandler);	//创建监听, 监听鼠标按下事件. 当鼠标按下时, 调用 moveHandler .
		}
 
		private function moveHandler(event:MouseEvent):void{
			this.nativeWindow.startMove();
		}
 
	   ]]>
	</mx:Script>
	<mx:TitleWindow id="mainWindow" width="210" height="80" layout="absolute" title="TitleWindow">
		<mx:Label text="Drag this TitleWindow." horizontalCenter="0" verticalCenter="0"/>
	</mx:TitleWindow>
</mx:WindowedApplication>

可是这样做有一个问题, 就是无论在 TitleWindow 的任意位置按下鼠标都可以拖动. 这不是我们想要的效果, 我们想要的是和用 PopUpManager 弹出 TitleWindow 一样的效果, 也就是点击标题栏拖动.

仔细研究了一下 PopUpManager 和 TitleWindow , 原来 TitleWindow 从超类 UIComponent 继承了一个私有的 Boolean 属性 isPopUp 决定着 TitleWindow 是否可以拖动, 而当通过 PopUpManager 弹出时会设置为 true .

既然知道了原理, 自然也就有了解决办法, 虽然我们不能直接设置 isPopUp 的值, 但是我们可以自己写一个类继承 TitleWindow , 通过默认的构造方法来改变 isPopUp 的值.
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
package components
{
	import mx.containers.TitleWindow;
 
	public class MainTitleWindow extends TitleWindow
	{
		public function MainTitleWindow()
		{
			super();
			this.isPopUp = true;	//设置为 true , 表示可以拖动
		}
	}
}

但是这样做并不完美, 我们并没有达到自定义的效果. 例如在一个地方我们需要自定义的 TitleWindow 能拖动, 而在另一个地方我们却希望它不能拖动. 这时候我们依然不能改变 isPopUp 的值, 而去修改代码也并不能两全其美.

不过我们可以为自定义的 TitleWindow 添加一个 Boolean 属性 dragEnabled , 通过它的 set 方法来改变 isPopUp 的值.
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package components
{
	import mx.containers.TitleWindow;
 
	public class MainTitleWindow extends TitleWindow
	{
		public function MainTitleWindow()
		{
			super();
		}
 
		private var _dragEnabled:Boolean = false;	
 
		public function set dragEnabled(value:Boolean):void
		{
			this._dragEnabled = value;
			this.isPopUp = value;	//设置 dragEnabled 的值时, 同时更改 isPopUp 的值
		}
 
		public function get dragEnabled():Boolean
		{
			return this._dragEnabled;
		}
	}
}

简单点说, dragEnabled 和 isPopUp 是一样的, 只是我们可以直接对它进行设置. 通过对它的设置改变 isPopUp 的值. 这样我们在需要拖动时只需要将 dragEnabled 属性设置为 true 就行了.

我们只需要将自定义的 TitleWindow 引入到页面就可以使用, 代码如下:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
	width="600" height="300" xmlns:components="components.*">
 
	<components:MainTitleWindow id="mainWindow" width="210" height="80" layout="absolute" 
		title="TitleWindow" dragEnabled="true">
		<mx:Label text="You can drag this TitleWindow." horizontalCenter="0" verticalCenter="0"/>
	</components:MainTitleWindow>
</mx:Application>


你可以对上面的 Flash 点击右键查看源代码, 也可下载项目. 下面的内容主要针对 AIR 桌面应用.

这样, 我们已经做了一个可以拖动的 TitleWindow , 在网页上可以正常的拖动. 并不是说在 AIR 桌面应用上不能拖动, 试试看, 完全可以拖动. 但是有一个很尴尬的现象, 那就是我们原本隐藏了的窗体出现了, 别担心, 只是出现了滚动条. 将 TitleWindow 往左上拖, 没有出现滚动条, 但是我们自定义的 TitleWindow 也不见了, 如果这时候释放鼠标左键, 恭喜你, 你现在也无法将它拖回来了. 你可以将上面 Flash 内的 TitleWindow 和 Panel 往左边拖动, 直到消失.

造成这个问题, 是因为我们的应用设置的窗体就只有这么大, 当然, 你也完全可以将窗体的大小设置为桌面的大小, 将滚动条隐藏. 这样看上去是解决了这个问题, 但是另一个问题接踵而来, 那就是当用 PopUpManager 弹出一个窗体或是 Alert 提示框弹出时, 将覆盖整个桌面, 这也十分尴尬.

放心, 有问题, 自然也有解决办法, 而且非常的完美.(我个人认为啦.-.-!)
原理如下, 既然我们要拖动 TitleWindow , 那我们在拖动的同时也拖动主窗体.
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" title="TitleWindow/Panel Drag Example" 
	initialize="init();" showFlexChrome="false" width="230" height="100">
	<mx:Script>
	   <![CDATA[
 
		public function init():void
		{
			mainWindow.addEventListener(MoveEvent.MOVE,moveHandler);	//创建监听, 监听移动事件. 当窗口移动时, 调用 moveHandler .
		}
 
		private function moveHandler(event:MoveEvent):void{
			this.nativeWindow.startMove();
		}
 
	   ]]>
	</mx:Script>
	<components:MainTitleWindow id="mainWindow" x="10" y="10" width="210" height="80" layout="absolute" title="TitleWindow" 
		showCloseButton="true" dragEnabled="true" close="this.nativeWindow.close()">
		<mx:Label text="You can drag this TitleWindow." horizontalCenter="0" verticalCenter="0"/>
	</components:MainTitleWindow>
</mx:WindowedApplication>

这个和最初的创建拖动差不多, 同样是对 TitleWindow 添加监听, 但是这次监听的事件是 MoveEvent.MOVE 移动事件. 当 TitleWindow 拖动时, 同时移动主窗体. 这样就不会有越界等尴尬问题, 而且在 Windows7 上拖动 TitleWindow 左右摇晃还可最小化其它窗口. 因为现在拖动 TitleWindow 就是在拖动主窗体啦.

好啦, 以上就是 TitleWindow 的拖动, 当然, 此方法对 Panel 一样有效. 这里也只讲解了拖动的问题, 其实自定义控件是很强大, 只要你想的到, 改造成什么样都行.

如果你想做一个没有 TitleWindow 没有 Panel 完全透明的窗体. 可以参考第一个例子, 只是将监听对象改变为 Label 等你想通过点击拖动的控件.

  1. 莫小路
    2010-04-04 00:24 | #1

    哎哟。都是些撒子乱七八糟的东西 看不懂。

    没事哈, 就是跑来跟你说我想你了

  2. 2010-04-08 15:01 | #2

    ;-) 监听,这个看起来不错

  3. Zeric
    Zeric
    2010-04-08 15:55 | #3

    @莫小路
    之前用手机审核你的评论, 没有办法回复, 你居然还跑到这里来了.
    咳, 专业不同, 你当然看不懂啦, 就像我也看不懂你们的琴谱一样.

    @Firm
    Flash 的工作原理就是事件机制, 通过监听相应事件执行相应处理.
    自定义实现的 TitleWindow 拖动和自身可拖动 TitleWindow 实质是相同的.
    都是通过对事件监听做出处理. 只不过 TitleWindow 自身是局部(标题栏)监听, 而我们是监听了它的整体, 所以点击任意位置都可以拖动.-.-!
    :mrgreen: 我好像没必要解释这个的, 上面说的也不保证完全准确.-.-!

  1. 本文目前尚无任何 trackbacks 和 pingbacks.
;-):|:x:twisted::smile::shock::sad::roll::razz::oops::o:mrgreen::lol::idea::grin::evil::cry::cool::arrow::???::?::!: