Auto-resize Dojo Mobile Charts on Orientation Change

The best I can tell, Dojo’s dojox.mobile.Charts2D do not auto-resize on their own when the phone’s orientation changes. I posted a question on how to get around this on the Dojo Community Forum and never got an answer. So, I had to cobble together my own solution.

I have to point out that the functionality I built by hand is inherent in Flex and Silverlight, and you wouldn’t even bat an eyelash thinking about this. So, from a productivity standpoint I spent about double and maybe even triple the time I should have needed in order to sort through why things weren’t working as they should, and to build my own best practice for handling it. 

I do consider what I built as a hack, so caveat emptor. It should at least give you a good starting point to improve on what I’ve already done. There are some important things to note.

  • Here’s a sample demonstrating the functionality: https://andygup.net/samples/realestate/
  • Dojo does not provide any State properties on the View. So, I had to build that.
  • Dojo does not provide any way to bind a dijit to a mobile View. In other words, this enables the Chart to take action automatically when something happens in the View. Check…yep, I bolted that in.
  • Dojo, as far as I know, does not provide a way to detect when the phone’s orientation changes. So you have to listen for that at the window object level. I’m fairly certain that the pattern I used is not completely reliable across all platforms, but it’s what I had to work with. So, I built that too.
  • I also had to detect if there was no orientation change prior to a View transition. This was so that I didn’t unnecessarily redraw the chart and make it appear to flicker. This check was important because my chart is in a secondary View. There seems to be a bug in charts redraw() function in that the chart may self destruct if you try to redraw it from a different View.
  • There’s a bug in the Android native browser that passes the previous orientation event object to the listener. You actually have to set an event timer so that you retrieve the final, and most recent, orientation event object.
Here’s how you initialize the chart. In this case, I’m using a pieChart. This snippet also includes the html markup:
pieChart = new com.agup.PieChart("chart1","statsView").pieChart;

<div id="chart1ParentDiv" dojoType="dojox.mobile.RoundRect">
        <div id="chart1" style="width:100%; height: 350px;"></div>
</div>

Here’s the PieChart Class that I built to encapsulate the functionality I described above:

dojo.declare("com.agup.PieChart",null,{
    pieChart:null,
    orientationChanged:null,
    constructor:function(chartDiv,chartView){
        this.pieChart = this._createChart(chartDiv);
        this.orientationChanged = false;
        if(chartView)this._setTransitionListener(chartView);
        if(chartView)this._setOrientationListener();
    },
    _createChart:function(chartDiv){
        //create the chart
        //Had problems with using just HTML markup, so creating it here and piping to DIV
        var pieChart = new dojox.charting.Chart2D(chartDiv);
        //set the theme
        pieChart.setTheme(dojox.charting.themes.PlotKit.blue);
        //add plot
        pieChart.addPlot("default", {
            type: "Pie",
            radius: 100,
            fontColor: "black",
            labelOffset: "-20"
        });

        pieChart.isVisible = false; //NOTE: this is a new public property that we inject

        return pieChart;
    },
    _setTransitionListener:function(/* DIV of dojox.mobile.View where chart resides - typeof String  */view){
        var test = dijit.byId(view);
        var pieChart = this.pieChart;
        dojo.connect(test, "onAfterTransitionIn",null,
                dojo.hitch(this,function(){
                    pieChart.isVisible = true;
                    if(pieChart != null && this.orientationChanged == true)var time = setTimeout(function(){pieChart.resize()},700);
                })
        );

        dojo.connect(test, "onAfterTransitionOut",null,
                function(){
                    pieChart.isVisible = false;
                }
        );
    },
    _setOrientationListener:function(){
        var supportsOrientationChange = "onorientationchange" in window,
                orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";

        window.addEventListener(orientationEvent,
            dojo.hitch(this,function(){
                var pieChart = this.pieChart;
                var orientationChanged = this.orientationChanged;
                if(pieChart != null && pieChart.isVisible == false){
                    orientationChanged = true;
                }
                if(pieChart != null && pieChart.isVisible == true){
                    orientationChanged = false;
                    var time = setTimeout(function(){pieChart.resize()},700);
                }
        }), false);
    }
});

The Largest Conference For Mapping and Geospatial Developers – Esri DevSummit 2012

I’ll be presenting at the Esri DevSummit next week so if you are attending please swing by my sessions and say “hi”. If you aren’t familiar with Esri or the conference, about 1400 developers and other technical experts converge on Palm Springs, California every Spring to learn all things technical about building commercial and enterprise geographic information systems. There will be everything from introductory Dojo workshops to deep dives into the heart of our APIs.

If you’re around here’s my schedule. I’d be very interested to hear about what you are working on:

Monday,  March 26

Getting Started with the ArcGIS Web APIs – 8:30am – 11:45am, Pasadena Room. I’ll be presenting the portion related to our ArcGIS API for JavaScript.

Gettings Started with Smartphone and Tablet ArcGIS Runtime SDKs – 1:15pm – 4:45pm, Pasadena Room. In this session, I’ll be presenting on our ArcGIS Runtime SDK for Android.

Wednesday, March 28

Flex the World – 10:30am, Demo Theater 2. I’ll be presenting with my esteemed colleague Sajit Thomas on Apache Flex.

Figuring out Date Time Zones in Adobe Flex/Actionscript

I was really frustrated the past few weeks when a major clock component of an app kept failing when viewed from different time zones. It was always supposed to show Antarctica time and only that. I searched and searched for definitive examples on the web, but never found what I needed.

At the heart of this is the ActionScript Date Class. It’s unfortunately a very poor implementation. I expected to simply have a property where you give the Class a timezone offset and presto you magically get the time for that location of the world. Silly me. In addition the documentation is incorrect in that the getTime() method does NOT in fact report the time in UTC. And, to make things even more fun, the getTimeZoneOffset() method returns minutes rather than milliseconds.

However, after enough iterations I was finally able to cobble together what seems to be a graceful solution. The trick is to use the getTime() method. Then add the time zone offset in milliseconds. This, in theory, gives you UTC time. Then add or subtract the number of hours in milliseconds for your fixed clock. Oh, and I also used the DateTimeFormatter to beautify everything up. Here’s the code to hopefully save someone else a bunch of additional coding:

var df:DateTimeFormatter = new DateTimeFormatter();
df.useUTC = false;
df.timeStyle = "short";
df.dateStyle = "medium";
var d:Date = new Date();

var millisecondsPerThreeHours:int = 1000 * 60 * 60 * 3; //using a -3 hour UTC offset
var timeZoneOffsetMilliSeconds:Number = d.getTimezoneOffset() * 60 * 1000;

d.setTime(d.getTime() + timeZoneOffsetMilliSeconds - millisecondsPerThreeHours);

var antarcticaTime2:String = d.toString();
antarcticaTimeTextArea.text = df.format(antarcticaTime2);