I have been using Mvvmcross to develop Android application. I am dealing with the issue of ViewModel lifecycle during a rotation. It seems that generally ViewModel is preserved during a rotation. However this is not the case when I present ViewModels in MvxTabActivity. When the rotation happens it always calls a ViewModel constructor.
I have used similar code structure as in N+1 tutorial https://github.com/slodge/NPlus1DaysOfMvvmCross/tree/master/N-25-Tabbed.
Is there a way to modify this tutorial to keep ViewModels in memory during rotation when using MvxTabActivity?
The default ViewModel caching which attempts to workaround the Android rotation behaviour is based around IMvxSingleViewModelCache - so it's not too surprising it can't cope with multiple Activities and multiple ViewModels.
For where this interface is declared and used, see https://github.com/slodge/MvvmCross/search?q=IMvxSingleViewModelCache&ref=cmdform
If this behaviour is troubling you, then you should be able to work around it by one of :
1. Use fragment based tabs rather than Activity based ones
Android handles Fragment lifecycle differently to Activity ones.
2. Or continue using activity based tabs, but implement your own IMvxSingleViewModelCache
It should be simple, for example, to identify your child view models with by their 'Child' naming convention.
With this done you can then implement something like:
public class MyCustomViewModelCache
: IMvxSingleViewModelCache
{
private const string BundleCacheKey = "__mvxVMCacheKey";
private int _counter;
private IMvxViewModel _currentViewModel;
public void Cache(IMvxViewModel toCache, Bundle bundle)
{
if (toCache != null
&& toCache.GetType().Name.StartsWith("Child"))
{
// don't worry about caching child view models
return;
}
_currentViewModel = toCache;
_counter++;
if (_currentViewModel == null)
{
return;
}
bundle.PutInt(BundleCacheKey, _counter);
}
public IMvxViewModel GetAndClear(Bundle bundle)
{
var storedViewModel = _currentViewModel;
_currentViewModel = null;
if (bundle == null)
return null;
var key = bundle.GetInt(BundleCacheKey);
var toReturn = (key == _counter) ? storedViewModel : null;
return toReturn;
}
}
This class based on MvxSingleViewModelCache.cs with just one small addition.
You can register an instance of this class as the IMvxSingleViewModelCache singleton during the InitializeLastChance of your Setup.
Mvx.RegisterSingleton<IMvxSingleViewModelCache>(new MyCustomViewModelCache());
With this done, the home/tab activity should (I think) continue to work - and it'll pass the viewmodels down to the tab children after rotation.
(Other possibilities for IMvxSingleViewModelCache are possible - e.g. it could cache multiple view models - but please don't let it cache too many view models for too long or you may run into 'out of memory' conditions)
3. Or switch the Android rotation handling off
If you add the android:configChanges="orientation" flag (or it's monodroid equivalent Attribute) then you can just handle the rotation yourself.
Related
A number of our MVVMcross views depend remote services to fully display themselves. We typically kick this off a Task in ViewModel's Init() using to get it async. ViewModel properties are set in the Task upon completion, UI updated via PropertyChanged notifications.
Sometimes the remote data (and task) completes before the View has bound it's listeners and thus no property changed event is received.
This issue is touched on at async Init and Property Changed in MvvmCross but the solution feels like duplication of presentation logic.
We've had success buffering PropertyChanged notifications until the end of ViewDidLoad, but we'd like to turn below into a more generic solution by hooking into the MVX framework.
Is there a way to hook mvvmcross's view creation to fire our code off after viewDidLoad completes?
Base View Model
public abstract class BaseViewModel : MvxViewModel{
protected bool _deferPropertyChangedEvents = true;
private readonly List<PropertyChangedEventArgs> _deferedPropertyChangedEvents = new List<PropertyChangedEventArgs>();
public override void RaisePropertyChanged(PropertyChangedEventArgs changedArgs)
{
lock(_deferedPropertyChangedEvents){
if (!_deferPropertyChangedEvents)
{
base.RaisePropertyChanged(changedArgs);
}
else
{
// buffer it up
_deferedPropertyChangedEvents.Add(changedArgs);
}
}
}
public void EndDeferringPropertyChangedEvents()
{
lock(_deferedPropertyChangedEvents){
_deferPropertyChangedEvents = false;
// playback all buffered notifications
foreach (var e in _deferedPropertyChangedEvents)
{
RaisePropertyChanged(e);
}
_deferedPropertyChangedEvents.Clear();
}
}
}
Sample view
public class SomeView : MvxViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
var bindings = this.CreateBindingSet<StopView, SomeViewModel>();
.....
bindings.Apply();
// plays back any PropertyChanged() notifications that were buffered
// up while the view was initializing
// ---> want to find a way to have MVX call this
ViewModel.EndDeferringPropertyChangedEvents();
}
}
As a simple answer, I believe your own line can easily be called using a BaseViewModel cast:
// ---> want to find a way to have MVX call this
((BaseViewModel)ViewModel).EndDeferringPropertyChangedEvents();
However, on a more technical note, I think it might be useful to further examine and understand why this Deferring code is necessary - to further take a look at what the underlying threading problems are.
There are a number of factors that are puzzling me at present::
During the line bindings.Apply(); all current bound property values should be transferred from the ViewModel to the View - so calling EndDeferringPropertyChangedEvents(); in the next line should (in theory) only rarely get different values.
Further, the default MvvmCross RaisePropertyChanged method changed notifications across to the UI thread. Because ViewDidLoad is also invoked on the UI thread, this means that any RaisePropertyChanged calls made on background threads during ViewDidLoad should all be automatically deferred until after ViewDidLoad has finished and the UI thread becomes available.
Looking at the MvxNotifyPropertyChanged code, the only potential gap I can see where mutli-threading might find a way through this automatic RaisePropertyChanged deferral is in this optimisation check:
// check for subscription before potentially causing a cross-threaded call
if (PropertyChanged == null)
return;
(from https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNotifyPropertyChanged.cs#L76)
If your ViewModel Init method is also using async for it's Task management, then this async code should also be using the UI thread - so the "callback" of this async operation should also be marshalled back to the UI thread (and so shouldn't be executed during ViewDidLoad itself).
As I said, these factors are puzzling me - I don't have a definitive answer/explanation - sorry! But I'd love to see an example problem and to try to help solve it at a generic level.
In the N+1 video #34 (Progress), there was an example of using CreateBindingSet() for the Android version, which is not typical. But the narrator also mentioned briefly that the same can be done on the Windows platform.
As much as I tried, however, I am unable to get a View's property to be bound to its ModelView on the Windows Phone. I always get a NullReferenceException.
The closest I came was the code below, including suggestions from ReSharper. Here's my FirstView.xaml.cs:
using Cirrious.MvvmCross.Binding.BindingContext;
using Whatever.ViewModels;
namespace Whatever {
// inheriting from IMvxBindingContextOwner was suggested by ReSharper also
public partial class FirstView : BaseView, IMvxBindingContextOwner {
public class MyBindableMediaElement
{
private string _theMediaSource = "whatever";
public string TheMediaSource
{
get
{
return _theMediaSource;
}
set
{
_theMediaSource = value;
}
}
}
public FirstView()
{
InitializeComponent();
_mediaElement = new MyBindableMediaElement(this.theMediaElement);
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
// the corresponding view model has a .SongToPlay property with get/set defined
set.Bind(_mediaElement).For(v => v.TheMediaSource).To(vm => vm.SongToPlay);
set.Apply();
}
public IMvxBindingContext BindingContext { get; set; } // this was suggested by ReSharper
}
I get a NullReferenceException in MvxBaseFluentBindingDescription.cs as soon as the view is created. The exact location is below:
protected static string TargetPropertyName(Expression<Func<TTarget, object>> targetPropertyPath)
{
var parser = MvxBindingSingletonCache.Instance.PropertyExpressionParser; // <----- exception here**
var targetPropertyName = parser.Parse(targetPropertyPath).Print();
return targetPropertyName;
}
I have not seen a working example of creating a binding set on a Windows Phone emulator. Has anyone gotten this to work? Thanks.
I can confirm that the narrator said that remark a little too flippantly without actually thinking about how he might do it...
However, with a little effort, you definitely can get the CreateBindingSet to work in Windows if you want to.
Before you start, do consider some alternatives - in particular, I suspect most people will use either Windows DependencyProperty binding or some hand-crafted code-behind with a PropertyChanged event subscription.
If you do want to add CreateBindingSet code to a Windows project then:
Add the Binding and BindingEx assemblies to your Ui project - the easiest way to do this is using nuget to add the BindingEx package.
In your Setup class, override InitializeLastChance and use this opportunity to create a MvxWindowsBindingBuilder instance and to call DoRegistration on that builder. Both these first two steps are covered in the n=35 Tibet binding video - and it's this second step that will initialise the binding framework and help you get past your current 'NullReferenceException' (for the code, see BindMe.Store/Setup.cs)
In your view, you'll need to implement the IMvxBindingContextOwner interface and you'll need to ensure the binding context gets created. You should be able to do this as simply as BindingContext = new MvxBindingContext();
In your view, you'll need to make sure the binding context is given the same DataContext (view model) as the windows DataContext. For a Phone Page, the easiest way to do this is probably just to add BindingContext.DataContext = this.ViewModel; to the end of your phone page's OnNavigatedTo method. Both steps 3 and 4 could go in your BaseView if you intend to use Mvx Binding in other classes too.
With this done, you should be able to use the CreateBindingSet code - although do make sure that all binding is done after the new MvxBindingContext() has been created.
I've not got a windows machine with me right now so I'm afraid this answer code comes untested - please do post again if it does or doesn't work.
I can confirm it works almost perfectly; the only problem is, there are no defaults register, so one has to do the full binding like:
set.Bind(PageText).For(c => c.Text).To(vm => vm.Contents.PageText).OneTime();
to fix this, instead of registering MvxWindowsBindingBuilder, I am registering the following class. Note: I have just created this class, and needs testing.
public class UpdatedMvxWindowsBindingBuilder : MvxWindowsBindingBuilder
{
protected override void FillDefaultBindingNames(IMvxBindingNameRegistry registry)
{
base.FillDefaultBindingNames(registry);
registry.AddOrOverwrite(typeof(Button), "Command");
registry.AddOrOverwrite(typeof(HyperlinkButton), "Command");
//registry.AddOrOverwrite(typeof(UIBarButtonItem), "Clicked");
//registry.AddOrOverwrite(typeof(UISearchBar), "Text");
//registry.AddOrOverwrite(typeof(UITextField), "Text");
registry.AddOrOverwrite(typeof(TextBlock), "Text");
//registry.AddOrOverwrite(typeof(UILabel), "Text");
//registry.AddOrOverwrite(typeof(MvxCollectionViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxTableViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxImageView), "ImageUrl");
//registry.AddOrOverwrite(typeof(UIImageView), "Image");
//registry.AddOrOverwrite(typeof(UIDatePicker), "Date");
//registry.AddOrOverwrite(typeof(UISlider), "Value");
//registry.AddOrOverwrite(typeof(UISwitch), "On");
//registry.AddOrOverwrite(typeof(UIProgressView), "Progress");
//registry.AddOrOverwrite(typeof(IMvxImageHelper<UIImage>), "ImageUrl");
//registry.AddOrOverwrite(typeof(MvxImageViewLoader), "ImageUrl");
//if (_fillBindingNamesAction != null)
// _fillBindingNamesAction(registry);
}
}
This is a skeleton from Touch binding, and so far I have only updated three controls to test out (Button, HyperButton and TextBlock)
just thought I would share something I have found to help delivering data across an application I am wondering what others think about this I wanted to have a way to capture event bubbling up back down to other components but in a way that it would make it easy to use anywhere in may app so this is what i came up with.
I Extend the Application class and wrap in an abstract function registering a function of any component anywhere and capture it at the top most level and pass to where ever i chose to.
public class AxApplication extends Application
{
public var ___registeredEvents:Array = new Array();
public var ___registeredFunctions:Array = new Array();
function AxApplication()
{
super();
}
public function localRegisterForEvent(e:Event,func:*,caller:*):void
{
caller.addEventListener(e.type,localCallerEventHandler,true,3);
caller.addEventListener(e.type,localCallerEventHandler,false,3);
___registeredEvents.push(e);
___registeredFunctions.push(func);
}
public function localCallerEventHandler(e:*):void
{
if(e!=null)
{
for(var i:int = 0 ; i< ___registeredEvents.length; i++)
{
if(e.type == ___registeredEvents[i].type)
{
___registeredFunctions[i](e);
//the registered function gets called
//there no garbage collection implemented!
}
}
}
}
}
I think that is not a very useful solution. Why? Because you scatter AxApplication references around the application. Views and Model instance don't need any references to the application at all. It would be better to to implement a controller layer which uses a simple eventBus property, which could look like:
private static const _EVENT_BUS:IEventDispatcher = FlexGlobals.topLevelApplication;
protected final function eventBus():IEventDispatcher {
return _EVENT_BUS;
}
If you implement a base view controller/mediator (depending from which framework you're coming), you don't have any reference to non-framework classes at all, which makes it highly reusable. It is just a simple reuse of the Application singleton which you use to dispatch system wide events. You register listeners in the view controller/mediator and update the views or models accordingly. RobotLegs for example uses a system wide event dispatcher as well.
Why not just using the parentApplication approach? Because you can't implement tests (the generated test-runner of IDEs won't extend your AxApplication) or just yank the components/models in a different application - that is basically not possible.
I am using Box2D for the first time seriously in a medium sized Flash Game that I am working on. My current experience with Box2D is limited to creating a world, bodies and adding those bodies to the world in a functional manner.
I'm finding it easy enough to integrate Box2D into my game environment, maintaining well-written code and have completed a few tutorials that walk through dealing with collisions. The issue that I'm facing now is that my game will have many bodies, each interacting with other bodies in different ways, and I'm finding it hard to write my own b2ContactListener subclass without it getting extremely messy.
Based off a tutorial I used, I have created my own subclass of b2ContactListener and added an override of the BeginContact() method. The argument that BeginContact() receives when it is called will reference an instance of b2Contact, through which I can access two b2Fixture instances (the two instances that have collided). I am then able to access the b2Body instance associated with each of those b2Fixtures.
Problem: Currently I have a roundabout way of finding out what two things collided (i.e. whether they're a wall and a missile, or the player and a tree, etc) which uses GetUserData() and looks like this as an example:
var f1Player:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Player
var f2Player:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Player
var f1Tree:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Tree
var f2Tree:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Tree
// ... continutes with all possible combinations.
// Example of managing a collision:
if(f1Player && f2Tree)
{
// Player (FixtureA) and Tree (FixtureB)
}
if(f2Player && f1Tree)
{
// Player (FixtureB) and Tree (FixtureA)
}
As you can see, this is going to end up extremely long and unmanageable. I also have to write each set of actions to perform twice to cater for a certain element being FixtureA or FixtureB, or vice versa (obviously in the form of a function call with the parameters swapped around rather than literally re-written).
This is clearly not the correct approach, but I haven't been able to locate resources that more thoroughly explain collision detection management.
Does anyone have experience with collision detection management using Box2D that they can share? Also, is using SetUserData( entityThatOwnsTheBody ); the correct way to be using that method?
Yeah, it's a bit of a nuisance indeed. Actually I think the way you have it is quite typical.
fwiw Box2D itself has to deal with a similar problem when testing whether fixtures overlap. There are a bunch of functions such as b2CollideCircles, b2CollidePolygonAndCircle, b2CollidePolygons etc, and when two fixtures come near each other the engine chooses which of these functions should be used.
It does this by putting the function pointers in a 2-dimensional array, then looks up the appropriate function in this array by using the two shape types as index. See the first three functions in b2Contact.cpp for details.
Of course, if you can't pass around function references like this in AS3 then I guess this answer doesn't help much, but I thought I would post anyway as C/C++/JS users might come by.
I've used c++ version of Box2d, but I think the same approach will work in actionscript. I create a class Object, that contain a b2Body *_body pointer and a pointer to graphical representation. _body's UserData was set to point to Object *. class Object had the following methods:
virtual bool acceptsContacts ();
virtual void onContactBegin (const ContactData &data);
virtual void onContactEnded (const ContactData &data);
virtual void onContactPreSolve (const ContactData &data);
virtual void onContactPostSolve (const ContactData &data);
When collision was detected in b2ContactListener subclass, it checked if collided bodies have user data. If so, it casted their user data to Object* and if any of the collided objects accepted contacts - it created ContactData ( a class with all required information about collision) and put it in it's internal list to deliver later.
When b2World::update method returned, ContactListener delivers all contact information to objects to process. Delivery was delayed in order you could create new bodies, joints and so on, right when processing collision (which is not allowed while update is executing)
Also you must notify ContactListener (just put a pointer to it inside ContactData) if one of the collided body was deleted during collision processing, so it can invalidate appropriate contacts and not deliver them
I've come up with something much nicer than the original.
Firstly, I just have my Being class (which owns a b2Body) set itself as its bodies' UserData. This class will also contain an onContact() method and look similar to the below:
public class Being
{
private var _body:b2Body;
public function Being()
{
// Define the body here.
// ...
_body.SetUserData(this);
}
public function onCollision(being:Being = null):void
{
//
}
}
Then in my own b2ContactListener implementation, I simply pass the colliding Being (or null, if there is no Being assigned to the colliding b2Body's UserData) to the opposing Being's onCollision():
override public function BeginContact(contact:b2Contact):void
{
var bodyA:b2Body = contact.GetFixtureA().GetBody();
var bodyB:b2Body = contact.GetFixtureB().GetBody();
var beingA:Being = bodyA.GetUserData() as Being || null;
var beingB:Being = bodyB.GetUserData() as Being || null;
beingA && beingA.onCollision(beingB);
beingB && beingB.onCollision(beingA);
}
And finally in each of my subclasses of Being, I can easily prepare logic appropriate for a collision between other Beings of a certain type:
class Zombie extends Being
{
override public function onCollision(being:Being = null):void
{
if(being && being is Bullet)
{
// Damage this Zombie and remove the bullet.
// ...
}
}
}
I am writing an iOS game in Flash and I need a way to clone polymorphic objects.
I have BaseClass, SubClass1, SubClass2 (and so on...) and I need a clone() method in BaseClass, that will create a copy of the current object, without a conditional such as
var obj:BaseClass;
if(this is SubClass1) {
obj = new SubClass1();
}else if(this is SubClass2) {
obj = new SubClass2();
}else...
I need a way to create an object and create the exact bytes (yes, a shallow copy is enough for my purpose) of the object. I've looked at:
AS3 - Clone an object
As3 Copy object
http://actionscripthowto.com/how-to-clone-objects-in-as3/
But none seem to work. Probably not available in AIR 3.3 for iOS SDK. (they compile, but the code doesn't work in my case)
Is there any other way, or did anybody achieve to clone an object in AIR for iOS?
Thanks,
Can.
Bit-by-bit cloning cannot be done with ActionScript, unless your class only contains primitive values (i.e. a simple data structure). That's what the ByteArray approach you've linked to in this question's answer is used for - but when you're dealing with complex types, especially display objects, you'll soon come to the limits (as, I gather, you have already realized).
So this more or less leaves you with two options:
Create a new object and copy all of its fields and properties.
This is the way to go if you're going to need behavior and field values, and you didn't use any drawing methods (i.e., you can not copy vector graphics this way). Creating a new class instance without knowing its exact type can be done in a generalized way using reflections, getQualifiedClassName() and getDefinitionByName() will help you there, and if you need more than just the name, describeType(). This does have limits, too, though:private fields will not be available (they don't appear in the information provided by describeType()), and in order to not run into performance problems, you will have to use some sort of cacheing. Luckily, as3commons-reflect has already solved this, so implementing the rest of what you need for a fully functional shallow copy mechanism is not too complex.
Create a new instance like this:
var newObject:* = new Type.forInstance( myObject ).clazz();
Then iterate over all accessors, variables and dynamic properties and assign the old instance's values.
I have implemented a method like this myself, for an open source framework I am working on. You can download or fork it at github. There isn't any documentation yet, but its use is as simple as writing:
var myCopy:* = shallowCopy( myObject );
I also have a copy() method there, which creates a true deep copy. This, however, has not been tested with anything but data structures (albeit large ones), so use at your own risk ;)
Create a bitmap copy.
If you do have vector graphics in place, this is often easier than recreating an image: Simply draw the content of the object's graphics to a new Bitmap.
function bitmapCopy( source:Sprite ):Bitmap {
source.cacheAsBitmap = true;
var bitmapData:BitmapData = new BitmapData( source.width, source.height, true, 0xFFFFFF );
bitmapData.draw( source, new Matrix(), null, null, null, true );
return new Bitmap( bitmapData, PixelSnapping.AUTO, true );
}
You need to create an abstract clone method in the base class and implement it for each subclass. In the specific implementations, you would copy all of the properties of the object to the new one.
public class BaseClass {
public function clone():BaseClass
{
// throw an error so you quickly see the places where you forgot to override it
throw new Error("clone() should be overridden in subclasses!");
return null;
}
}
public class Subclass1 extends BaseClass {
public override function clone():BaseClass
{
var copy:Subclass1 = new Subclass1();
copy.prop1 = prop1;
copy.prop2 = prop2;
// .. etc
return copy;
}
}
If you wanted to create a generic default implementation of clone, you could use describeType to access the properties and copy them over:
public function clone():BaseClass
{
var defn:XML = describeType(this);
var clsName:String = defn.#name;
var cls:Class = getDefinitionByName(clsName) as Class;
var inst:* = new cls();
for each(var prop:String in (defn.variable + defn.accessor.(#access == 'readwrite')).#name )
{
inst[prop] = this[prop];
}
return inst;
}
The main issue with this is that the describeType XML can get quite large - especially if you are dealing with objects that extend DisplayObject. That could use a lot of memory and be slow on iOS.