AIR2.0入门教程[二]:拖拽增强

前一篇文章中,我们简单介绍了AIR2.0的一些新特性,今天我们来着重了解一下其中对拖拽支持的增强。

AIR文件拖拽的实质

首先让我们来探究一下AIR拖拽中的本质,如下图所示,处于本地系统中的AIR应用,和系统之间通过拖拽方式产生的数据通讯,实际上是通过剪切板进行的。剪切板作为一个中介,如果你拖拽一个文件到AIR应用,那么实质上是将这个文件放到了剪切板,然后AIR应用会从剪切板中获取数据;反之也是如此,当你把文件或数据从AIR应用中拖拽到文件系统中,系统也会根据剪切板中的文件类型决定下一步的操作(比如保存文件)。

拖拽的三个阶段

我们可以把整个拖拽的过程分为三个阶段:启动,拖动和放下。

  1. 启动:用户通过按住鼠标按键从组件或组件中的项目进行拖动,启动拖放操作。涉及的事件包括nativeDragStart 和nativeDragComplete。启动方式是调取NativeDragManager.doDrag(),这个方法调取后,会启动拖拽的过程。
  2. 拖动:用户在按住鼠标按键的同时,将鼠标光标移至其他组件、应用程序,或移至桌面。涉及的事件包括nativeDragUpdate,nativeDragEnter,nativeDragOver,nativeDragExit。其中nativeDragEnter事件会被我们用于检测数据格式,以决定是否允许接受该数据格式。如果格式合法,我们可以调用NativeDragManager.acceptDragDrop()方法,允许该数据在组件上放下。这个时候屏幕上光标的形状会有所变化。
  3. 放下:用户在符合条件的放置目标上释放鼠标。如果是在AIR应用内部放下,涉及的事件包括nativeDragDrop,我们可以捕获这个事件,来获取剪切板中的数据,如果放下的区域在AIR外部,那么将会由操作系统来确定如何处理该操作。

下图描述了NativeDragEvent的各种类型,不同颜色代表这是由不同的对象调度的。

AIR还提供了一个ClipboardFormats类,定义了各种不同的剪切板描述格式,参见下图(这是AIR1.5的版本,在AIR2中增加了一个新的格式,稍后叙述):

按照数据的来源和去处,我们可以将拖拽分为两种类别:拖出和拖入。

拖出操作

拖出的执行过程:

  1. 创建Clipboard对象包裹要传输的数据
  2. 触发mouseDown事件,启动拖拽NativeDragManager.doDrag()
  3. 完成拖拽传输

拖出的代码示例:

  1. private function onMouseDown(event:MouseEvent):void {
  2. (myLoader.content as MovieClip).stop();
  3. var bd:BitmapData = new BitmapData(600,500,true,0x000000);
  4. bd.draw(myLoader);
  5. display = new Bitmap(bd);
  6. var jpg:JPEGEncoder = new JPEGEncoder();
  7. var ba:ByteArray = jpg.encode(bd);
  8. file = File.documentsDirectory.resolvePath("image.jpg");
  9. var fileStream:FileStream = new FileStream();
  10. fileStream.open(file, FileMode.WRITE);
  11. fileStream.writeBytes(ba,0,ba.length);
  12. fileStream.close();
  13. var transferObject:Clipboard = createClipboard(display);
  14. NativeDragManager.doDrag(this, transferObject, display.bitmapData, new Point(-mouseX,-mouseY));
  15. }
  16. public function createClipboard(image:Bitmap):Clipboard {
  17. var transfer:Clipboard = new Clipboard();
  18. transfer.setData("bitmap", image, true);
  19. transfer.setData(ClipboardFormats.BITMAP_FORMAT, image.bitmapData, false);
  20. transfer.setData(ClipboardFormats.FILE_LIST_FORMAT,new Array(file),false);
  21. return transfer;
  22. }

