Problem
The view's size property values are intended to reflect the rendered dimensions. However, if these properties are queried too soon after rendering has completed, the old values will be output.
The advice of querying property values within a window open event listener does not work.
Test case
var win = Ti.UI.createWindow({
// fullscreen: false, // once fixed, please check with this line uncommented
backgroundColor:'red'
});
var view = Ti.UI.createView({
backgroundColor:'blue',
width:'50',
height:50
});
win.add(view);
Ti.API.info("*** Before window open - view.size.width = 50 ***");
Ti.API.info("view.size w x h: " + view.size.width + " x " + view.size.height); // incorrect
view.width = 100;
win.addEventListener('open', function(){
Ti.API.info("*** Window open - view.size.width = 100 ***");
Ti.API.info("view.size w x h: " + view.size.width + " x " + view.size.height); // incorrect
});
Ti.Gesture.addEventListener('orientationchange', function(){
Ti.API.info("*** orientationchange - view.size.width = 200 ***");
view.width = 200;
Ti.API.info("view.size w x h: " + view.size.width + " x " + view.size.height); // incorrect
});
win.open();
Logs
1454 AndroidRuntime D >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
1454 AndroidRuntime D CheckJNI is ON
60 InputManagerService W Got RemoteException sending setActive(false) notification to pid 1430 uid 10044
1454 AndroidRuntime D --- registering native functions ---
60 ActivityManager I Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.appcelerator.testing10/.Testing10Activity }
60 ActivityManager I Start proc com.appcelerator.testing10 for activity com.appcelerator.testing10/.Testing10Activity: pid=1461 uid=10044 gids={1015, 3003}
1454 AndroidRuntime D Shutting down VM
1454 AndroidRuntime I NOTE: attach of thread 'Binder Thread #3' failed
1461 TiApplication I (main) [0,0] checkpoint, app created.
1461 TiApplication I (main) [536,536] Titanium 1.8.0.1 (2011/12/22 13:09 fbdc96f)
1461 TiFastDev D (main) [164,700] Enabling Fastdev on port 58902
1461 TiFastDev D (main) [10,710] sent tokens successfully
1461 TiFastDev D (main) [1,711] Fastdev session handshake succesful.
1461 TiApplication I (main) [8,719] Titanium Javascript runtime: v8
1461 TiApplication W (main) [68,787] activity stack is emtpy, unable to get current activity
1461 TiRootActivity I (main) [0,0] checkpoint, on root activity create, savedInstanceState: null
1461 TiApplication W (main) [119,119] activity stack is emtpy, unable to get current activity
1461 TiApplication W (main) [1,120] activity stack is emtpy, unable to get current activity
1461 TiApplication W (main) [1,121] activity stack is emtpy, unable to get current activity
1461 TiApplication E (KrollRuntimeThread) [937,1058] APP PROXY: ti.modules.titanium.app.AppModule@44eb3110
1461 TiAssetHelper D Fetching "app.js" with Fastdev...
1461 TiAPI I *** Before window open - view.size.width = 50 ***
1461 TiAPI I view.size w x h: 0 x 0
1461 TiAPI I *** Window open - view.size.width = 100 ***
1461 TiAPI I view.size w x h: 0 x 0
1461 TiRootActivity I (main) [0,0] checkpoint, on root activity resume. activity = com.appcelerator.testing10.Testing10Activity@44e76478
60 ActivityManager I Displayed activity com.appcelerator.testing10/.Testing10Activity: 3359 ms (total 3359 ms)
60 WindowManager I Setting rotation to 1, animFlags=1
60 ActivityManager I Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/1 nav=3/1 orien=2 layout=34 uiMode=17 seq=70}
1461 TiAPI I *** orientationchange - view.size.width = 200 ***
1461 TiAPI I view.size w x h: 100 x 50
60 ActivityManager I Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/2 nav=3/1 orien=2 layout=34 uiMode=17 seq=71}
60 WindowManager I Setting rotation to 0, animFlags=0
60 ActivityManager I Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/2 nav=3/1 orien=1 layout=34 uiMode=17 seq=72}
1461 TiAPI I *** orientationchange - view.size.width = 200 ***
1461 TiAPI I view.size w x h: 200 x 50
Discussions
See Q/A topic:
http://developer.appcelerator.com/question/124426/how-does-dimensions-work#216821
Workaround
Using setTimeout allows the size values to be updated to reflect the re-rendered view.
Thanks for raising this ticket, Ivan. I've verified the behavior and so am moving it to the main project now.
I think what is missing here is an event to tell the application that the view has finished laying out components. That is, when you update the
width
orheight
property, you trigger a new layout cycle, but we don't have any way to tell when it finishes.I'm not Android Java programmer, so I can't comment is that possible to do or not. But - I think this can be done from JavaScript. :) Currently I'm using one approach in my app to get this functionality right, but in limited edition that suits my needs. Now, I have made basic example that shows idea how the problem from this ticket can be solved using pure JavaScript. The code can be found here: https://gist.github.com/1505988 It just shows basic idea and it needs more work to make it work in in any case (different layout mechanisms, position properties, something else?). While working on that example, I noticed issue. I'm working on Windows XP, example is compiled with Titanium SDK version: 1.9.0 (12/20/11 19:16 fd3b938) and I'm using Android 3.1 (Lenovo Thinkpad tablet). View can't be re-sized when it has dimension defined in configuration object passed to factory method, or when dimension property is set with non-numeric value (percentage, 'auto'). You can see this issue by running example, pressing "Set fixed" button, "Set percentage" button and then "Set fixed" button (last attempt to set fixed value will fail), or by commenting out a line that defines view's height:
That is obvious bug, I think. (ah yes, almost forgot, "alert" dialogs don't show in right order) Now, to return to the point, as you can see, dimensions can be calculated from JavaScript. I think that you use some kind of JS wrappers to native components, so if this can't be implemented in Java, maybe it can in JavaScript. :) Hope this helps. Cheers
But what if window is already opened (I can change element size after window opens)? My initial ticket report was about that. We need to write "setTimeout" and other magic just to get basic thing as element size?!
I have reopened and updated this ticket, as the values are incorrect even using the the advice to use the window open event.
It seems that the size dimensions aren't available on window open, but they are a few milliseconds later. This code was run in the Android Emulator using Ti 1.8.1 and Android 2.2:
The result is: I/TiAPI ( 603): labelCreatedBeforeWinOpen height after no timeout: 0 I/TiAPI ( 603): labelCreatedAfterWinOpen height after no timeout: 0 I/TiAPI ( 603): labelCreatedBeforeWinOpen height after timeout of 1ms: 0 I/TiAPI ( 603): labelCreatedAfterWinOpen height after timeout of 1ms: 19 I/TiAPI ( 603): labelCreatedBeforeWinOpen height after timeout of 10ms: 19 I/TiAPI ( 603): labelCreatedAfterWinOpen height after timeout of 10ms: 19 I/TiAPI ( 603): labelCreatedBeforeWinOpen height after timeout of 100ms: 19 I/TiAPI ( 603): labelCreatedAfterWinOpen height after timeout of 100ms: 19 I/TiAPI ( 603): labelCreatedBeforeWinOpen height after timeout of 1000ms: 19 I/TiAPI ( 603): labelCreatedAfterWinOpen height after timeout of 1000ms: 19
Here is a possible workaround for this issue, which will check every 10ms if the size has been calc'd for the window object yet. Once it has then it will call the callback function, which the developer can use as their window load function.
Yeah, I also noticed that "open" event is (sometimes) triggered before window is actually rendered. For example, I show activity indicator before creating a window and it's content and I use window's "open" event to hide activity indicator. In some cases, activity indicator hides before window content is visible on screen. Introducing timeout for activity indicator hiding (about 100ms) solves that issue. But I think that issue in different thing that has influence on this issue.
Getting the height of a view with the setTimeout method has worked for me. Until I have more items than fit on the screen. It seems to be impossible to get the height of a view that is not on screen, even though one can for example scroll to reveal it.
Julian Please raise a new ticket for that issue, including a simple test case. Read [How to Submit a Bug Report](https://wiki.appcelerator.org/display/guides/How+to+Submit+a+Bug+Report) to find out the correct way to do this. Many thanks for your input.
In 2.0, there is a new 'postlayout' event which is fired after a layout pass occurs. If you replace the 'open' event with 'postlayout' it should work as expected. Please refer to http://docs.appcelerator.com/titanium/2.0/index.html#!/guide/Transitioning_to_the_New_UI_Layout_System for more information.
OK, thanks. But, IMHO, this ticket shouldn't be marked as invalid (since it's valid ticket - at least it was at the time it was reported).
Agreed. This ticket was fixed as part of composite layout implementation in 2.0 release.
Thanks Neeraj. :)
Verified fixed on: Titanium SDK: 2.2.0.v20120716092112 Titanium Studio, build: 2.1.1.201207121732 Devices Android 3.2 and Android emulator.