0

http://jacksondunstan.com/articles/1642 I followed this to the T, yet I'm running into issues. I'm trying to save a bunch of icons. They have a custom class "Iconn" and are stored in a Vector.<Iconn> for future use. Once the user adds a new icon, I open up a filestream and use it to write the entire vector.

        public function addShortcut(f:File):void
    {
        //Display on side
        icons.reverse(); //Add to the front so it displays on the top.
        icons.push(new Iconn(f)); //Use 16x16 bitmap

        addChild(icons[icons.length - 1]);
        icons.reverse();

        //Save object
        fs = new FileStream();
        fs.open(shortcuts, FileMode.WRITE);
        fs.writeObject(icons);
        fs.close();

        reorder(); //Reorganizes the icons on the screen.
    }

This works all and well with no errors, but when I try to re-launch the application with some icons saved, the vector doesn't even exist.

    icons = new Vector.<Iconn>();
    if (shortcuts.exists)
    {
        trace("Shortcuts exist..adding");
        fs = new FileStream();
        fs.open(shortcuts, FileMode.READ);
        icons = fs.readObject() as Vector.<Iconn>;
        fs.close();
        trace("icons = " + icons); //TypeError: Error #1009: Cannot access a property or method of a null object reference.
        trace("icons length  = " + icons.length); //TypeError: Error #1009: Cannot access a property or method of a null object reference.
    }

I tried adding registerClassAlias("Vector.<Iconn>", Vector.<Iconn>);, but then I get a compiler error 1067: Implicit coercion of a value of type __AS3__.vec:Vector.<Iconn> to an unrelated type Class.

Edit: Here is my Iconn class http://pastebin.com/5TujzpvR