该代码的执行过程是,首先有一个正在播放的动画,我用Bitmap获取其中的像素数据,然后用JPEGEncoder编码为JPEG图像文件数据,生成临时文件(这段代码是AIR1.5版本,在AIR2中我们不再需要生成临时文件,而直接使用FilePromise机制)。

拖入操作

拖出的执行过程:

  1. 将一个 Clipboard 对象拖到一个组件上方。
  2. 调度 nativeDragEnter 事件。
  3. 检查数据格式,如果可用,调用 NativeDragManager.acceptDragDrop()。
  4. NativeDragManager 更改鼠标光标,以指示可以放置此对象。
  5. 用户将此对象放在此组件上。
  6. 接收组件调度 nativeDragDrop 事件。
  7. 接收组件从事件对象内的 Clipboard 对象中读取所需格式的数据。

拖入的代码示例:

  1. private function initApp():void {
  2. this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER,onDragIn);//通常做拖入文件的类型检查
  3. this.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP,onDrop);//拖拽完成事件
  4. }
  5. private function onDragIn(event:NativeDragEvent):void{
  6. var transferable:Clipboard = event.clipboard;
  7. if(transferable.hasFormat(ClipboardFormats.FILE_LIST_FORMAT) && event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT)[0].nativePath.split(".")[1] == "jpg"){
  8. NativeDragManager.acceptDragDrop(this);
  9. } else {
  10. Alert.show("不接受的格式");
  11. }
  12. }
  13. private function onDrop(event:NativeDragEvent):void{
  14. var dropfiles:Array= event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
  15. var currentFile:File = dropfiles[0];
  16. imageSource = currentFile.url;
  17. }

该代码的执行过程是,当拖拽目标进入,判断是否合法,如果可以接收该格式,则调取NativeDragManager.acceptDragDrop()。在onDrop方面里,则取得了剪切板中的数据。

AIR2.0中的拖拽增强

AIR1.5的缺憾

  1. 不能拖拽一个不存在的文件
  2. 如果你需要将数据拖拽到桌面,需要先保存为临时文件,然后操作临时文件。

AIR2.0的增强

  1. 引入了File Promise的概念
  2. 你现在可以从AIR2.0创建的应用中拖拽一个不存在的文件(或位于Server上的文件),并且在松开鼠标后提供这个文件的数据
  3. 在ClipboardFormats类中,增加了FILE_PROMISE_LIST_FORMAT,这个专门用于File Promise对象

File Promise简介

File Promise 是这样一种机制,我们可以简单理解为信用卡机制,你先承诺给系统,肯定会提供一个文件,当操作系统接收文件的时候,你再把文件的数据写入。这样我们就省略了要创建一个临时文件的步骤。

在AIR中,并没有一个直接的FilePromise类,而是提供了一个IFilePromise的接口。就是说,只要实现这个接口的类,就具备了File Promise的功能。这对于初学者有些不便,不像File类那样直接就可以使用,不过这也提供了更大的扩展性。你可以为你的各种数据实现File Promise功能。

AIR内部只为这个接口实现了一个类:URLFilePromise,顾名思义,这个类就是帮助你将远程URL的文件保存到本地的。它有两个必须的属性:request定义你要发送的请求(通常是WEB上的一个文件地址),relativePath则定义了要保存的文件名。

下面是一段简单的使用URLFilePromise的代码:

  1. private function doDrag():void {
  2. var cb:Clipboard = new Clipboard();
  3. var items:Array = lt.selectedItems;
  4. var promises:Array = new Array();
  5. for each (var item:Object in items) {
  6. var fp:URLFilePromise = new URLFilePromise();
  7. var req:URLRequest = new URLRequest(item.url);
  8. fp.request = req;
  9. fp.relativePath = item.name;
  10. promises.push(fp);
  11. }
  12. cb.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, promises);
  13. NativeDragManager.doDrag(lt, cb);
  14. }

