I have been writing about Sencha Touch and Cordova for some time now. In a sense, it is the documentation of my learning of these two platforms in the last couple of months which might help somebody out there who is struggling with the same issues that I once had. I have never used Sencha Architect for my development environment. I am using Visual Studio as Text Editor as well as Packager for Cordova Applications with TACO extension.
In my path of mastering Cordova and Sencha Touch, I have stumbled upon building orientation aware applications using these frameworks, and this is part 1 of multi-part tutorial about how to create orientation aware cross-platform application using these two frameworks.
For any orientation aware application, there are two things that the developer should take care of:
1. Device orientation at the start of the application.
2. Orientation change events
But first, we need to look into the application architecture to decide how to incorporate orientation change in the application. As you know Sencha Touch framework works on MVC architecture with a slight twist of Stores and Profiles. If you are interested in Sencha Touch architecture and how profiles work in Sencha Touch, this post might be useful for you. Right now our focus is not on Models, Controllers, and Stores but purely on Views of the application as the orientation will only affect the views of the application. In the context of Sencha Touch, profiles also play a crucial role while designing orientation awareness. A lot of times, your views are different for different profiles of the application. For the simplicity of tutorial, I am going to take 2 profiles — one tablet, and one phone — and build orientation awareness for both of them.
Here are the steps that we are going to follow while building orientation aware application:
- Create a sample application with two profiles – Tablet and Mobile
- Install Orientation Plugin from Cordova plugin manager.
- Create views and controller
- Add views to viewport based on the orientation of the device at the start of the application
- Handle the orientation change event
- Take care of stylesheets.
To start with I have created a sample Cordova application in Visual Studio 2015 named OrientationTutorial. I have added Sencha-Touch-Debug.js and Sencha-Touch.css files to my application’s root folder and loaded them in index.html. Pretty normal stuff, isn’t it? The tricky part is, unlike Sencha Architect Visual Studio doesn’t have desired visual builder for Sencha Touch that can let you build your views, controllers, models, stores, and profiles in different folders and build a nice app.js for you in the end. So we have to do it ourselves.
In app.js, I create the application, while in view, profile, and controller folders, I create respective views, profile, and controllers. I load all the files in index.html body, and pray to God that everything works fine. (Kidding, when Sencha Touch breaks, even God cannot fix it.) The solution explorer looks something like this:
Main view is basically just the toolbar on the top of the application. Home view has some example buttons which do nothing basically. (At least for now). So here is how we are going to do it. We create main view, which is a container with a toolbar on the top, and stick it to the application forever. Then based on the orientation of the application we change add the appropriate home view to the application.
Go to config.xml, open plugin tab and add orientation plugin to your application. It should look something like this:
I have created the main view at the launch of the respective profile. That means the Main view is add when the phone/tablet profile is launched.
All the default views such as homePhone and homeTablet are by default landscape views. We will create portrait view differently. For that, add the folder to the view folder called ‘portrait’. Inside this, create homePortrait view.
Now, some important things. In our design architecture, we are going to create views based on their alias’. Sencha Touch creates alias’ based on xtypes in the background. Which means we cannot use same xtype for views which are essentially same but are designed differently for different profiles and orientation. For example, the home view has 5 buttons, which all work the same. But based on whether the view is rendered on tablet or phone and whether the orientation is portrait or landscape, the position of these buttons change. So for the different rendering, we have different views. But we might need the same xtype in the controller for these views, to tie these to some model or load data from store. To overcome this problem, we use two approaches. We tie different profile views to the same widget by inserting profile name between them explicitly at the initialization of the profile. This way the views can share the same xtype but are rendered based on their profile names. This works for profiles because on one device only one profile is activated. The code looks something like this:
However, what about orientation? Orientation can change multiple times while using the application. And so we cannot even explicitly tie the orientation view to the alias. To fix this, we simply create the view by adding widget.portrait.* to the item ID of the view whenever the portrait view is to be created, and for landscape view we just use normal alias. Also, inside controller we avoid using alias in configuration refs. That is because when you use alias in config refs and you have to use the views inside controllers, you have to invoke them twice with two different alias. This increases duplicate code.
Instead one can either use IDs or Ref. IDs are advised against by Sencha Developer Team and rightfully so. IDs add a lot of confusion as well as if you don’t destroy the views with same ids, you are in a deep trouble with multiple hours of debugging one line of code. Ref however, in my opinion, is the best approach to tackle this issue. The configuration inside controller should look something similar to:
Notice the refs in the Config.
And here is how the landscape view of the home looks -
I have created five sample buttons and arranged them in a bit vbox container and then smaller hbox containers. The arrangement has been handled in the sencha view while positioning and sizes are configured in css.
When the application is launched, the showTab function is launched with the id of homeView. showTab function checks the current orientation of the device, and accordingly adds prefix to ID in order to create the correct widget with alias. The code snippet is as following:
Now without creating portrait view of home, if we just apply orientation change event, this happens:
As you can see, the view is totally distorted, buttons cannot be seen, and Title bar cannot hold complete text. Some of this can be handled by CSS (Which I will cover in the next part of this tutorial), however, in order to give a seamless experience to the end user, one needs to design a different view altogether.
So I created a portrait version of the home view. Notice that it has same ID and same ref but NOT same xtype. This is very important. You cannot have two views with same xtype. It creates a hell of confusion inside framework, and more often than not, framework gives you wrong view for wrong orientation.
The view looks like this:
As you can see the view is still not perfect. Background is not exactly in the center, nor is it visible, as well as buttons need better alignment and title bar can do with smaller font to accommodate complete text. However, we will take care of that with CSS.
Now the only thing that remains is handling the orientation change event. For that, we add orientationchange event to viewport, and handle it in onOrientationChange function. Well, there is actually nothing much to handle, just get the active items id which in this case is homeView. Destroy the landscape component, and add the portrait with the help of showTab function which I have already described above.
In Main controller, I added control to viewport inside config, and the function onOrientationChange is created in Main controller as given below:
That’s all. The basic orientation awareness is implemented. In the next part, I would talk about creating different classes for different orientation, and handling multiple views.