flex与excel相互复制
在Excel(OpenOffice Calc)中,当多个单元格被复制时,这些单元格的数据是以tab键分隔一行行数据(tab-separated values (TSV))存放在剪贴板中的,当这些数据被粘贴到Google Spreadsheets中时,这些TSV格式的数据被解析并被相应插入到Google Spreadssheets中了。
既然知道其中的奥妙,那么剩下的就是在Flex中实现同样的效果了。以下是我们的实现思路,可以在Excel和Flex相互直接复制粘贴数据。
我们的思路的精妙所在是隐藏文本(TextField)组件的使用:
在DataGrid中,当按下Ctrl键时,我们创建一个隐藏的文本(TextField)组件,并将焦点定位给它,这样,我们就可以接受任何通过Ctrl+V粘贴过来的数据。相应的我们也将DataGrid选择行数据以TSV格式拷贝到TextField组件中,并且将所有的文本选择,这样我们使用Ctrl+C操作就可以复制当前行所有数据了。
监听隐藏文件组件的textInput事件。如果有任何数据被粘贴的话,那么在这里数据将被解析,并插入到DataGrid中。
当Ctrl键释放后,移除隐藏的文本组件。
以下是完整实现示例代码:
- <?xml version="1.0"?>
- <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
- <mx:Script>
- <![CDATA[
- import flash.events.KeyboardEvent;
- private function dataGridKeyDownHandler(event:KeyboardEvent):void
- {
- if (event.ctrlKey && !dataGrid.getChildByName("clipboardProxy"))
- {
- // Add an invisible TextField object to the DataGrid
- var textField:TextField = new TextField();
- textField.name = "clipboardProxy";
- dataGrid.addChild(textField);
- textField.visible = false;
- textField.type = TextFieldType.INPUT;
- textField.multiline = true;
- // Populate the TextField with selected data in TSV format
- textField.text = getTextFromItems(dataGrid.selectedItems);
- textField.setSelection(0, textField.text.length - 1);
- // Listen for textInput event
- textField.addEventListener(TextEvent.TEXT_INPUT,
- clipboardProxyPasteHandler);
- // Set player-level focus to the TextField
- systemManager.stage.focus = textField;
- }
- }
- private function dataGridKeyUpHandler(event:KeyboardEvent):void
- {
- if (!event.ctrlKey)
- {
- var textField:TextField = TextField(dataGrid
- .getChildByName("clipboardProxy"));
- if (textField)
- dataGrid.removeChild(textField);
- }
- }
- private function clipboardProxyPasteHandler(event:TextEvent):void
- {
- // Extract values from TSV format and populate the DataGrid
- var items:Array = getItemsFromText(event.text);
- for each (var item:Object in items)
- dataGrid.dataProvider.addItem(item);
- }
- private function getItemsFromText(text:String):Array
- {
- var rows:Array = text.split("\n");
- if (!rows[rows.length - 1])
- rows.pop();
- var columns:Array = dataGrid.columns;
- var itemsFromText:Array = [];
- for each (var rw:String in rows)
- {
- var fields:Array = rw.split("\t");
- var n:int = Math.min(columns.length, fields.length);
- var item:Object = {};
- for (var i:int = 0; i < n; i++)
- item[columns[i].dataField] = fields[i];
- itemsFromText.push(item);
- }
- return itemsFromText;
- }
- private function getTextFromItems(items:Array):String
- {
- var columns:Array = dataGrid.columns;
- var textFromItems:String = "";
- for each (var it:Object in items)
- {
- for each (var c:DataGridColumn in columns)
- textFromItems += it[c.dataField] + "\t";
- textFromItems += "\n";
- }
- return textFromItems;
- }
- ]]>
- </mx:Script>
- <mx:DataGrid id="dataGrid" editable="true"
- keyDown="dataGridKeyDownHandler(event)"
- keyUp="dataGridKeyUpHandler(event)">
- <mx:columns>
- <mx:DataGridColumn headerText="Scheduled Date"
- dataField="scheduledDate" />
- <mx:DataGridColumn headerText="Home Team"
- dataField="homeTeam" />
- <mx:DataGridColumn headerText="Away Team"
- dataField="awayTeam" />
- <mx:DataGridColumn headerText="Field"
- dataField="field" />
- </mx:columns>
- <mx:dataProvider>
- <mx:Object scheduledDate="4/1/2006" homeTeam="Chester Bucks"
- awayTeam="Long Valley Hitters" field="Dawn Field" />
- </mx:dataProvider>
- </mx:DataGrid>
- </mx:Application>
这样我们可以写一个自定义组件继承自Datagrid组件来支持行级数据的复制粘贴:
- package com.yyhy.flex.util
- {
- import flash.events.KeyboardEvent;
- import flash.events.TextEvent;
- import flash.text.TextField;
- import flash.text.TextFieldType;
- import mx.controls.*;
- import mx.controls.dataGridClasses.DataGridColumn;
- //import mx.events.
- public class EIDataGrid extends DataGrid
- {
- public function EIDataGrid()
- {
- super();
- this.addEventListener(KeyboardEvent.KEY_DOWN,KeyDownHandler);
- this.addEventListener(KeyboardEvent.KEY_UP,KeyUpHandler);
- }
- private function KeyDownHandler(event:KeyboardEvent):void
- {
- if (event.ctrlKey && !this.getChildByName("clipboardProxy"))
- {
- // Add an invisible TextField object to the DataGrid
- var textField:TextField = new TextField();
- textField.name = "clipboardProxy";
- this.addChild(textField);
- textField.visible = false;
- textField.type = TextFieldType.INPUT;
- textField.multiline = true;
- // Populate the TextField with selected data in TSV format
- textField.text = getTextFromItems(this.selectedItems);
- //textField.text = getTextFromItems(dataGrid.dataProvider.source);
- textField.setSelection(0, textField.text.length – 1);
- // Listen for textInput event
- textField.addEventListener(TextEvent.TEXT_INPUT,
- clipboardProxyPasteHandler);
- // Set player-level focus to the TextField
- systemManager.stage.focus = textField;
- }//end if
- }//end function
- private function KeyUpHandler(event:KeyboardEvent):void
- {
- if (!event.ctrlKey)
- {
- var textField:TextField = TextField(this.getChildByName("clipboardProxy"));
- if (textField)
- {
- this.removeChild(textField);
- }//end if
- }//end if
- }//end function
- private function clipboardProxyPasteHandler(event:TextEvent):void
- {
- // Extract values from TSV format and populate the DataGrid
- var items:Array = getItemsFromText(event.text);
- for each (var item:Object in items)
- this.dataProvider.addItem(item);
- }//end function
- private function getItemsFromText(text:String):Array
- {
- var rows:Array = text.split("\n");
- if (!rows[rows.length - 1])
- rows.pop();
- var columns:Array = this.columns;
- var itemsFromText:Array = [];
- for each (var rw:String in rows)
- {
- var fields:Array = rw.split("\t");
- var n:int = Math.min(columns.length, fields.length);
- var item:Object = {};
- for (var i:int = 0; i < n; i++)
- item[columns[i].dataField] = fields[i];
- itemsFromText.push(item);
- }
- return itemsFromText;
- }//end function
- private function getTextFromItems(items:Array):String
- {
- var columns:Array = this.columns;
- var textFromItems:String = "";
- //add datagrid headtext
- for each (var i: DataGridColumn in columns)
- {
- textFromItems += i.headerText + "\t";
- }
- textFromItems += "\n";
- for each (var it:Object in items)
- {
- for each (var c: DataGridColumn in columns)
- textFromItems += it[c.dataField] + "\t";
- textFromItems += "\n";
- }
- return textFromItems;
- }//end function
- }
- }
在AIR环境下,可以使用as3xls-1.0.swc库来导出excel:
- <?xml version="1.0" encoding="utf-8"?>
- <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
- <mx:Script>
- <![CDATA[
- import mx.controls.CheckBox;
- import mx.controls.Alert;
- import com.as3xls.xls.ExcelFile;
- import com.as3xls.xls.Sheet;
- import flash.filesystem.*;
- [Bindable]
- private var dp:Array = [
- {idx:1, names: "test1", sex: "b" },
- {idx:2, names: "test2", sex: "g" }
- ];
- public function doSelect(o:Object):void
- {
- Alert.show("行的数据分别是:"+o.idx+"/"+o.names+"/"+o.sex);
- }
- private var sheet:Sheet;
- private function onCreate():void {
- var excelFile:ExcelFile = new ExcelFile();
- sheet = new Sheet();
- sheet.resize(10, 10);
- sheet.setCell(0, 0, "Today’s date:");
- sheet.setCell(0, 1, new Date());
- excelFile.sheets.addItem(sheet);
- var mbytes:ByteArray = excelFile.saveToByteArray();
- var stream:FileStream = new FileStream();
- var docsDir:File = File.documentsDirectory.resolvePath("abc.xls"); // 定死文件名
- try
- {
- docsDir.browseForSave("Save As");
- docsDir.addEventListener(Event.SELECT, saveData);
- }
- catch (error:Error)
- {
- trace("Failed:", error.message)
- }
- function saveData(event:Event):void
- {
- var newFile:File = event.target as File;
- if (!newFile.exists)
- {
- var stream:FileStream = new FileStream();
- stream.open(newFile, FileMode.WRITE);
- stream.writeBytes(mbytes);
- // 写文件流
- stream.close();
- }
- }
- }
- ]]>
- </mx:Script>
- <mx:Panel>
- <mx:Button label="导出" click="onCreate()"/>
- <mx:DataGrid id="dg1" dataProvider ="{dp}">
- <mx:columns>
- <mx:DataGridColumn width="20" headerText="" >
- <mx:itemRenderer>
- <mx:Component>
- <mx:CheckBox change=" {outerDocument.doSelect(data as Object)} " />
- </mx:Component>
- </mx:itemRenderer>
- </mx:DataGridColumn>
- <mx:DataGridColumn headerText="names" dataField="names" width="200" />
- <mx:DataGridColumn headerText="sex" dataField="sex" width="300" />
- </mx:columns>
- </mx:DataGrid>
- </mx:Panel>
- </mx:WindowedApplication>
如果是flex web怎么办?flash.filesystem.File (AIR Only)在flex是实现不了的。这样就需要后台来完成。以Java为示例:
例如 flex中一个导出按钮,可以直接 flash.net.navigateToURL(new URLRequest(url),”_blank”); url 可以是一个jsp页面 或者 一个servet。也就是你说第二种方式 收到客户端数据。然后直接设置下
Response.Buffer = TRUE
Response.AddHeader “Content-Disposition”, “attachment”
Response.contentType=”application/vnd.ms-excel”
Response.Buffer 是设置缓存区的。
url当然指jsp或者servlet的访问 路径了。 Response.Buffer = TRUE
Response.AddHeader “Content-Disposition”, “attachment”
Response.contentType=”application/vnd.ms-exce 这个就是在生成jsp或者servlet的页面中设置的
例如
- <%@ page contentType="application/msexcel" %>
- <!–
- 以上这行设定本网页为excel格式的网页 –>
- <%
- response.setHeader("Content-disposition","inline; filename=test1.xls");
- //以上这行设定传送到前端浏览器时的档名为test1.xls
- //就是靠这一行,让前端浏览器以为接收到一个excel档
- %>
- <html>
- <head>
- <title>Excel档案呈现方式</title>
- </head>
- <body>
- <table border="1" width="100%">
- <tr>
- <td>姓名</td><td>身份证字号</td><td>生日</td>
- </tr>
- <tr>
- <td>李玟</td><td>N111111111</td><td>1900/11/12</td>
- </tr>
- <tr>
- <td>梁静如</td><td>N222222222</td><td>1923/10/1</td>
- </tr>
- <tr>
- <td>张惠妹</td><td>N333333333</td><td>1934/12/18</td>
- </tr>
- </table>
- </body>
- </html>
也可以使用servlet将流直接输出到客户端。这里推荐Java Excel API开源类库,操作Excel非常简单。
Flex图片另存为的解决方法-类似可以解决另存为excel:
我们知道Flex对于本地的限制比AIR要大,当我们想保存一个由Flex生成的文件必须借由服务器来完成,现在有一个需求就是,用户想保存Flex生成的图片在本地,我们要完成这个过程,必须先将Flex生成的图片转换为通用的数据格式,即ByteArray,然后由后台程序帮助写文件,形式上类似先上传,再下载,只不过中间不用保存实际的物理文件。
Flex端
- <?xml version="1.0" encoding="utf-8"?>
- <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
- <mx:Script>
- <![CDATA[
- import mx.graphics.codec.JPEGEncoder;
- import mx.graphics.ImageSnapshot;
- private function saveAs(){
- var en:JPEGEncoder = new JPEGEncoder(100); //压缩图片,100是指质量
- var ba:ByteArray=en.encode(ImageSnapshot.captureBitmapData(img));//将控件转为BitmapData后再转为ByteArray
- var request:URLRequest = new URLRequest("/TestForLCDS/servlet/UploadServlet");
- request.method="POST";
- request.data=ba;
- request.contentType = "application/octet-stream"; //这个很重要,设置成流数据
- navigateToURL(request,"_blank"); //因为要浏览器触发下载事件,所以就不用异步方式打开连接了
- }
- ]]>
- </mx:Script>
- <mx:Button x="228" y="10" label="另存为本地图片" click="saveAs()"/>
- <mx:Image id="img" x="10" y="10" source="img.jpg" width="200" height="200" scaleContent="false"/>
- </mx:Application>
后台Java servlet
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("application/x-download"); //内容是下载
- response.setHeader("Content-Disposition","attachment;filename=" + "test.jpg");//文件名,可以进一步处理
- //读数据
- BufferedInputStream inputStream = new BufferedInputStream(request.getInputStream());
- OutputStream outputStream = response.getOutputStream();
- byte [] bytes = new byte[1024];
- int v;
- //写数据
- while((v=inputStream.read(bytes))>0){
- outputStream.write(bytes,0,v);
- }
- outputStream.flush();
- outputStream.close();
- inputStream.close();
- }
- }