注意代码中使用了数组,这意味着可以允许用户同时拖拽多个远程文件到本地。这已经为实现类似FTP应用桌面端扫清了障碍。

下面我们来探讨实现自定义的File Promise功能类。

自定义File Promise功能类

实现IFilePromise接口

如果你想使用File Promise机制但不能使用URLFilePromise,可以在自定义类中实现该接口。注意数据源是支持同步和异步的,具体罗列如下:

  1. ByteArray (同步)
  2. FileStream (同步或异步)
  3. Socket (异步)
  4. URLStream (异步)

实现该接口,必须实现的方法:

  1. open():IDataInput 返回数据源对象,这个对象必须实现IDataInput接口,如果是异步,还必须实现IEventDispatcher
  2. get relativePath():String 提供一个包含文件名称的路径
  3. get isAsync():Boolean 数据是同步还是异步
  4. close():void 当数据读取完毕或错误时关闭
  5. reportError( e:ErrorEvent ):void 异常时抛出错误

自定义FilePromise类实例

  1. package com.riameeting.promise
  2. {
  3. import flash.desktop.IFilePromise;
  4. import flash.display.Bitmap;
  5. import flash.events.ErrorEvent;
  6. import flash.utils.ByteArray;
  7. import flash.utils.IDataInput;
  8.  
  9. import mx.graphics.codec.JPEGEncoder;
  10.  
  11. public class BitmapFilePromise implements IFilePromise
  12. {
  13. private const fileSize:int = 5000; //size of file data
  14. private var filePath:String = "imagePromise.jpg";
  15.  
  16. public var bmp:Bitmap;
  17.  
  18. public function BitmapFilePromise(b:Bitmap)
  19. {
  20. bmp = b;
  21. }
  22.  
  23. public function get relativePath():String
  24. {
  25. return filePath;
  26. }
  27.  
  28. public function get isAsync():Boolean
  29. {
  30. return false;
  31. }
  32.  
  33. public function open():IDataInput
  34. {
  35. var jpg:JPEGEncoder = new JPEGEncoder();
  36. var ba:ByteArray = jpg.encode(bmp.bitmapData);
  37. ba.position = 0;
  38. return ba;
  39. }
  40.  
  41. public function close():void{}
  42.  
  43. public function reportError(e:ErrorEvent):void{}
  44. }
  45. }

  1. private function onMouseDown(event:MouseEvent):void {
  2. (myLoader.content as MovieClip).stop();
  3. var bd:BitmapData = new BitmapData(600,500,true,0x000000);
  4. bd.draw(myLoader);
  5. display = new Bitmap(bd);
  6. var fp:BitmapFilePromise = new BitmapFilePromise(display);
  7. var cb:Clipboard = new Clipboard();
  8. cb.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, [fp]);
  9. NativeDragManager.doDrag(myLoader, cb);
  10. }

在接下来的教程中,我们将继续探讨更多的AIR2新特性。

riadevID: 
您给予的分值: None 平均分: 4.3 ( 3 票)

open方法里return的是byte

open方法里return的是byteArray类型,是否因此拷到系统剪贴板里的也是byteArray类型,而office不能识别导致不能写入office?

AIR---Office

怎样才能将Air里图片拖到Office里?上面例子拖到桌面上是可以的

我的意思是,上面只

我的意思是,上面只是代码片段 很多东西不全的。

汗,上面不就是示例

汗,上面不就是示例吗

没有范例代码么?

没有范例代码么?

发表新评论

  • 网页地址和电子邮件地址将会被自动转换为链接。
  • 行和段被自动切分。
  • 您可以使用下面的标签来高亮显示您的评论内容: <code>, <blockcode>. 可以使用"[foo]".旁边显示标签样式 "<foo>" PHP代码可以用这样的区块来包含<?php ... ?> or <% ... %>

更多格式化选项信息

验证区域
系统验证:请回答下面的问题