Posts tagged AS3
ListCollectionView.removeAll() – just what exactly gets removed?
Mar 11th
Short answer – all visible (i.e. not filtered-out) items get removed, NOT all items.
Consider this coding masterpiece:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var ac:ArrayCollection = new ArrayCollection(['aa', 'bb', 'c']); trace(ac.length + " " + ac.source.length); ac.filterFunction = function ( item : Object ) : Boolean { return (String(item).length > 1); }; ac.refresh(); trace(ac.length + " " + ac.source.length); ac.removeAll(); trace(ac.length + " " + ac.source.length); ac.filterFunction = null; trace(ac.length + " " + ac.source.length); ac.refresh(); trace(ac.length + " " + ac.source.length); |
The output is (using SDK 4.6):
3 3
2 3
0 1
0 1
1 1
Yey, item ‘c’ cheated death in an ArrayCollection! It’s easy to see what’s happening inside ListCollectionView – Dr.Death loops only over visible items.
I’m guessing that this is intentional & not a bug in the SDK – I just think that this should be communicated better… or ideally the function could be improved to:
1 |
Note: I promise to stop complaining about poor ArrayCollection… for a while
Pain with ArrayCollection addItemAt
Jun 29th
ArrayCollection has some nifty functionality, but it isn’t always a smooth sailing.
Gotcha 1: addItemAt doesn’t work if the ArrayCollection is sorted. Well, it does make sense, but there are situations when we could take advantage of the fact that sorting doesn’t happen before a refresh() call. Anyway, something that IMHO should be mentioned in the docs rather than only in ListCollectionView source: “if we’re sorted addItemAt is meaningless, just add to the end”.
Gotcha 2: Well, say, that we insist of using our collection, and so we try to remove the sorting to be able to do a meaningfull addItemAt. We set ac.sort=null; but instead of a great success, things explode on addItemAt. Well, the internal logic of ListCollectionView requires you to do a ac.refresh() after setting sort to null; if you don’t do that, it’s internal vars are out of whack and null references creep in. IMHO ArrayCollection should be smart enough to handle removal of sort without a subsequent refresh call.
Gotcha 3: Well, we call the ac.refresh() after setting ac.sort=null but now our item order is all screwed up. Epic phail. So, let’s just give up and copy the collection instead, shall we?
1 | var ac1:ArrayCollection = new ArrayCollection(ac0.toArray()); |
Simple example – Flash (AS3) and FormMail.cgi
May 29th
I’ve just created a little sample for a friend. He is a designer and “just needed something” that would allow him to capture the data that users enter. FormMail.cgi fits the bill – no need to touch any server files or databases, just point your Flash to a FormMail script that is already installed on your server and off you go – you are sending emails from Flash. It feels bit 90-ies, but it works.
What will you need:
- Flash CS3 or newer (download free 30 days trial from Adobe)
- URL to FormMail.cgi. Many hosting providers have installed FormMail.cgi for you – you will definitely have it if you have cPanel, look under Scripts to find the right path to yours. Otherwise ask your hosting provider or pinch somebody else’s FormMail (not recommended
).
Here are instructions that any Flash designer should be able to follow:
- Create a AS3 Flash document.
- Create your text inputs. My code is using 3 Text Inputs named: messageTI, nameTI, emailTI. You can create more, or rename them, but then you will have to add/modify the code a little bit.
- Create a button and name it sendB
- Create a dynamic text and name it statusTF
- Create an Actions layer and in the first frame put the following code, than modify the FormMail URL and recipient’s email:
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 26 27 28 29 30 31 32 33 34 35 36 37 | import flash.events.MouseEvent; import flash.profiler.showRedrawRegions; import flash.net.URLRequest; import flash.net.URLRequestMethod; sendB.addEventListener(MouseEvent.CLICK, sendData); function sendData(e:MouseEvent):void { if (messageTI.text.length > 3 && nameTI.text.length > 3 && emailTI.text.indexOf("@") > -1) { //very, very basic data validation (as this is not a data validation tutorial) var r:URLRequest = new URLRequest("http://YOUR-URL-TO-FORM-MAIL/FormMail.cgi"); //modify this URL var v:URLVariables = new URLVariables(); v.recipient = "YOUR-EMAIL@DOMAIN.com"; //put your email here v.subject = "My FormMail test"; //this will be the subject of the email v.email = emailTI.text; v.realname = nameTI.text; //user's name will be appended to his/hers email to form a From field of the email. v.theMessage = messageTI.text; //theMessage is a custom key. If you need more Input fields, just make up more custom fields like this one r.data = v; r.method = URLRequestMethod.POST; var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, completeHandler); try { loader.load(r); } catch (error:Error) { statusTF.text = "Unable to send data."; //we could definitely do more in terms of error handling, however this is not a error handling tutorial } } else { statusTF.text = "Please enter all required information."; } } function completeHandler(event:Event):void { statusTF.text = "Data submitted."; var loader:URLLoader = URLLoader(event.target); trace("completeHandler: " + loader.data); } |
Notes:
- You can place your components (Button, TextInputs & Dynamic Text) anywhere you like on a page, style them anyhow you like, but make sure they are in the first frame and have the correct names (case sensitive).
- If email is getting send but you are not receiving any – check your spam folder.
- You are too lazy to copy/paste? I hear you. Here is the sample flash file.
Flex SDK bug: The first color in ColorPicker (black) cannot be selected
May 21st
Ha, I found a bug in the SDK! (Well, OK, I didn’t find it, one of the testers did
. Let’s be very nice; let’s put it in Adobe’s Bug System, describe it and suggest a workaround
.
ADL doesn’t open the Air application
May 9th
So, you are writing an Air app (or more likely just imported one from somewhere), you try to debug it and the AIR Debug Launcher (ADL) just sits in the Dock (on Mac) and doesn’t open any window, doesn’t throw an error, nothing. Well, you will want to check that you are not using a Flex application class but rather the Air application class; i.e. <s:WindowedApplication>. Duh
Reinventing the wheel – PNG Encoder Transparency
Apr 20th
AlivePDF is an easy to use library to create PDF’s directly in Flash Player. There are several posts, i.e. here and here, describing how you can insert a hires image of any DisplayObject, i.e. a chart. These posts tell you how to modify a PNGEncoder in a way that it discards transparency – but in case your DisplayObject had some actual transparency, it won’t end up looking very good – transparent parts turn black, semi-transparent dark – what you really want is to give the DisplayObject a solid white background.
You can specify solid background colour on nearly any Flex component or you can always place the component in a white Canvas (or on white Rect in Flex 4).
Finally, the obvious approach (as we already had to hack it anyway) is to modify the mx.graphics.codec.PNGEncoder to replace transparency with white. There are no doubt great PNGEncoders out there, but I didn’t quickly find one that would discard transparency and extend IImageEncoder interface, so I jumped at the (rare) opportunity to have a play with >> and & operators – and have done one for you. The downside is that it’s not very fast – that’s likely caused by my poor coding, please feel free to suggest how to optimise it.
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | //////////////////////////////////////////////////////////////////////////////// // // NonTransparentPNGEncoder.as // STEHIL.COM, 2010 // example usage with AlivePDF: // var p:PDF = new PDF( Orientation.PORTRAIT, Unit.MM, Size.A4 ); // p.addPage(); // var linechartSnap:ImageSnapshot = ImageSnapshot.captureImage(linechart, IMAGE_DPI, new NonTransparentPNGEncoder()); // p.addImageStream(linechartSnap.data, ColorSpace.DEVICE_RGB, null, 0, 150, 180, 80); // // based on mx.graphics.codec.PNGEncoder // // ADOBE SYSTEMS INCORPORATED // Copyright 2007 Adobe Systems Incorporated // All Rights Reserved. // // NOTICE: Adobe permits you to use, modify, and distribute this file // in accordance with the terms of the license agreement accompanying it. // //////////////////////////////////////////////////////////////////////////////// package com.stehil.util { import flash.display.BitmapData; import flash.utils.ByteArray; import mx.graphics.codec.IImageEncoder; /** * The PNGEncoder class converts raw bitmap images into encoded * images using Portable Network Graphics (PNG) lossless compression. * * <p>For the PNG specification, see http://www.w3.org/TR/PNG/</p>. */ public class NonTransparentPNGEncoder implements IImageEncoder { //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * @private * The MIME type for a PNG image. */ private static const CONTENT_TYPE:String = "image/png"; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. */ public function NonTransparentPNGEncoder() { super(); initializeCRCTable(); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Used for computing the cyclic redundancy checksum * at the end of each chunk. */ private var crcTable:Array; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // contentType //---------------------------------- /** * The MIME type for the PNG encoded image. * The value is <code>"image/png"</code>. */ public function get contentType():String { return CONTENT_TYPE; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Converts the pixels of a BitmapData object * to a PNG-encoded ByteArray object. * * @param bitmapData The input BitmapData object. * * @return Returns a ByteArray object containing PNG-encoded image data. */ public function encode(bitmapData:BitmapData):ByteArray { return internalEncode(bitmapData, bitmapData.width, bitmapData.height, bitmapData.transparent); } /** * Converts a ByteArray object containing raw pixels * in 32-bit ARGB (Alpha, Red, Green, Blue) format * to a new PNG-encoded ByteArray object. * The original ByteArray is left unchanged. * * @param byteArray The input ByteArray object containing raw pixels. * This ByteArray should contain * <code>4 * width * height</code> bytes. * Each pixel is represented by 4 bytes, in the order ARGB. * The first four bytes represent the top-left pixel of the image. * The next four bytes represent the pixel to its right, etc. * Each row follows the previous one without any padding. * * @param width The width of the input image, in pixels. * * @param height The height of the input image, in pixels. * * @param transparent If <code>false</code>, alpha channel information * is ignored but you still must represent each pixel * as four bytes in ARGB format. * * @return Returns a ByteArray object containing PNG-encoded image data. */ public function encodeByteArray(byteArray:ByteArray, width:int, height:int, transparent:Boolean = true):ByteArray { return internalEncode(byteArray, width, height, transparent); } /** * @private */ private function initializeCRCTable():void { crcTable = []; for (var n:uint = 0; n < 256; n++) { var c:uint = n; for (var k:uint = 0; k < 8; k++) { if (c & 1) c = uint(uint(0xedb88320) ^ uint(c >>> 1)); else c = uint(c >>> 1); } crcTable[n] = c; } } /** * @private */ private function internalEncode(source:Object, width:int, height:int, transparent:Boolean = true):ByteArray { // The source is either a BitmapData or a ByteArray. var sourceBitmapData:BitmapData = source as BitmapData; var sourceByteArray:ByteArray = source as ByteArray; if (sourceByteArray) sourceByteArray.position = 0; // Create output byte array var png:ByteArray = new ByteArray(); // Write PNG signature png.writeUnsignedInt(0x89504E47); png.writeUnsignedInt(0x0D0A1A0A); // Build IHDR chunk var IHDR:ByteArray = new ByteArray(); IHDR.writeInt(width); IHDR.writeInt(height); IHDR.writeByte(8); // bit depth per channel IHDR.writeByte(2); // color type: RGBA IHDR.writeByte(0); // compression method IHDR.writeByte(0); // filter method IHDR.writeByte(0); // interlace method writeChunk(png, 0x49484452, IHDR); // Build IDAT chunk var IDAT:ByteArray = new ByteArray(); var alpha:uint; var r:uint; var g:uint; var b:uint; for (var y:int = 0; y < height; y++) { IDAT.writeByte(0); // no filter var x:int; var pixel:uint; for (x = 0; x < width; x++) { pixel = sourceBitmapData.getPixel32(x, y); alpha = pixel >> 24 & 0xFF; //alpha=255 if no transparency r = pixel >> 16 & 0xFF; g = pixel >> 8 & 0xFF; b = pixel & 0xFF; alpha = 0xFF - alpha; //invert alpha value r+=alpha; g+=alpha; b+=alpha; IDAT.writeByte(limit8byte(r)); IDAT.writeByte(limit8byte(g)); IDAT.writeByte(limit8byte(b)); } } IDAT.compress(); writeChunk(png, 0x49444154, IDAT); // Build IEND chunk writeChunk(png, 0x49454E44, null); // return PNG png.position = 0; return png; } private function limit8byte(n:uint):uint { if (n>0xFF) return 0xFF; else return n; } /** * @private */ private function writeChunk(png:ByteArray, type:uint, data:ByteArray):void { // Write length of data. var len:uint = 0; if (data) len = data.length; png.writeUnsignedInt(len); // Write chunk type. var typePos:uint = png.position; png.writeUnsignedInt(type); // Write data. if (data) png.writeBytes(data); // Write CRC of chunk type and data. var crcPos:uint = png.position; png.position = typePos; var crc:uint = 0xFFFFFFFF; for (var i:uint = typePos; i < crcPos; i++) { crc = uint(crcTable[(crc ^ png.readUnsignedByte()) & uint(0xFF)] ^ uint(crc >>> 8)); } crc = uint(crc ^ uint(0xFFFFFFFF)); png.position = crcPos; png.writeUnsignedInt(crc); } } } |