sfxworks
  • 1,031
  • 8
  • 27
  • One thing, you what to class register your `Iconn` class, not the Vector; registerClassAlias("Iconn", Iconn); Also is the `shortcuts` physical file written (updated) on the filesystem during the shortcuts() method and then not overwritten/deleted between app re-launches? i.e. You are not writing it to a tmp location in an Air mobile app/ – SushiHangover Oct 29 '15 at 19:04
  • I'll try registering that. And yes, using the applicationStorageDirectory and performing an overwrite every time the user clicks the button to add a new icon. Result: ArgumentError: Error #1063: Argument count mismatch on Iconn(). Expected 1, got 0. at flash.filesystem::FileStream/readObject() at Sidebar()[desktop project\src\Sidebar.as:45] Which is: icons = fs.readObject() as Vector.; after registerClassAlias("Iconn", Iconn); – sfxworks Oct 29 '15 at 19:26
  • What does your `Iconn` class look like? I see you pass an argument `f` in your code... but deserialization cannot pass any constructor argument (it doesn't really make sense in that context) so if your class has any non-optional constructor arguments you will get this error. – Aaron Beall Oct 29 '15 at 20:20
  • You need to `registerClassAlias` any non primitive class nested in your vector. Also, display objects do not work well with serialization. Though raw bitmap data does. – BadFeelingAboutThis Oct 29 '15 at 21:19
  • @Aaron that might be why. I pass over the file to the Iconn on construction so I can point to it later. [pastebin](http://pastebin.com/qUQqqb04) – sfxworks Oct 30 '15 at 03:36
  • @BadFeelingAboutThis I tried registering Bitmap and File after Iconn under my sidebar, but I get the same results. – sfxworks Oct 30 '15 at 03:36
  • For the record this question is still unanswered. I need some more detail as far as what I can and cannot serialize. – sfxworks Oct 30 '15 at 10:41
  • Serializing `Bitmap` is problematic (if not completely impossible) because it's a display object. Certainly just using `registerClassAlias` is not going to cut it there. What you can do is serialize `BitmapData` (or a `ByteArray` of image data) and implement `ISerializable` to convert it to a `Bitmap`. – Aaron Beall Oct 30 '15 at 14:42
  • You should update your question to show your `Iconn` class. – BadFeelingAboutThis Oct 30 '15 at 16:01
  • You may find this question informative, look at Vesper's and my answers. http://stackoverflow.com/questions/30125221/using-file-to-save-scene-object-locations-to-rebuild-later-in-as3 – BadFeelingAboutThis Oct 30 '15 at 16:08
  • @BadFeelingAboutThis sorry about that, I thought I had already linked it. I have found an alternative solution and that is just a writeutf of the native path of the file and then reconstruct it. It does handle a scenario where an application updates its icon without notice but I still would like to know how to get this done. – sfxworks Oct 31 '15 at 10:08
  • @Aaron Ah I see. So save the bitmap instead of the class. Thank you. – sfxworks Oct 31 '15 at 10:09

1 Answers1

2

There's a few reasons this isn't working.

  1. When objects are unserialized, you cannot pass parameters to their constructors. This means that you either need to take out all constructor arguments of ALL classes nested inside your object, or make them all optional by giving them default values.

  2. You have to register every single non-primitive class you use in the object being serialized (including the object's class itself). This includes nested classes. In your case, this includes at least:

    flash.display.Bitmap;
    flash.display.Sprite;
    flash.events.MouseEvent;
    flash.filesystem.File;
    fl.transitions.Tween;
    fl.transitions.easing.Strong;
    

    Plus anything referenced inside of those classes.

  3. Serializing display objects just doesn't work* (it is technically possible with a custom class that implements IExternalizable, but it's quite involved). If you want an example, there is one here.


The best way to serialize display objects, is to create a very very basic class that contains the bare minimum data needed to reconstruct your display object.

In your case, it looks like all you really need is bitmap data. So you could make a simple class like the following:

package 
{
    import flash.geom.Rectangle;
    import flash.utils.ByteArray;

    public class SaveData 
    {
        public var imgBytes:ByteArray;
        public var bounds:Rectangle;
        public var transparent:Boolean;
    }
}

All it stores is the raw bitmap data (a byte array of pixel values), and bounds of the bitmap.

First, you need to register all the classes used. That would look like this:

        //you need to register your class that you're using writeObject on
        registerClassAlias("SaveData", SaveData); 

        //you also need to register the two classes that are referenced in your SaveData class
        registerClassAlias("flash.utils.ByteArray", ByteArray);
        registerClassAlias("flash.geom.Rectangle", Rectangle);

        //you ALSO need to register Point since it's referenced inside the Rectangle class
        registerClassAlias("flash.geom.Point", Point);

Now, to save it, do mostly what you're already doing:

//create a vector to store your list of items to save
var saveArray:Vector.<SaveData> = new Vector.<SaveData>();

//loop through all your icons and do the following in the loop (where bitmap is the icon's bitmap):

var saveData:SaveData = new SaveData();
saveData.bounds = bitmap.getBounds(bitmap);
saveData.imgBytes = bitmap.bitmapData.getPixels(saveData.bounds);
saveData.transparent = bitmap.bitmapData.transparent;
saveArray.push(saveData);

//write the Vector to the file
fs = new FileStream();
fs.open(shortcuts, FileMode.WRITE);
fs.writeObject(saveArray);
fs.close();

Now, when you're ready to load it all back in:

fs = new FileStream();
fs.open(shortcuts, FileMode.READ);
var saveArray:Vector.<SaveData> = fs.readObject() as Vector.<SaveArray>;
fs.close();

//now loop through your array and re-create all your icons.

for(var i:int=0;i<saveArray.length;i++){
    var bmd:BitmapData = new BitmapData(saveArray[i].bounds.width, saveArray[i].bounds.height, saveArray[i].transparent);
    bmd.setPixels(saveArray[i].bounds, saveArray[i].imgBytes);

    var bitmap:Bitmap = new Bitmap(bmd);

    //instead of passing in a file to your Iconn class, tweak it so you pass in a bitmap:
    var icon:Iconn = new Iconn(bitmap);

    //do what you need to do with he icon
}
Community
  • 1
  • 1
BadFeelingAboutThis
  • 14,445
  • 2
  • 33
  • 40