Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Cross-Platform Mobile Development

96 Articles
article-image-getting-started-playstation-mobile
Packt
26 Apr 2013
7 min read
Save for later

Getting Started with PlayStation Mobile

Packt
26 Apr 2013
7 min read
(For more resources related to this topic, see here.) The PlayStation Mobile (PSM) SDK represents an exciting opportunity for game developers of all stripes, from hobbyists to indie and professional developers. It contains everything you need to quickly develop a game using the C# programming language. Perhaps more importantly, it provides a market for those games. If you are currently using XNA, you will feel right at home with the PSM SDK. You may be wondering at this point, Why develop for PlayStation Mobile at all? Obviously, the easiest answer is, so you can develop for PlayStation Vita , which of itself will be enough for many people. Perhaps, though the most important reason is that it represents a group of dedicated gamers hungry for games. While there are a wealth of games available for Android, finding them on the App Store is a mess, while supporting the literally thousands of devices is a nightmare. With PlayStation Mobile, you have a common development environment, targeting powerful devices with a dedicated store catering to gamers. We are now going to jump right in and get those tools up and running. Of course, we will also write some code and show how easy it is to get it running on your device. PlayStation Mobile allows you to target a number of different devices and we will cover the three major targets (the Simulator, PlayStation Vita, and Android). You do not need to have a device to follow along, although certain functionality will not be available on the Simulator. One thing to keep in mind with the PlayStation Mobile SDK is that it is essentially two SDKs in one. There is a much lower level set of libraries for accessing graphics, audio, and input, as well as a higher-level layer build over the top of this layer, mostly with the complete source available. Of course, underneath this all there is the .NET framework. In this article, we are going to deal with the lower level graphics interface. If the code seems initially quite long or daunting for what seems like a simple task, don't worry! There is a much easier way that we will cover later in the book. Accessing the PlayStation Mobile portal This recipe looks at creating a PSM portal account. For this process it is mandatory to download and use the PSM SDK. Getting ready You need to have a Sony Entertainment Network (SEN) account to register with the PSM portal. This is the standard account you use to bring your PlayStation device online, so you may already have one. If not, create one at http://bit.ly/Yiglfk before continuing. How to do it... Open a web browser and log in to http://psm.playstation.net. Locate and click on the Register button. Sign in using the SEN account. Agree to the Terms and Conditions. You need to scroll to the bottom of the text before the Agree button is enabled. But, you always read the fine print anyways... don't you? Finally select the e-mail address and language you want for the PlayStation Mobile portal. You can use the same e-mail you used for your SEN account. Click on Register. An e-mail will be sent to the e-mail account you used to sign up. Locate the activation link and either click on it, or copy and paste into a browser window: Your account is now completed, and you can log in to the PSM developer portal now. How it works... A PlayStation Mobile account is mandatory to download the PSM tools. Many of the links to the portal require you to be logged in before they will work. It is very important that you create and activate your account and log in to the portal before continuing on with the book! All future recipes assume you are logged in to the portal. Installing the PlayStation Mobile SDK This recipe demonstrates how to install the PlayStation Mobile SDK. Getting ready First you need to download the PlayStation Mobile SDK; you can download it from http://bit.ly/W8rhhx. How to do it... Locate the installation file you downloaded earlier and double-click to launch the installer. Say yes to any security related questions. Take the default settings when prompting, making sure to install the runtimes and GTK# libraries. The installer for the Vita drivers will now launch. There is no harm in installing them even if you do not have a Vita: Installation is now complete; a browser window with the current release notes will open. How it works... The SDK is now installed on your machines. Assuming you used default directories, the SDK will be installed to C:Program Files (x86)SCEPSM if you are running 64 bit Windows, or to C:Program FilesSCEPSM if you are running 32 bit Windows. Additionally all of the documentation and samples have been installed under the Public account, located in C:UsersPublicDocumentsPSM. There's more... There are a number of samples available in the samples directory and you should certainly take a moment to check them out. They range in complexity from simple Hello World applications, up to a full blown 3rd person 3D role playing game (RPG). They are, however, often documented in Japanese and often rely on other samples, making learning from them a frustrating experience at times, at least, for those of us who do not understand Japanese! Creating a simple game loop We are now going to create our first PSM SDK application, which is the main loop of your application. Actually all the code in this sample is going to be generated by PSM Studio for us. Getting ready From the start menu, locate and launch PSM Studio in the PlayStation Mobile folder. How to do it... In PSM Studio, select the File | New | Solution... menu. In the resulting dialog box, in the left-hand panel expand C# and select PlayStation Suite, then in the right-hand panel, select PlayStation Suite Application. Fill in the Name field, which will automatically populate the Solution name field. Click on OK. Your workspace and boilerplate code will now be created; hit the F5 key or select the Run | Start Debugging menu to run your code in the Simulator. Not much to look at, but it's your first running PlayStation Mobile application! Now let's take a quick look at the code it generated: using System; using System.Collections.Generic; using Sce.PlayStation.Core; using Sce.PlayStation.Core.Environment; using Sce.PlayStation.Core.Graphics; using Sce.PlayStation.Core.Input; namespace Ch1_Example1 { public class AppMain{ private static GraphicsContext graphics; public static void Main (string[] args){ Initialize (); while (true) { SystemEvents.CheckEvents (); Update (); Render (); } } public static void Initialize (){ graphics = new GraphicsContext (); } public static void Update (){ var gamePadData = GamePad.GetData (0); } public static void Render () { graphics.SetClearColor (0.0f, 0.0f, 0.0f, 0.0f); graphics.Clear (); graphics.SwapBuffers (); } } } How it works... This recipe shows us the very basic skeleton of an application. Essentially it loops forever, displaying a black screen. private static GraphicsContext graphics; The GraphicsContext variable represents the underlying OpenGL context. It is used to perform almost every graphically related action. Additionally, it contains the capabilities (resolution, pixel depth, and so on) of the underlying graphics device. All C# based applications have a main function, and this one is no exception. Within Main() we call our Initialize() method, then loop forever, checking for events, updating, and finally rendering the frame. The Initialize() method simply creates a new GraphicsContext variable. The Update() method polls the first gamepad for updates. Finally Render() uses our GraphicsContext variable to first clear the screen to black using an RGBA color value, then clears the screen and swaps the buffers, making it visible. Graphic operations in PSM SDK generally are drawn to a back buffer. There's more... The same process is used to create PlayStation Suite library projects, which will generate a DLL file. You can use almost any C# library that doesn't rely on native code (pInvoke or Unsafe); however, they need to be recompiled into a PSM compatible DLL format. Color in the PSM SDK is normally represented as an RGBA value. The RGBA acronym stands for red, green, blue, and alpha. Each is an int variable type, with values ranging from 0 to 255 representing the strength of each primary color. Alpha represents the level of transparency, with 0 being completely transparent and 256 being opaque.
Read more
  • 0
  • 0
  • 3584

article-image-so-what-forgedui
Packt
17 Apr 2013
2 min read
Save for later

So, what is ForgedUI?

Packt
17 Apr 2013
2 min read
(For more resources related to this topic, see here.) ForgedUI is a Titanium Studio third-party WYSIWYG (what you see is what you get) module created with the aim to make cross-platform app development quicker and easier, by providing a visual environment and a drag-and-drop style interface. Even though Titanium generates multiple platform apps with a single codebase and facilitates the maintenance and management of mobile projects, it still lacks a design interface tool, and for this reason, ForgedUI comes to the market with the aim to improve what has been holding back a large portion of productivity gains on Titanium SDK. With a steep learning curve and a straightforward interface, Titanium developers are able to reduce app development time by visually designing their apps and get more time to concentrate on other aspects of the project. It doesn't matter if you choose to go with iOS or Android, ForgedUI will give you a hand on screen design, and alongside Titanium Studio, generate the cross-platform code with one click. The ForgedUI interface has common UI elements of the Android and iOS platforms and allows you to design with a simple drag-and-drop process instead of hand written code. This is how ForgedUI looks within Titanium Studio: Once you are happy with your UI design, you can generate the Titanium JavaScript code through ForgedUI with just one click, and this code can be integrated into new or old projects. The generated code is stored in a separate resource file within the current Titanium project. ForgedUI allows you to specify parent-child relationships between compatible components and intelligently generates code that conforms to the required layout and relationship rules. The key features of ForgedUI are as follows: Visual graphical user interface (GUI) designer Supports iPhone and Android (no Tablets) platform projects One-click code generation Seamless integration with Titanium Studio Summary This article has thus explained what ForgedUI is and how it makes cross-platform app development easier. Resources for Article : Further resources on this subject: Animating Properties and Tweening Pages in Android 3-0 [Article] Securely Encrypt Removable Media with Ubuntu [Article] Appcelerator Titanium: Creating Animations, Transformations, and Understanding Drag-and-drop [Article]
Read more
  • 0
  • 0
  • 2394

article-image-titanium-best-practices
Packt
15 Mar 2013
13 min read
Save for later

Titanium Best Practices

Packt
15 Mar 2013
13 min read
(For more resources related to this topic, see here.) CommonJS CommonJS is a set of specifications, and its purpose is to give a common guide to building JavaScript frameworks. It is not a framework in its own right. Appcelerator implementing these standards to their Titanium framework brings it inline with other frameworks such as NodeJS, and enables us as developers to use the same practices in more than one framework. The implications of this cannot be overstated. Developers can now switch between JavaScript frameworks, which have implemented the CommonJS model, without having a massive learning curve on the framework itself. CommonJS works by using an initial bootstrap file, which in Titanium is app.js. You then abstract your code into separate modules that are required into other modules when needed. Within Titanium, you can have native modules that enhance and expand the Titanium framework. These should not be confused with a CommonJS module, which is part of the main application. Code formatting How often have you needed to modify code that was written by other people or has been updated, modified, rewritten, or just generally messed around with? You find different people using different coding styles, braces on different lines, and equals signs at different positions; the list is endless. You then have to work your way through this code, getting more and more annoyed. Titanium Studio has a solution—an automatic code formatter, which you can set up as you require. To get to the configuration settings, go through the main menu to Preferences | Titanium Studio | Formatter, select JavaScript , and click on the edit icon. You will see the Preferences panel and you can configure it as required. If you wish to have these configurations across your development team, then you can export and import them as required. To format a file's code, open the file, go to the main menu, and select Source | Format. Alternatively, if you are on a Mac, the relevant shortcut keys are shift +command +F. If you have selected a section of code then that will be formatted, otherwise the whole file will be formatted. A gotcha with the formatter: If there are too many JavaScript errors it won't format the code. Code validation As developers, we spend our days hunting down spurious code anomalies. It could be a missing comma, semicolon, or that an event listener wasn't added while looping an array of objects. Anything that can make this task easier and show potential issues while we are writing code is a good thing. JSLint is one of those tools that you could use, as it highlights potential issues as you code. It has been described as your worst nightmare but also your best friend. When you first start using it, it may drive you mad with some of the seemingly meaningless warnings, but sort them out and be persistent; it will improve your codebase in the long term. Titanium Studio has JSLint built in but switched off by default. To enable it, go to Preferences | Titanium Studio | Validation, select JavaScript , and switch on JSLint Validator. As you can see, there are other validators on the list, which also help give you a hit list of potential issues. It is worth spending a little time making sure you understand what these are and how they work. Comment meaningfully Adding comments to your code should not be seen as a chore, instead they can be as important as the code itself. A well-commented codebase can and will save hours in the future, as you or a colleague go to update and maintain it. Always put a comment block at the start of source files, explain what the file does, and include a maintenance log, where the date, time, developer's name, and a brief description of the changes can be maintained. If you have a complex function put a comment block before it, explaining what it does. Also place inline comments where needed to explain certain pieces of code. /* * A CommonJS Module. * * This module does something … * * Author : Name * * Date : Today * * Maintenance Log * * Date : Author: * Changes : * */ Do not add comments for the sake of adding them. Make them meaningful, relevant, and useful. Too many comments can confuse the code structure. Do not pollute the global object Within an application you can define various application objects. Declare and use these with caution, as they use up resources and can easily cause clashes with other private and local objects. Application-level variables are not really required and should be avoided. If you require a small piece of data to move around the application, consider using persistent data or passing it to the required modules. Application-level event handlers are required for various tasks including, amongst others, background services and geolocation. If you do use them, always remove them when they are no longer needed. To control the flow of an application, you may need to set up a global listener, but you only need one with a common function to control the flow. In CommonJS there is no global scope; declaring a variable within a module makes it private to that module. Declaring variables in app.js as Ti.App.varName = [], does make them global, but is highly discouraged. JavaScript instance A JavaScript instance is where a session of the interpreter is invoked. In Titanium it is possible to create multiple instances within a single application. This is done by creating a window object with a url property to load the content. Var win = Ti.UI.createwindow({ url: '/app/ui/newWindow.js' }); Don't do this unless you have a very, very specific requirement. To benefit from all the advantages CommonJS provides, always work in a single JavaScript instance. The consequences of multiple instances include no scope across them, additional memory and resource usage, and a high risk of memory leaks. CommonJS modules With the adoption of the CommonJS specification by Appcelerator into the Titanium framework, you should only use the CommonJS modules, which are also referred to as factories. This provides many advantages: separation of code into specific modules, a more structured codebase, separate object scope, code maintainability, and much more. By using this method it becomes very difficult to pollute the global scope, as each module has its own object scope. Understanding this is key to the CommonJS method. Each module or factory contains functions and variables that can be exported; unless they are exported they are private to that module. This enables variable or function names to be the same in different modules without a scoping clash. By exporting only what is required at the end of the module, it enables the module to be self contained. /* A typical module format myModule.js */ var object1 = "1234"; var object2 = "5678"; /* My modules function */ functionmyModule(args){ … do something nice return mainObject; } exports.object1 = object1; exports.myModule = myModule; When a CommonJS module is required by another module it is loaded into memory. If you then require the same module elsewhere, it doesn't reload it into memory; it just makes it available to the new calling module. This means t hat if the first requiring module sets a value in the called module, when the called module is required by another module, the values which have been set are still set. It doesn't load a new instance of the module and reinitialize all the values. A few rules about modules: Only load them when needed Only export what is required by the calling module Use prototype where appropriate Avoid recursive requires Working with CommonJS recursive requires can cause major issues and, basically, you can't do it. A recursive require is where module A requires module B and module B then requires module A. You will quickly notice if you try this that this leads to a loop trying to process the continual call of the requires, and finally dies with a nondescript error message. CommonJS best practices CommonJS is one of the best things to have happened to the Titanium framework. To get the most out of the framework and the enhanced performance, these are a few things that should be considered: Be modular Be private Return an object Protect the global scope Control file loading One of the main advantages of CommonJS is the way it lends itself to creating well-structured, separated code. By being modular you create specific modules for separate sections of the code, i.e. a separate module for each window. This methodology facilitates the creation of common modules enabling a constructive codebase that is easy to understand and maintain. A good example of a common module would be one that contains the geolocation code and is then used across the whole application. Common modules enable the code to be extracted down, but don't go too far. It is tempting to extract code out into its own module when it actually lives in the module it is in. Do not be tempted to take code modularization to the extreme, having a module for each function is not necessary or productive. Remember that modules are loaded into memory once they have been required and they remain there. Making functions and variables private to a module maintains the module's integrity. Do not export all the functions in a module unless they are actually called from the requiring module. Export the required functions and variables at the end of the module, not by default. The following code example shows the two methods for exporting functions: // This exports the function inline exports.outFunc = function () { .. code .. return object; } // This only exports the function when and if required. function outFunc() { .. code .. return object; } exports.outFunc = outFunc; By defining the module functions in the second method, they become local to that module. This means that they can be used directly by any other function within the module. As you separate your code into modules, separate your modules into functions. Having one exported parent function that returns the main object after processing through other functions is a good practice. It is quite easy to declare module variables; you just declare them outside of any function. This gives them a scope that is global to the module. It is a very good way of maintaining a persistent state across the calling modules, but use them sparingly as they use up more resources. JavaScript, by default, returns the last action of a function to the calling function. This may not always be what is required, especially in the CommonJS model. Always return what you require from a function even if that is nothing. // New window function returning the null object. functionnewWin() { // Do something return; } exports.newWin = newWin; For an exported function always return the function's main object. Hence, creating a new window in a function should return that window object as the following code example shows: // New window function returning the parent object. function newWin() { var win = Ti.UI.createWindow({ .. parameters .. }); return win; } exports.newWin = newWin; The global scope should be considered as nonexistent, but you can still add application-level variables. If you have to use these, only declare them in the app.js file; do not declare them in any of the modules. At times you will have to create an application event listener in a module. Only do this when you have to and always, always remove the listener after you have finished with it. The following code example shows an event listener added with an in-built function. This is not good practice, as you cannot remove it later. The only way it will be removed is when the parent object is destroyed, which for application-level listeners is when the application is stopped, not put into background. function adList() { varmainObj = Ti.UI.createImageView(); mainObj.addEventListener('click', function(e) { .. do something here .. }); return mainObj; } Instead of defining listeners with an in-built function, always declare them to a calling function as the following example shows. They can then easily be removed, as you have to declare the called function to be identical in both addEventListener and removeEventListener. varmainObj = null; function eveList(e){ .. do something here … mainObj.removeEventListener('click', eveList); } function adList() { mainObj = Ti.UI.createImageView(); mainObj.addEventListener('click', eveList); return mainObj; } Managing memory We have explored some of the practices that will help in managing the application's memory. These practices range from controlling when the modules file is loaded to not using application-level events or variables. When you open a window, it is added to the window stack, and every time you open that window it is added to the stack. If you have a navigation system that enables you to move through the windows in any order, it is likely that you will end up with a large stack. Close the windows when they are not in use. varwin =i.UI.createWindow(); varwinBut =i.UI.createButton({ title : 'Press' }); function loadWin2() { varwin = Ti.UI.createWindow(); win.close(); win = null; win2.open(); } winBut.addEventListener('click', loadWin2); // opening a window win.add(winBut); win.open(); Some additional memory-intensive APIs in Titanium are web views, large tables, and events. It is not a good idea to have more than one web view instance running in the application at a time. If you do not close the window containing the web view instance and move to another one, at least remove the web view instance from the previous window and reload it when focus is made. The same applies to tables and map views. When you remove an object such as a web view from a window it may not always release the memory—even closing a window does not always release the memory. In all cases, null is your friend. The final code example shows a window with web view, an event listener, and how to clean up when a window is closed: // clean up example module function createWin() { var win = Ti.UI.createWindow(); varwebV = Ti.UI.createWebView(); win.addEventListener('open', openFunc); win.addEventListener('close', function(e) { win.removeEventListener('open'openFunc); win.remove(webV); webV = null; win = null; } win.add(webV); return win; } In this example the close event doesn't call another function. This is acceptable because when we close the window, the close event will fire, which nulls the win object and this removes the object's event listener. It is done this way to prevent having to use module variables to handle the window and web view objects being cleaned up in another function. Summary As you have seen, there are quite a few considerations to take into account when coding with Titanium. Some of these can also be applied to other programming languages. It is completely optional to follow best practices; they are there as a guide, as a place to start, and as a way to manage your code going forward. As a developer you will find your own way to implement the methodology used within the application, you will decide when, where, and what comments to add, which code format to use, and which module to put what code into. But best practices and guidelines are developed for a reason; they keep code consistent within the application, they allow other developers to pick up what is going on quickly, and enable clean and reliable code. Always apply a good coding style to your application. You will thank yourself in the future. Resources for Article : Further resources on this subject: Animating Properties and Tweening Pages in Android 3-0 [Article] Creating, Compiling, and Deploying Native Projects from the Android NDK [Article] Appcelerator Titanium: Creating Animations, Transformations, and Understanding Drag-and-drop [Article]
Read more
  • 0
  • 0
  • 2696

article-image-implementing-data-model
Packt
06 Mar 2013
13 min read
Save for later

Implementing the data model

Packt
06 Mar 2013
13 min read
(For more resources related to this topic, see here.) Getting on with it Before we define our model, let's define a namespace where it will live. This is an important habit to establish since it relieves us of having to worry about whether or not we'll collide with another function, object, or variable of the same name. While there are various methods used to create a namespace, we're going to do it simply using the following code snippet: // quizQuestion.js var QQ = QQ || {}; Now that our namespace is defined, we can create our question object as follows: QQ.Question = function ( theQuestion ) { var self = this; Note the use of self: this will allow us to refer to the object using self rather than using this. (Javascript's this is a bit nuts, so it's always better to refer to a variable that we know will always refer to the object.) Next, we'll set up the properties based on the diagram we created from step two using the following code snippet: self.question = theQuestion; self.answers = Array(); self.correctAnswer = -1; We've set the self.correctAnswer value to -1 to indicate that, at the moment, any answer provided by the player is considered correct. This means you can ask questions where all of the answers are right. Our next step is to define the methods or interactions the object will have. Let's start with determining if an answer is correct. In the following code, we will take an incoming answer and compare it to the self.correctAnswer value. If it matches, or if the self.correctAnswer value is -1, we'll indicate that the answer is correct: self.testAnswer = function( theAnswerGiven ) { if ((theAnswerGiven == self.correctAnswer) || (self.correctAnswer == -1)) { return true; } else { return false; } } We're going to need a way to access a specific answer, so we'll define the answerAtIndex function as follows: self.answerAtIndex = function ( theIndex ) { return self.answers[ theIndex ]; } To be a well-defined model, we should always have a way of determining the number of items in the model as shown in the following code snippet: self.answerCount = function () { return self.answers.length; } Next, we need to define a method that allows an answer to be added to our object. Note that with the help of the return value, we return ourselves to permitting daisy-chaining in our code: self.addAnswer = function( theAnswer ) { self.answers.push ( theAnswer ); return self; } In theory we could display the answers to a question in the order they were given to the object. In practice, that would turn out to be a pretty boring game: the answers would always be in the same order, and chances would be pretty good that the first answer would be the correct answer. So let's give ourselves a randomized list using the following code snippet: self.getRandomizedAnswers = function () { var randomizedArray = Array(); var theRandomNumber; var theNumberExists; // go through each item in the answers array for (var i=0; i<self.answers.length; i++) { // always do this at least once do { // generate a random number less than the // count of answers theRandomNumber = Math.floor ( Math.random() * self.answers.length ); theNumberExists = false; // check to see if it is already in the array for (var j=0; j<randomizedArray.length; j++) { if (randomizedArray[j] == theRandomNumber) { theNumberExists = true; } } // If it exists, we repeat the loop. } while ( theNumberExists ); // We have a random number that is unique in the // array; add it to it. randomizedArray.push ( theRandomNumber ); } return randomizedArray; } The randomized list is just an array of numbers that indexes into the answers[] array. To get the actual answer, we'll have to use the answerAtIndex() method. Our model still needs a way to set the correct answer. Again, notice the return value in the following code snippet permitting us to daisy-chain later on: self.setCorrectAnswer = function ( theIndex ) { self.correctAnswer = theIndex; return self; } Now that we've properly set the correct answer, what if we need to ask the object what the correct answer is? For this let's define a getCorrectAnswer function using the following code snippet: self.getCorrectAnswer = function () { return self.correctAnswer; } Of course, our object also needs to return the question given to it whenever it was created; this can be done using the following code snippet: self.getQuestion = function() { return self.question; } } That's it for the question object. Next we'll create the container that will hold all of our questions using the following code line: QQ.questions = Array(); We could go the regular object-oriented approach and make the container an object as well, but in this game we have only one list of questions, so it's easier to do it this way. Next, we need to have the ability to add a question to the container, this can be done using the following code snippet: QQ.addQuestion = function (theQuestion) { QQ.questions.push ( theQuestion ); } Like any good data model, we need to know how many questions we have; we can know this using the following code snippet: QQ.count = function () { return QQ.questions.length; } Finally, we need to be able to get a random question out of the list so that we can show it to the player; this can be done using the following code snippet: QQ.getRandomQuestion = function () { var theQuestion = Math.floor (Math.random() * QQ.count()); return QQ.questions[theQuestion]; } Our data model is officially complete. Let's define some questions using the following code snippet: // quizQuestions.js // // QUESTION 1 // QQ.addQuestion ( new QQ.Question ( "WHAT_IS_THE_COLOR_OF_THE_SUN?" ) .addAnswer( "YELLOW" ) .addAnswer( "WHITE" ) .addAnswer( "GREEN" ) .setCorrectAnswer ( 0 ) ); Notice how we attach the addAnswer and setCorrectAnswer methods to the new question object. This is what is meant by daisy-chaining: it helps us write just a little bit less code. You may be wondering why we're using upper-case text for the questions and answers. This is due to how we'll localize the text, which is next: PKLOC.addTranslation ( "en", "WHAT_IS_THE_COLOR_OF_THE_SUN?", "What is the color of the Sun?" ); PKLOC.addTranslation ( "en", "YELLOW", "Yellow" ); PKLOC.addTranslation ( "en", "WHITE", "White" ); PKLOC.addTranslation ( "en", "GREEN", "Green" ); PKLOC.addTranslation ( "es", "WHAT_IS_THE_COLOR_OF_THE_SUN?", "¿Cuál es el color del Sol?" ); PKLOC.addTranslation ( "es", "YELLOW", "Amarillo" ); PKLOC.addTranslation ( "es", "WHITE", "Blanco" ); PKLOC.addTranslation ( "es", "GREEN", "Verde" ); The questions and answers themselves serve as keys to the actual translation. This serves two purposes: it makes the keys obvious in our code, so we know that the text will be replaced later on, and should we forget to include a translation for one of the keys, it'll show up in uppercase letters. PKLOC as used in the earlier code snippet is the namespace we're using for our localization library. It's defined in www/framework/localization.js. The addTranslation method is a method that adds a translation to a specific locale. The first parameter is the locale for which we're defining the translation, the second parameter is the key, and the third parameter is the translated text. The PKLOC.addTranslation function looks like the following code snippet: PKLOC.addTranslation = function (locale, key, value) { if (PKLOC.localizedText[locale]) { PKLOC.localizedText[locale][key] = value; } else { PKLOC.localizedText[locale] = {}; PKLOC.localizedText[locale][key] = value; } } The addTranslation method first checks to see if an array is defined under the PKLOC.localizedText array for the desired locale. If it is there, it just adds the key/value pair. If it isn't, it creates the array first and then adds the key/value pair. You may be wondering how the PKLOC.localizedText array gets defined in the first place. The answer is that it is defined when the script is loaded, a little higher in the file: PKLOC.localizedText = {}; Continue adding questions in this fashion until you've created all the questions you want. The quizQuestions.js file contains ten questions. You could, of course, add as many as you want. What did we do? In this task, we created our data model and created some data for the model. We also showed how translations are added to each locale. What else do I need to know? Before we move on to the next task, let's cover a little more of the localization library we'll be using. Our localization efforts are split into two parts: translation and data formatting . For the translation effort , we're using our own simple translation framework, literally just an array of keys and values based on locale. Whenever code asks for the translation for a key, we'll look it up in the array and return whatever translation we find, if any. But first, we need to determine the actual locale of the player, using the following code snippet: // www/framework/localization.js PKLOC.currentUserLocale = ""; PKLOC.getUserLocale = function() { Determining the locale isn't hard, but neither is it as easy as you would initially think. There is a property (navigator.language) under WebKit browsers that is technically supposed to return the locale, but it has a bug under Android, so we have to use the userAgent. For WP7, we have to use one of three properties to determine the value. Because that takes some work, we'll check to see if we've defined it before; if we have, we'll return that value instead: if (PKLOC.currentUserLocale) { return PKLOC.currentUserLocale; } Next, we determine the current device we're on by using the device object provided by Cordova. We'll check for it first, and if it doesn't exist, we'll assume we can access it using one of the four properties attached to the navigator object using the following code snippet: var currentPlatform = "unknown"; if (typeof device != 'undefined') { currentPlatform = device.platform; } We'll also provide a suitable default locale if we can't determine the user's locale at all as seen in the following code snippet: var userLocale = "en-US"; Next, we handle parsing the user agent if we're on an Android platform. The following code is heavily inspired by an answer given online at http://stackoverflow.com/a/7728507/741043. if (currentPlatform == "Android") { var userAgent = navigator.userAgent; var tempLocale = userAgent.match(/Android.*([a-zA-Z]{2}-[a-zA-Z] {2})/); if (tempLocale) { userLocale = tempLocale[1]; } } If we're on any other platform, we'll use the navigator object to retrieve the locale as follows: else { userLocale = navigator.language || navigator.browserLanguage || navigator.systemLanguage || navigator.userLanguage; } Once we have the locale, we return it as follows: PKLOC.currentUserLocale = userLocale; return PKLOC.currentUserLocale; } This method is called over and over by all of our translation codes, which means it needs to be efficient. This is why we've defined the PKLOC.currentUserLocale property. Once it is set, the preceding code won't try to calculate it out again. This also introduces another benefit: we can easily test our translation code by overwriting this property. While it is always important to test that the code properly localizes when the device is set to a specific language and region, it often takes considerable time to switch between these settings. Having the ability to set the specific locale helps us save time in the initial testing by bypassing the time it takes to switch device settings. It also permits us to focus on a specific locale, especially when testing. Translation of text is accomplished by a convenience function named __T() . The convenience functions are going to be our only functions outside of any specific namespace simply because we are aiming for easy-to-type and easy-to-remember names that aren't arduous to add to our code. This is especially important since they'll wrap every string, number, date, or percentage in our code. The __T() function depends on two functions: substituteVariables and lookupTranslation. The first function is de fined as follows: PKLOC.substituteVariables = function ( theString, theParms ) { var currentValue = theString; // handle replacement variables if (theParms) { for (var i=1; i<=theParms.length; i++) { currentValue = currentValue.replace("%" + i, theParms[i-1]); } } return currentValue; } All this function does is handle the substitution variables. This means we can define a translation with %1 in the text and we will be able to replace %1 with some value passed into the function. The next function, lookupTranslation, is defined as follows: PKLOC.lookupTranslation = function ( key, theLocale ) { var userLocale = theLocale || PKLOC.getUserLocale(); if ( PKLOC.localizedText[userLocale] ) { if ( PKLOC.localizedText[userLocale][key.toUpperCase()] ) { return PKLOC.localizedText[userLocale][key.toUpperCase()]; } } return null; } Essentially, we're checking to see if a specific translation exists for the given key and locale. If it does, we'll return the translation, but if it doesn't, we'll return null. Note that the key is always converted to uppercase, so case doesn't matter when looking up a translation. Our __T() function looks as follows: function __T(key, parms, locale) { var userLocale = locale || PKLOC.getUserLocale(); var currentValue = ""; First, we determine if the translation requested can be found in the locale, whatever that may be. Note that it can be passed in, therefore overriding the current locale. This can be done using the following code snippet: if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { Locales are often of the form xx-YY, where xx is a two-character language code and YY is a two-character character code. My locale is defined as en-US. Another player's might be defined as es-ES. If you recall, we defined our translations only for the language. This presents a problem: the preceding code will not return any translation unless we defined the translation for the language and the country. Sometimes it is critical to define a translation specific to a language and a country. While various regions may speak the same language from a technical perspective, idioms often differ. If you use an idiom in your translation, you'll need to localize them to the specific region that uses them, or you could generate potential confusion. Therefore, we chop off the country code, and try again as follows: userLocale = userLocale.substr(0,2); if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { But we've only defined translations for English (en) and Spanish(es)! What if the player's locale is fr-FR (French)? The preceding code will fail, because we've not defined any translation for the fr language (French). Therefore, we'll check for a suitable default, which we've defined to be en-US, American English: userLocale = "en-US"; if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { Of course, we are now in the same boat as before: there are no translations defined for en-US in our game. So we need to fall back to en as follows: userLocale = "en"; if (! (currentValue=PKLOC.lookupTranslation(key, userLocale)) ) { But what happens if we can't find a translation at all? We could be mean and throw a nasty error, and perhaps you might want to do exactly that, but in our example, we're just returning the incoming key. If the convention of capitalizing the key is always followed, we'll still be able to see that something hasn't been translated. currentValue = key; } } } } Finally, we pass the currentValue parameter to the substituteVariables property in order to process any substitutions that we might need as follows: return PKLOC.substituteVariables( currentValue, parms ); } Summary In this article we saw the file quizQuestion.js which was the actual model: it specified how the data should be formatted and how we can interact with it. We also saw the quizQuestions.js file, which contained our actual question data. Resources for Article : Further resources on this subject: Configuring the ChildBrowser plugin [Article] Adding Geographic Capabilities via the GeoPlaces Theme [Article] iPhone: Issues Related to Calls, SMS, and Contacts [Article]
Read more
  • 0
  • 0
  • 1935

article-image-hooking-native-events
Packt
04 Jan 2013
9 min read
Save for later

Hooking into native events

Packt
04 Jan 2013
9 min read
(For more resources related to this topic, see here.) Pausing your application Although we want our users to spend their time solely on our applications, they will inevitably leave our application to open another one or do something else entirely. We need to be able to detect when a user has left our application but not closed it down entirely. How to do it... We can use the PhoneGap API to fire off a particular event when our application is put into the background on the device: Create the initial HTML layout for the application, and include the reference to the Cordova JavaScript file in the head tag of the document. <!DOCTYPE HTML> <html> <head> <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" /> <meta http-equiv="Content-type" content="text/html;> <title>Pausing an application</title> <script type="text/javascript" src="cordova-2.0.0.js"></script> </head> <body> </body> </html> Before the closing head tag, create a new script tag block and add the event listener to check when the device is ready and the PhoneGap code is ready to run. <script type="text/javascript"> document.addEventListener("deviceready", onDeviceReady, false); </script> Create the onDeviceReady function, which will run when the event listener is fired. Inside this, we'll create a new event listener that will check for a pause event, and once received will fire the onPause method. function onDeviceReady() { document.addEventListener("pause", onPause, false); } Let's create the onPause method. In this example application, we'll ask the device to notify the user that the application has moved into the background by playing an audio beep. The numeric parameter specifies how many times we want the audio notification to be played — in this case, just once. function onPause() { navigator.notification.beep(1); } Developing for iOS? There is no native beep API for iOS. The PhoneGap API will play an audio file using the media API, but the developer must provide the file, named beep.wav and under 30 seconds in length, in the /www directory of the application project files. iOS will also ignore the beep count argument and will play the audio once. If developing for Windows 7 mobile, the WP7 Cordova library contains a generic beep audio file that will be used. When we run the application on the device, if you press the home button or navigate to another application, the device will play the notification audio. How it works... To correctly determine the flow of our lifecycle events, we first set up the deviceready event listener to ensure that the native code was properly loaded. At this point, we were then able to set the new event listener for the pause event. As soon as the user navigated away from our application, the native code would set it into the background processes on the device and fire the pause event, at which point our listener would run the onPause method. To find out more about the pause event, please refer to the official documentation, available here: http://docs.phonegap.com/en/2.0.0/cordova_events_events.md.html#pause. There's more... In this recipe we applied the pause event in an incredibly simple manner. There is a possibility your application will want to do something specific other than sending an audio notification when the user pauses your application. For example, you may want to save and persist any data currently in the view or in memory, such as any draft work (if dealing with form inputs) or saving responses from a remote API call. We'll build an example that will persist data in the next recipe, as we'll be able to quantify its success when we resume the use of the application and bring it back into the foreground. Resuming your application Multi-tasking capabilities that are now available on mobile devices specify that the user has the ability to switch from one application to another at any time. We need to handle this possibility and ensure that we can save and restore any processes and data when the user returns to our application. How to do it... We can use the PhoneGap API to detect when our application is brought back into the foreground on the device. The following steps will help us to do so: Create the initial layout for the HTML and include the JavaScript references to the Cordova and the xui.js files. We will also be setting the deviceready listener once the DOM has fully loaded, so let's apply an onload attribute to the body tag. <!DOCTYPE HTML> <html> <head> <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>Resuming an application</title> <script type="text/javascript" src="cordova-2.0.0.js"></script> <script type="text/javascript" src="xui.js"></script> </head> <body onload="onLoad()"> </body> </html> Create a new script tag block before the closing head tag and add the deviceready event listener within the onLoad method. We'll also set two global variables, savedTime, and localStorage, the latter of which will reference the localStorage API on the device: <script type="text/javascript"> var savedTime; var localStorage = window.localStorage; function onLoad() { document.addEventListener("deviceready", onDeviceReady, false); } </script> Create the onDeviceReady function, within which we'll set the two event listeners to check for the pause and resume events, as follows: function onDeviceReady() { document.addEventListener("pause", onPause, false); document.addEventListener("resume", onResume, false); } We can now add the first of the new callback functions for the added listeners. onPause will run when a pause event has been detected. In this method, we'll create a new date variable holding the current time, and store it into the global savedTime variable we created earlier. If the user has entered something in to the text input field, we'll also take the value and set it into the localStorage API, before clearing out the input field. function onPause() { savedTime = new Date(); var strInput = x$('#userInput').attr('value'); if(strInput) { localStorage.setItem('saved_input', strInput); x$('#userInput').attr('value', ''); } } Define the onResume method, which will run when a resume event has been detected. In this function, we'll save a new date variable and we'll use it in conjunction with the savedTime variable created in the onPause method to generate the time difference between the two dates. We'll then create a string message to display the time details to the user. We'll then check the localStorage for the existence of an item stored using the key saved_input. If this exists, we'll extend the message string and append the saved user input value before setting the message into the DOM to display. function onResume() { var currentTime = new Date(); var dateDiff = currentTime.getTime() - savedTime.getTime(); var objDiff = new Object(); objDiff.days = Math.floor(dateDiff/1000/60/60/24); dateDiff -= objDiff.days*1000*60*60*24; objDiff.hours = Math.floor(dateDiff/1000/60/60); dateDiff -= objDiff.hours*1000*60*60; objDiff.minutes = Math.floor(dateDiff/1000/60); dateDiff -= objDiff.minutes*1000*60; objDiff.seconds = Math.floor(dateDiff/1000); var strMessage = '<h2>You are back!</h2>' strMessage += '<p>You left me in the background for ' strMessage += '<b>' + objDiff.days + '</b> days, ' strMessage += '<b>' + objDiff.hours + '</b> hours, ' strMessage += '<b>' + objDiff.minutes + '</b> minutes, ' strMessage += '<b>' + objDiff.seconds + '</b> seconds.</p>'; if(localStorage.getItem('saved_input')) { strMessage = strMessage + '<p>You had typed the following before you left:<br /><br />' strMessage += '"<b>' + localStorage.getItem('saved_input') + '</b>"</p>'; } x$('#message').html(strMessage); } Finally, let's add the DOM elements to the application. Create a new div element with the id attribute set to message, and an input text element with the id set to userInput. <body onload="onLoad()"> <div id="message"></div> <input type="text" id="userInput" /> </body> When we run the application on the device, the initial output would provide the user with an input box to enter text, should they wish to, as shown in the following screenshot: If we were to pause the application and then resume it after a period of time, the display would then update to look something like the following screenshot: How it works... We set up the deviceready event listener after the DOM was fully loaded, which would then run the onDeviceReady function. Within this method we then added two new event listeners to catch the pause and resume events respectively. When the application is paused and placed into the background processes on the device, we saved the current date and time into a global variable. We also checked for the existence of any user-supplied input and if it was present we saved it using the localStorage capabilities on the device. When the application was resumed and placed back into the foreground on the device, the onResume method was run, which obtained the time difference between the saved and current datetime values to output to the user. We also retrieved the saved user input from the localStorage if we had set it within the onPause method. To find out more about the resume event, please refer to the official documentation, available here: http://docs.phonegap.com/en/2.0.0/cordova_events_events.md.html#resume.
Read more
  • 0
  • 0
  • 2628

article-image-page-events
Packt
02 Jan 2013
4 min read
Save for later

Page Events

Packt
02 Jan 2013
4 min read
(For more resources related to this topic, see here.) Page initialization events The jQuery Mobile framework provides the page plugin which automatically handles page initialization events. The pagebeforecreate event is fired before the page is created. The pagecreate event is fired after the page is created but before the widgets are initialized. The pageinit event is fired after the complete initialization. This recipe shows you how to use these events. Getting ready Copy the full code of this recipe from the code/08/pageinit sources folder. You can launch this code using the URL http://localhost:8080/08/pageinit/main.html How to do it... Carry out the following steps: Create main.html with three empty <div> tags as follows: <div id="content" data-role="content"> <div id="div1"></div> <div id="div2"></div> <div id="div3"></div> </div> Add the following script to the <head> section to handle the pagebeforecreate event : var str = "<a href='#' data-role='button'>Link</a>"; $("#main").live("pagebeforecreate", function(event) { $("#div1").html("<p>DIV1 :</p>"+str); }); Next, handle the pagecreate event : $("#main").live("pagecreate", function(event) { $("#div1").find("a").attr("data-icon", "star"); }); Finally, handle the pageinit event : $("#main").live("pageinit", function(event) { $("#div2").html("<p>DIV 2 :</p>"+str); $("#div3").html("<p>DIV 3 :</p>"+str); $("#div3").find("a").buttonMarkup({"icon": "star"}); }); How it works... In main.html, add three empty divs to the page content as shown. Add the given script to the page. In the script, str is an HTML string for creating an anchor link with the data-role="button" attribute. Add the callback for the pagebeforecreate event , and set str to the div1 container. Since the page was not yet created, the button in div1 is automatically initialized and enhanced as seen in the following image. Add the callback for the pagecreate event . Select the previous anchor button in div1 using the jQuery find() method, and set its data-icon attribute. Since this change was made after page initialization but before the button was initialized, the star icon is automatically shown for the div1 button as shown in the following screenshot. Finally, add the callback for the pageinit event and add str to both the div2 and div3 containers. At this point, the page and widgets are already initialized and enhanced. Adding an anchor link will now show it only as a native link without any enhancement for div2, as shown in the following screenshot. But, for div3, find the anchor link and manually call the buttonmarkup method on the button plugin, and set its icon to star. Now when you load the page, the link in div3 gets enhanced as follows:     There's more... You can trigger "create" or "refresh" on the plugins to let the jQuery Mobile framework enhance the dynamic changes done to the page or the widgets after initialization. Page initialization events fire only once The page initialization events fire only once. So this is a good place to make any specific initializations or to add your custom controls. Do not use $(document).ready() The $(document).ready() handler only works when the first page is loaded or when the DOM is ready for the first time. If you load a page via Ajax, then the ready() function is not triggered. Whereas, the pageinit event is triggered whenever a page is created or loaded and initialized. So, this is the best place to do post initialization activities in your app. $(document).bind("pageinit", callback() {...});</p>  
Read more
  • 0
  • 0
  • 8153
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-getting-started-livecode-mobile
Packt
27 Jul 2012
8 min read
Save for later

Getting Started with LiveCode for Mobile

Packt
27 Jul 2012
8 min read
  (For more resources on Mobile Development, see here.) iOS, Android, or both? It could be that you only have an interest in iOS or only in Android. You should be able to easily see where to skip ahead to, unless you're intrigued about how the other half lives! If, like me, you're a capitalist, then you should be interested in both OSes. Far fewer steps are needed to get the Android SDK than to get the iOS developer tools, because of having to sign up as a developer with Apple, but the configuraton for Android is more involved. We'll go through all the steps for Android and then the ones for iOS. If you're an iOS-only kind of person, skip the next few sections, picking up again at the Becoming an iOS Developer section. Becoming an Android developer It is possible to develop Android OS apps without having to sign up for anything, but we'll try to be optimistic and assume that within the next 12 months, you will find time to make an awesome app that will make you rich! To that end, we'll go over what is involved in signing up to publish your apps in both the Android Market and the Amazon Appstore. Android Market A good starting location would be http://developer.android.com/. You will be back here shortly to download the Android SDK, but for now, click on the Learn More link in the Publish area. There will be a sign-in screen; sign in using your usual Google details. Which e-mail address to use? Some Google services are easier to sign up for, if you have a Gmail account. Creating a Google+ account, or signing up for some of their Cloud services, requires a Gmail address (or so it seemed to me at the time!). If you have previously set up Google Checkout as part of your account, some of the steps in signing up process become simpler. So, use your Gmail address, and if you don't have one, create one! Google charges a $25 fee for you to sign up for Android Market. At least you find out about this right away! Enter the values for Developer Name, Email Address, Website URL (if you have one), and Phone Number. The payment of the $25 can be done through Google Checkout. Using Google Checkout saves you from having to enter in your billing details, each time. Hopefully you won't guess the other 12 digits of my credit card number! Finally, you need to agree to the Android Market Developer Distribution Agreement. You're given an excuse to go and make some coffee… Some time later, you're all signed up and ready to make your fortune!   Amazon Appstore Whereas the rules and costs for the Google Android Market are fairly relaxed, Amazon has taken a more Apple-like approach, both in the amount they charge you to register and in the review process for accepting app submissions. The starting page is http://developer.amazon.com/home.html.   When you click on Get Started, you will be asked to sign into your Amazon account. Which e-mail address to use? This feels like déjà vu! There is no real advantage in using your Google e-mail address when signing up for the Amazon Appstore Developer Program, but if you happen to have an account with Amazon, sign in with that one. It will simplify the payment stage, and your developer account and general Amazon account will be associated with each other. You are asked to agree to the APPSTORE DISTRIBUTION AGREEMENT terms before learning about the costs. Those costs are $99 per year, but the first year is free. So that's good! Unlike the Google Android Market, Amazon asks for your bank details upfront, ready to send you lots of money later, we hope! That's it; you're ready to make another fortune, to go along with the one that Google sends you!   Downloading the Android SDK Head back over to http://developer.android.com/, and click on the Download link, or go straight to http://developer.android.com/sdk/index.html. In the book previously mentioned, we're only going to cover Windows and Mac OS X (Intel), and only as much as is needed to make LiveCode work with the Android and iOS SDKs. If you intend to do native Java-based applicatons, then you may be interested in reading through all of the steps that are described in the web page: http://developer.android.com/sdk/installing.html Click on the Download link for your platform. The steps you'll have to go through are different for Mac and Windows. Let's start with Mac. Installing Android SDK on Mac OS X (Intel) LiveCode itself doesn't require an Intel Mac; you can develop stacks using a PowerPC-based Mac, but both the Android SDK and some of the iOS tools require an Intel-based Mac, which sadly means that if you're reading this as you sit next to your Mac G4 or G5, then you're not going to get too far! The file that you just downloaded will automatically expand to show a folder named android-sdk-macosx. It may be in your downloads folder right now; a more natural place for it would be in your Documents folder, so move it there before performing the next steps. There is an SDK Readme text file that lists the steps you will need to take. If those steps are different to what we have here, then follow the steps in the Readme, in case they have been updated since the steps shown here were written. Open the application Terminal, which is in Applications/Utilities. You need to change the directories to be located in the android-sdk-macosx folder. One handy trick in Terminal is that you can drag the items into the Terminal window to get the file path to that item. Using that trick, you can type cd and a space into the Terminal window, then drag the android-sdk-macosx folder just afer the space character. You'll end up with this line: new-host-3:~ colin$ cd /Users/colin/Documents/android-sdk-macosx Of course, the first part of the line and the user folder will match yours, not mine! The rest will look the same. Here's how it would look for a user named fred: new-host-3:~ fred$ cd /Users/fred/Documents/android-sdk-macosx Whatever your name is, press the Return or Enter key after entering that line. The location line changes to look similiar to the following: new-host-3:android-sdk-macosx colin$ Either type carefully or copy and paste this line from the read me file: tools/android update sdk --no-ui Press Return or Enter again. How long the downloads take will depend on your Internet connection. Even with a very fast Internet connection, it will still take over an hour.   Installing Android SDK on Windows The downloads page recommends using the exe download link, and that will do extra things, such as check whether you have the Java Development Kit (JDK) installed. When you click on the link, use either the Run or Save options as you would with any download of a Windows installer. Here we opted to use Run. If you do use Save, then you will need to open the file after it has saved to your hard drive. In the following case, as the JDK wasn't installed, a dialog box appears saying go to Oracle's site to get the JDK: If you see this, then you can leave the dialog box open and click on the Visit java.oracle.com button. On the Oracle page, you have to click on a checkbox to agree to their terms, and then on the Download link that corresponds with your platform. Choose the 64-bit option if you are running a 64-bit version of Windows, or the x86 option if you are running 32-bit Windows. Either way, you're greeted with another installer to Run or Save, as you prefer. Naturally, it takes a while for that installer to do its thing too! When the installation completes, you will see a JDK registration page; it's up to you if you register or not. Back at the Android SDK installer dialog box, you can click on the Back button, and then the Next button to get back to that JDK checking stage; only now it sees that you have the JDK installed. Complete the remaining steps of the SDK installer, as you would with any Windows installer. One important thing to notice; the last screen of the installer offers to open the SDK Manager. You do want to do that, so resist the temptation to uncheck that box! Click on Finish, and you'll be greeted with a command-line window for a few moments, then the Android SDK Manager will appear and do its thing. As with the Mac version, it takes a very long time for all these add-ons to download. Pointing LiveCode to the Android SDK After all that installation and command-line work, it's a refreshing change to get back into LiveCode! Open the LiveCode Preferences, and choose Mobile Support. We will set the two iOS entries after getting iOS going (on Mac that is, these options will be grayed out on Windows). For now, click on the … button next to the Android development SDK root field, and navigate to where the SDK has been installed. If you followed the earlier steps correctly, then it will be in the Documents folder on Mac, or in C:Program Files (x86)Android on Windows (or somewhere else if you chose to use a custom location). Phew! Now, let's do iOS…
Read more
  • 0
  • 0
  • 2179

article-image-tabula-rasa-nurturing-your-site-tablets
Packt
09 Mar 2012
16 min read
Save for later

Tabula Rasa: Nurturing your Site for Tablets

Packt
09 Mar 2012
16 min read
The human touch There's a reason touchscreen interfaces were rarely used before Apple re-invented them in the iPhone. It's because programming them is very difficult. With a mouse-driven interface you have a single point of contact: the mouse's pointer. With a touchscreen, you potentially have ten points of contact, each one with a separate motion. And you also have to deal with limiting spurious input when the user accidentally touches the tablet when they didn't mean to. Does the user's swipe downward mean they want to scroll the page or to drag a single page element? The questions go on to infinity. With this article, we stand on the shoulders of those giants who have done the heavy lifting and given us a JavaScript interface that registers touch and gestures for use in our web pages. Many Bothans died to bring us this information. To understand the tablet is to understand the touch interface, and in order to understand the touch interface, we need to learn how touch events differ from mouse events. But that begs the question: what is an event? The event-driven model Many developers use JavaScript-based events and have not even the slightest clue as to what they can do or their power. In addition, many developers get into situations where they don't know why their events are misfiring or, worse yet, bubbling to other event handlers and causing a cascade of event activity. As you may or may not know, an HTML document is made up of a series of tags organized in a hierarchical structure called the HTML document. In JavaScript, this document is referred to through the reserved word document. Simple enough, right? Well, what if I want to interact with the tag inside of a document, and not the document as a whole? Well, for that we need a way of addressing nested items inside the main <html> tag. For that, we use the Document Object Model (DOM). DOM is a cross-platform and language-independent convention for representing and interacting with objects in HTML, XHTML, and XML documents. Aspects of the DOM (such as its elements) may be addressed and manipulated within the syntax of the programming language in use. The public interface of a DOM is specified in its Application Programming Interface (API). For more details on DOM, refer to the Wikipedia document at: http://en.wikipedia.org/wiki/Document_Object_Model. The body of that document then becomes document.body. The head of the document, likewise, becomes document.head. Now, what happens when your mouse interacts with this web page? This is said to be a DOM event. When you click, the elements that are the receivers of that action are said to propagate the event through the DOM. In the early days, Microsoft and Netscape/Firefox had competing ways of handling those events. But they finally gave way to the modern W3C's standard, which unifies the two ways and, even more importantly, jQuery has done a lot to standardize the way we think about events and event handling. In most browsers today, mouse events are pretty standardized, as we are now more than 20 years into the mouse-enabled computing era: For tablets and touchscreen phones, obviously, there is no mouse. There are only your fingers to serve the purpose of the mouse. And here's where things get simultaneously complicated as well as simple. Touch and go Much of what we talk about as touch interaction is made up of two distinct types of touches—single touches and gestures. A single touch is exactly that. One finger placed on the screen from the start till the end. A gesture is defined as one or more fingers touching the surface of the area and accompanied by a specific motion: Touch + Motion. To open most tablets, you swipe your finger across a specific area. To scroll inside a div element, you use two fingers pushing up and down. In fact, scrolling itself is a gesture and tablets only respond to the scroll event once it's over. We will cover more on that later. Gestures have redefined user interaction. I wonder how long it took for someone to figure out that the zoom in and zoom out is best accomplished with a pinch of the fingers? It seems so obvious once you do it and it immediately becomes second nature. My mom was pinching to zoom on her iPhone within the first 5 minutes of owning it. Touch events are very similar to multiple mouse events without a hover state. There is no response from the device when a finger is over the device but has not pressed down. There is an effort on the part of many mobile OS makers to simulate the hover event by allowing the hover event to trigger with the first click, and the click event to trigger with the second click on the same object. I would advise against using it for any meaningful user interaction as it is inconsistently implemented, and many times the single click triggers the link as well as the hover-reveal in drop-down menus. Not using the hover event to guide users through navigation changes the way we interact with a web page. Much of the work we've done to guide users through our pages is based on the hover-response event model to clue users in on where links are. We have to get beyond that. Drop-down menus quickly become frustrating at the second and third levels, especially if the click and hover events were incorrectly implemented in the desktop browser. Forward and back buttons are rendered obsolete by a forward and backwards swipe gesture. The main event There are basically three touch events—touchstart, touchmove, and touchend. Gesture events are, likewise: gesturestart, gesturemove, and gestureend. All gestures register a touch event but not all touch events register gestures. Gestures are registered when multiple fingers make contact with the touch surface and register significant location change in a concerted effort, such as two or more fingers swiping, a pinch action, and so on. In general, I've found it a good practice to use touch events to register finger actions; but it is required to return null on a touch event when there are multiple fingers involved and to handle such events with gestures. jQuery mobile has a nice suite of touch events built into its core that we can hook into. But jQuery and jQuery mobile sometimes fall short of the interaction we want to have for our users, so we'll outline best practices for adding customized user touch events to both the full and mobile version of the demo site. Let's get started… Time for action – adding a swipe advance to the home page The JavaScript to handle touch events is a little tricky; so, pay attention: Add the following lines to both sites/all/themes/dpk/js/global.js and sites/all/themes/dpk_mobile/js/global.js: Drupal.settings.isTouchDevice = function() { return "ontouchstart" in window; } if (Drupal.settings.isTouchDevice() ) { Drupal.behaviors.jQueryMobileSlideShowTouchAdvance = { attach: function(context, settings) { self = Drupal.behaviors.jQueryMobileSlideShowTouchAdvance; jQuery.each(jQuery(".views_slideshow_cycle_main. viewsSlideshowCycle-processed"), function(idx, value) { value.addEventListener("touchstart", self. handleTouchStart); jQuery(value).addClass("views-slideshow-mobileprocessed"); }) jQuery(self).bind("swipe", self.handleSwipe); }, detach: function() { }, original: { x: 0, y: 0}, changed: { x: 0, y: 0}, direction: { x: "", y: "" }, fired: false,handleTouchStart: function(evt) { self = Drupal.behaviors.jQueryMobileSlideShowTouchAdvance; if (evt.touches) { if (evt.targetTouches.length != 1) { return false; } if (evt.touches.length) { evt.preventDefault(); evt. stopPropagation() } self.original = { x: evt.touches[0].clientX, y: evt. touches[0].clientY } self.target = jQuery(this).attr("id").replace("views_ slideshow_cycle_main_", ""); Drupal.viewsSlideshow.action({ "action": "pause", "slideshowID": self.target }); evt.target.addEventListener("touchmove", self. handleTouchMove); evt.target.addEventListener("touchend", self. handleTouchEnd); } }, handleTouchMove: function(evt) { self = Drupal.behaviors.jQueryMobileSlideShowTouchAdvance; self.changed = { x: (evt.touches.length) ? evt.touches[0].clientX: evt.changedTouches[0].clientX, y: (evt.touches.length) ? evt.touches[0].clientY: evt.changedTouches[0].clientY }; h = parseInt(self.original.x - self.changed.x), v = parseInt(self.original.y - self.changed.y); if (h !== 0) { self.direction.x = (h < 0) ? "right":"left"; } if (v !== 0) { self.direction.y = (v < 0) ? "up": "down"; } jQuery(self).trigger("swipe"); }, handleTouchEnd: function(evt) { self = Drupal.behaviors.jQueryMobileSlideShowTouchAdvance; evt.target.removeEventListener("touchmove", self. handleTouchMove); evt.target.removeEventListener("touchend", self. handleTouchEnd); self.fired = false; }, handleSwipe: function(evt) { self = Drupal.behaviors.jQueryMobileSlideShowTouchAdvance; if (evt != undefined && self.fired == false) { Drupal.viewsSlideshow.action({ "action": (self.direction.x == "left")?"nextSlide":"previousSlide", "slideshowID": self.target}); self.fired = true; //only fire advance once per touch } } } } Clear Drupal's cache by either navigating to Configuration | Performance and clicking on the Clear cache button or entering these lines in a terminal: cd ~/sites/dpk/drush cc all Navigate to either home page with a touch-enabled device and you should be able to advance the home page slideshow with your fingers. What just happened? Let's take a look at how this code works. First, we have a function, isTouchDevice. This function returns true/false values if touch events are enabled on the browser. We use an if statement to wall off the touchscreen code, so browsers that aren't capable don't register an error. The Drupal behavior jQueryMobileSlideShowTouchAdvance has the attach and detach functions to satisfy the Drupal behavior API. In each function, we locally assign the self variable with the value of the entire object. We'll use this in place of the this keyword. In the Drupal behavior object, this can sometimes ambiguously refer to the entire object, or to the current sub-object. In this case, we want the reference to be to just the sub-object so we assign it to self. The attach function grabs all slideshow_cycle div elements in a jQuery each loop. The iteration of the loop adds an event listener to the div tag. It's important to note that the event listener is not bound with jQuery event binding. jQuery event binding does not yet support touch events. There's an effort to add them, but they are not in the general release that is used with Drupal 7. We must then add them with the browser native function, AddEventListener. We use the handleTouchStart method to respond to the touchstart event. We will add touchend and touchmove events after the touchstart is triggered.The other event that we're adding listens to this object for the swipe event. This is a custom event we will create that will be triggered when a swipe action happens. We will cover more on that shortly.The detach function is used to add cleanup to items when they are removed from the DOM. Currently, we have no interaction that removes items from the DOM and therefore no cleanup that's necessary for that removal to take place.Next, we add some defaults—original, changed, direction, and fired. We'll use those properties in our event response methods.HandleTouchStart event is fired when the finger first touches the surface. We make sure the evt.touches object has value and is only one touch. We want to disregard touches that are gestures. Also, we use preventDefault and stopPropagation on the event to keep it from bubbling up to other items in the DOM. self.original is the variable that will hold the touch's original coordinates. We store the values for touch[0]. We also name the target by getting the DOM ID of the cycle containing the div element. We can use string transforms on that ID to obtain the ID of the jQuery cycle being touched and will use that value when we send messages to the slideshow, based on the touch actions, like we do in the next line. We tell the slideshow to pause normal activity while we figure out what the user wants. To figure that out, we add touchmove and touchend events listening to the div element. handleTouchMove figures out the changed touch value. It does so by looking at the ClientX and ClientY values in the touch event.Some browsers support the changedTouches value which will do some calculations on how much the touch has changed since the last event was triggered. If it's available, we use it, or we use the value of the X and Y coordinates in the touch event's touches array. We do some subtraction against the original touch to find out how much the touch has changed and in what direction. We use self.direction to store the direction of the change. We store the direction in and tell the world that a swipe has begun on our div element by triggering a custom event on our self object.If you remember correctly, we used the handleSwipe method to respond to the swipe event. In handleSwipe we make sure the event has not already fired. If it hasn't, we use that swipe event to trigger a next or previous action on our jQuery cycle slideshow. Once we've fired the event, we change the self.fired to true so it will only fire once per touch. In the touchend responder, HandleTouchEnd, we remove both the touchmove and touchend responders and reset the fired state.But adding the touch events to both the desktop and the mobile themes begs the question, "Into which category does the table fall?" Have a go hero – adding a swipe gesture Add a swipe gesture event to the Menu Item page that allows you to scroll through menu items. The changing landscape (or portrait) Responsive web design is a design discipline that believes that the same markup should be used for both desktop and mobile screens, with the browser managing the display of items, rather than the user choosing an experience. If the screen is smaller, the layout adjusts and content emphasis remains.Conversely, the popularity of Internet-connected game consoles and DVI ports on large screen televisions gives us yet another paradigm for web pages—the large screen. I sit in front of a 72" TV screen and connect it to either my laptop or iPad and I have a browsing experience that is more passive, but completely immersive.Right now, I bet you're thinking, "So which is it Mr Author, two sites or one?" Well, both, actually. In some cases, with some interactions it will be necessary to do two site themes and maintain them both. In some cases, when you can start from scratch, you can do a design that can work on every browser screen size. Let's start over and put responsive design principals to work with what we already know about media queries and touch interfaces. "Starting over" or "Everything you know about designing websites is wrong" Responsive web design forces the designer to start over—to forget the artificial limitations of the size that print imposes and to start with a blank canvas. Once that blank canvas is in place, though, how do you fill it? How do you create "The One True Design" (cue the theme music)?This book is not a treatise on how to create the perfect design. For that, I can recommend A Book Apart and anything published by smashingmagazine.com. Currently, they are at the forefront of this movement and regularly publish ideas and information that is helpful without too much technical jargon.No, this book is more about giving you strategies to implement the designs you're given or that you create using Drupal. In point of fact, responsive design, at the time of writing, is in its infancy and will change significantly over the next 10 years, as new technology forces us to rethink our assumptions about what books, television, and movies are and what the Web is.So suffice to say, it begins with content. Prioritizing content is the job of the designer. Items you want the user to perceive first, second, and third are the organizing structure of your responsive design makeover. In most instances, it's helpful to present the web developer with four views of the website. Wire framing made easy Start with wireframes. A great wire framing tool is called Balsamiq. It has a purposefully "rough" look to all of the elements you use. That way, it makes you focus on the elements and leave the design for a later stage. It's also helpful for focusing clients on the elements. Many times the stake holders see a mockup and immediately begin the discussion of "I like blue but I don't like green/I like this font, but don't like that one." It can be difficult to move the stake holders out of this mindset, but presenting them with black-and-white chalk-style drawings of website elements can, in many cases, be helpful. Balsamiq is a great tool for doing just that: These were created with Balsamiq but could have been created in almost any primitive drawing program. There are many free ones as well as the more specialized pay ones. A simple layout like this is very easy to plan and implement. But very few of the websites you develop will ever be this simple. Let's take for instance that the menu item we have not, as yet, implemented, is for online ordering. How does that work? What do those screens look like? At this point we have a Menu page but, as per this mockup, that menu page will become the online ordering section. How do we move these menu items we created to a place where they can be put in an order and paid for? And more importantly, how does each location know what was ordered from their location?These are questions that come up in the mockup and requirements phase and whether you are building the site yourself or being given requirements from a superior, or a client, you now have a better idea of the challenges you will face implementing the single design for this site. With that, we've been given these mockups for the new online ordering system. The following mockup diagram is for adding an order: The following mockup diagram is for placing an order: We'll implement these mockups using the Drupal 7 Commerce module. The Commerce module is just a series of customized data entities and views that we can use as the building blocks of our commerce portion. We'll theme the views in the standard Drupal way but with an eye to multi-width screens, lack of hover state, and keeping in mind "hit zones" with fingers on small mobile devices. We'll also add some location awareness to assist with the delivery process. Once an order is placed, an e-mail will need to be sent to the correct franchise notifying them of the pizza order and initiating the process of getting it out the door.
Read more
  • 0
  • 0
  • 3284

article-image-various-components-sencha-touch
Packt
16 Feb 2012
8 min read
Save for later

The Various Components in Sencha Touch

Packt
16 Feb 2012
8 min read
  (For more resources on this topic, see here.) The reader can benefit from the previous article on Sencha Touch: Layouts Revisited. The TabPanel and Carousel components In our last application, we used buttons and a card layout to create an application that switched between different child items. While it is often desirable for your application to do this programmatically (with your own buttons and code), you can also choose to have Sencha Touch set this up automatically, using TabPanel or Carousel. TabPanel TabPanel is useful when you have a number of views the user needs to switch between, such as, contacts, tasks, and settings. The TabPanel component autogenerates the navigation for the layout, which makes it very useful as the main container for an application. The following is a code example: new Ext.Application({ name: 'TouchStart', launch: function() { this.viewport = new Ext.TabPanel({ fullscreen: true, cardSwitchAnimation: 'slide', tabBar:{ dock: 'bottom', layout: { pack: 'center' } }, items: [{ xtype: 'container', title: 'Item 1', fullscreen: false, html: 'TouchStart container 1', iconCls: 'info' }, { xtype: 'container', html: 'TouchStart container 2', iconCls: 'home', title: 'Item 2' }, { xtype: 'container', html: 'TouchStart container 3', iconCls: 'favorites', title: 'Item 3' }] }); }}); TabPanel, in this code, automatically generates a card layout; you don't have to declare a layout. You do need to declare a configuration for the tabBar component. This is where your tabs will automatically appear. In our previous code example, we dock the toolbar at the bottom. This will generate a large square button for each child item in the items list. The button will also use the iconCls value to assign an icon to the button. The title configuration is used to name the button. If you dock the tabBar component at the top, it makes the buttons small and round. It also eliminates the icons, even if you declare a value for iconCls, in your child items. Only the title configuration is used when the bar is docked at the top. Carousel The Carousel component is similar to TabPanel, but the navigation it generates is more appropriate for things such as slide shows. It probably would not work as well as a main interface for your application, but it does work well as a way to display multiple items in a single swipeable container. Similar to TabPanel, Carousel gathers its child items and automatically arranges them in a card layout. In fact, we can actually make just some simple modifications to our previous code to make it into a Carousel: new Ext.Application({ name: 'TouchStart', launch: function() { this.viewport = new Ext.Carousel({ fullscreen: true, direction: 'horizontal', items: [{ html: 'TouchStart container 1' }, { html: 'TouchStart container 2' }, { html: 'TouchStart container 3' }] }); }}); The first thing we did was create a new Ext.Carousel class instead of a new Ext.TabPanel class. We also added a configuration for direction, which can be either horizontal (scrolling from left to right) or vertical (scrolling up or down). We removed the docked toolbar, because, as we will see, Carousel doesn't use one. We also removed iconClass and title from each of our child items for the same reason. Finally, we removed the xtype configuration, since the Carousel automatically creates a panel for each of its items. Unlike TabPanel, Carousel has no buttons, only a series of dots at the bottom, with one dot for each child item. While it is possible to navigate using the dots, the Carousel component automatically sets itself up to respond to a swipe on a touch screen. You can duplicate this gesture in the browser by clicking and holding with the mouse, while moving it horizontally. If you declare a direction: vertical configuration in your Carousel, you can swipe vertically, to move between the child items. TabPanel and the Carousel components understand the activeItem configuration. This lets you set which item appears when the application first loads. Additionally, they all understand the setActiveItem() method that allows you to change the selected child item after the application loads. Carousel also has methods for next() and previous(), which allow you to step through the items in order. It should also be noted that, since TabPanel and Carousel both inherit from the panel, they also understand any methods and configurations that panels and containers understand. Along with containers and panels, TabPanel and Carousel will serve as the main starting point for most of your applications. However, there is another type of panel you will likely want to use at some point: the FormPanel.   FormPanel The FormPanel panel is a very specialized version of the panel, and as the name implies, it is designed to handle form elements. Unlike panels and containers, you don't need to specify the layout for FormPanel. It automatically uses its own special form layout. A basic example of creating a FormPanel would look something like this: var form = new Ext.form.FormPanel({ items: [ { xtype: 'textfield', name : 'first', label: 'First name' }, { xtype: 'textfield', name : 'last', label: 'Last name' }, { xtype: 'emailfield', name : 'email', label: 'Email' } ]}); For this example, we just create the panel and add items for each field in the form. Our xtype tells the form what type of field to create. We can add this to our Carousel and replace our first container, as follows: this.viewport = new Ext.Carousel({ fullscreen: true, direction: 'horizontal', items: [form, { layout: 'fit', html: 'TouchStart container 2' }, { layout: 'fit', html: 'TouchStart container 3' }]}); Anyone who has worked with forms in HTML should be familiar with all of the standard field types, so the following xtype attribute names will make sense to anyone who is used to standard HTML forms: checkboxfield fieldset hiddenfield passwordfield radiofield selectfield textfield textareafield These field types all match their HTML cousins, for the most part. Sencha Touch also offers a few specialized text fields that can assist with validating the user's input: emailfield - Accepts only a valid e-mail address, and on iOS devices, will pull up an alternate e-mail address and URL-friendly keyboard numberfield - Accepts only numbers urlfield - Accepts only a valid web URL, and also brings up the special keyboard These special fields will only submit if the input is valid. All of these basic form fields inherit from the main container class, so they have all of the standard height, width, cls, style, and other container configuration options. They also have a few field-specific options: label - A text label to use with the field labelAlign - Where the label appears; this can be top or left, and defaults to left labelWidth - How wide the label should be name - This corresponds to the HTML name attribute, which is how the value of the field will be submitted maxLength - How many characters can be used in the field required - If the field is required in order for the form to submit Form field placement While FormPanel is typically the container you will use when displaying form elements, you can also place them in any panel or toolbar, if desired. FormPanel has the advantage of understanding the submit() method that will post the form values via AJAX request or POST. If you include a form field in something that is not a FormPanel, you will need to get and set the values for the field using your own custom JavaScript method. In addition to the standard HTML fields, there are a few specialty fields available in Sencha Touch. These include the datepicker, slider, spinner, and toggle fields. DatePicker datepickerfield places a clickable field in the form with a small triangle on the far right side. You can add a date picker to our form by adding the following code after the emailfield item: ,{ xtype: 'datepickerfield', name : 'date', label: 'Date'} When the user clicks the field, a DatePicker will appear, allowing the user to select a date by rotating the month, day, and year wheels, by swiping up or down. Sliders, spinners, and toggles Sliders allow for the selection of a single value from a specified numerical range. The sliderfield value displays a bar, with an indicator, that can be slid horizontally to select a value. This can be useful for setting volume, color values, and other ranged options. Like the slider, a spinner allows for the selection of a single value from a specified numerical range. The spinnerfield value displays a form field with a numerical value with + and - buttons on either side of the field. A toggle allows for a simple selection between one and zero (on and off) and displays a toggle-style button on the form. Add the following new components to the end of our list of items: ,{ xtype : 'sliderfield', label : 'Volume', value : 5, minValue: 0, maxValue: 10},{ xtype: 'togglefield', name : 'turbo', label: 'Turbo'},{xtype: 'spinnerfield',minValue: 0,maxValue: 100,incrementValue: 2,cycle: true} The following screenshot shows how the new components will look: Our sliderfield and spinnerfield have configuration options for minValue and maxValue. We also added an incrementValue attribute, to spinnerfield, that will cause it to move in increments of 2 whenever the + or - button is pressed.  
Read more
  • 0
  • 0
  • 2947

article-image-sencha-touch-layouts-revisited
Packt
16 Feb 2012
7 min read
Save for later

Sencha Touch: Layouts Revisited

Packt
16 Feb 2012
7 min read
  (For more resources on this topic, see here.) The reader can benefit from the previous article on The Various Components in Sencha Touch. The base component class When we talk about components in Sencha Touch, we are generally talking about buttons, panels, sliders, toolbars, form fields, and other tangible items that we can see on the screen. However, all of these components inherit their configuration options, methods, properties, and events from a single base component with the startlingly original name of component. This can obviously lead to a bit of confusion, so we will refer to this as Ext.Component. One of the most important things to understand is that you will never actually use Ext.Component directly. It is simply used as a building block for all of the other components in Sencha Touch. However, it is important to be familiar with the base component class, because anything it can do, all the other components can do. Learning this one class can give you a huge head start on everything else. Some of the more useful configuration options of Ext.Component are as follows: border cls disabled height/width hidden html margin padding scroll style ui Ext.Component also contains a number of useful methods that will allow you to get and set properties on any Sencha Touch component. Here are a few of those methods: addCls and removeCls: Add or remove a CSS class from your component. destroy: Remove the component from memory. disable and enable: Disable or enable the component (very useful in forms). getHeight, getWidth, and getSize: Get the current height, width, or size of the component. Size returns both height and width. You can also use setHeight, setWidth, and setSize, to change the dimensions of your component. show and hide: Show or hide the component. setPosition: Set the top and left values for the component. update: Update the content area of a component. Unlike our configuration options, methods can only be used once the component is created. This means we also need to understand how to get the component itself before we can begin using the methods. This is where the Ext class comes into play. The Ext object and Ext.getCmp() The Ext object is created, by default, when the Sencha Touch library is loaded. This object has methods that are used to create our initial application and its components. It also allows us to talk to our other components after they have been created. For example, let's take a look at a code example: new Ext.Application({ name: 'TouchStart', launch: function() { var hello = new Ext.Container({ fullscreen: true, html: '<div id="hello">Hello World</div>', id: 'helloContainer' }); this.viewport = hello;}}); The configuration option, id: 'helloContainer' will allow us to grab the container, later on, using our Ext class and the method getCmp(). For example, we can add the following code after this.viewport = hello;: var myContainer = Ext.getCmp('helloContainer');myContainer.update('Hello Again!'); By using Ext.getCmp, we get back the component with an id value of helloContainer, which we then set to our variable myContainer. Using this method returns an actual component, in this case a container. Since we get this object back as a container component, we can use any of the methods that the container understands. For our example, we used the update() method to change the content of the container to 'Hello Again!'. Typically, these types of changes will be generated by a button click and not in the launch function. This example simply shows that we can manipulate the component on the fly even after it gets created. The ID configuration It's a good idea to include an id configuration in all of your components. This makes it possible to use Ext.getCmp() to get to those components, later on, when we need them. Remember to keep the ID of every component unique. If you plan on creating multiple copies of a component, you will need to make sure that a unique ID is generated for each copy. The Ext.getCmp() method is great for grabbing Sencha Touch components and manipulating them.   Layouts revisited Layouts are another area we need to expand upon. When you start creating your own applications, you will need a firm understanding of how the different layouts affect what you see on the screen. To this end, we are going to start out with a demonstration application that shows how the different layouts work. For the purposes of this demo application, we will create the different components, one at a time, as individual variables. This is done for the sake of readability and should not be considered the best programming style. Remember that any items created this way will take up memory, even if the user never views the component. var myPanel = new Ext.Panel({ ... It is always a much better practice to create your components, using xtype attibutes, within your main container: items: [{ xtype: 'panel', ... This allows Sencha Touch to render the components as they are needed, instead of all at once when the page loads. The card layout To begin with, we will create a simple card layout: new Ext.Application({ name: 'TouchStart', launch: function() { var layoutPanel = new Ext.Panel({ fullscreen: true, layout: 'card', id: 'layoutPanel', cardSwitchAnimation: 'slide', items: [hboxTest] }); this.viewport = layoutPanel; }}); This sets up a single panel with a card layout. The card layout arranges its items similar to a stack of cards. Only one of these cards is active and displayed at a time. The card layout keeps any additional cards in the background and only creates them when the panel receives the setActiveItem() command. Each item in the list can be activated by using setActiveItem() and the item number. This can be a bit confusing, as the numbering of the items is zero-indexed, meaning that you start counting at zero and not one. For example, if you want to activate the fourth item in the list, you would use: layoutPanel.setActiveItem(3); In this case, we are starting out with only a single card/item called hboxTest. We need to add this container to make our program run. The hbox layout Above the line that says var layoutPanel = new Ext.Panel({, in the preceding code, add the following code: var hboxTest = new Ext.Container({ layout: { type: 'hbox', align: 'stretch' }, items: [{ xtype: 'container', flex: 1, html: 'My flex is 1', margin: 5, style: 'background-color: #7FADCF' }, { xtype: 'container', flex: 2, html: 'My flex is 2', margin: 5, style: 'background-color: #7FADCF' }, { xtype: 'container', width: 80, html: 'My width is 80', margin: 5, style: 'background-color: #7FADCF' }]}); This gives us a container with an hbox layout and three child items. Child and parent In Sencha Touch, we often find ourselves dealing with very large arrays of items, nested in containers that are in turn nested in other containers. It is often helpful to refer to a container as a parent to any items it contains. These items are then referred to as the children of the container. The hbox layout stacks its items horizontally and uses the width and flex values to determine how much horizontal space each of its child items will take up. The align: 'stretch' configuration causes the items to stretch to fill all of the available vertical space. If we run the code now, we should see this: You should try adjusting the flex and width values to see how it affects the size of the child containers. You can also change the available configuration options for align (center, end, start, and stretch), to see the different options available. Once you are finished, let's move on and add some more items to our card layout.  
Read more
  • 0
  • 0
  • 2801
article-image-creating-simple-application-sencha-touch
Packt
15 Feb 2012
10 min read
Save for later

Creating a Simple Application in Sencha Touch

Packt
15 Feb 2012
10 min read
  (For more resources on this topic, see here.) Setting up your folder structure Before we get started, you need to be sure that you've set up your development environment properly. Root folder You will need to have the folders and files for your application located in the correct web server folder, on your local machine. On the Mac, this will be the Sites folder in your Home folder. On Windows, this will be C:xamphtdocs (assuming you installed xampp). Setting up your application folder Before we can start writing code, we have to perform some initial set up, copying in a few necessary resources and creating the basic structure of our application folder. This section will walk you through the basic setup for the Sencha Touch files, creating your style sheets folder, and creating the index.html file. Locate the Sencha Touch folder you downloaded. Create a folder in the root folder of your local web server. You may name it whatever you like. I have used the folder name TouchStart in this article. Create three empty sub folders called lib, app, and css in your TouchStart folder. Now, copy the resources and src folders, from the Sencha Touch folder you downloaded earlier, into the TouchStart/lib folder. Copy the following files from your Sencha Touch folder to your TouchStart/lib folder: sencha-touch.js sencha-touch-debug.js sencha-touch-debug-w-comments.js Create an empty file in the TouchStart/css folder called TouchStart.css. This is where we will put custom styles for our application. Create an empty index.html file in the main TouchStart folder. We will flesh this out in the next section. Icon files Both iOS and Android applications use image icon files for display. This creates pretty rounded launch buttons, found on most touch-style applications. If you are planning on sharing your application, you should also create PNG image files for the launch image and application icon. Generally, there are two launch images, one with a resolution of 320 x 460 px, for iPhones, and one at 768 x 1004 px, for iPads. The application icon should be 72 x 72 px. See Apple's iOS Human Interface Guidelines for specifics, at http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/mobilehig/IconsImages/IconsImages.html. When you're done, your folder structure should look as follows: Creating the HTML application file Using your favorite HTML editor, open the index.html file we created when we were setting up our application folder. This HTML file is where you specify links to the other files we will need in order to run our application. The following code sample shows how the HTML should look: <!DOCTYPE html><html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>TouchStart Application - My Sample App</title> <!-- Sencha Touch CSS --> <link rel="stylesheet" href="lib/resources/css/sencha-touch.css"type="text/css"> <!-- Sencha Touch JS --> <script type="text/javascript" src="lib/sencha-touch-debug.js"></script> <!-- Application JS --> <script type="text/javascript" src="app/TouchStart.js"></script> <!-- Custom CSS --> <link rel="stylesheet" href="css/TouchStart.css" type="text/css"> </head> <body></body></html> Comments In HTML, anything between <!-- and --> is a comment, and it will not be displayed in the browser. These comments are to tell you what is going on in the file. It's a very good idea to add comments into your own files, in case you need to come back later and make changes. Let's take a look at this HTML code piece-by-piece, to see what is going on in this file. The first five lines are just the basic set-up lines for a typical web page: <!DOCTYPE html><html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>TouchStart Application - My Sample App</title> With the exception of the last line containing the title, you should not need to change this code for any of your applications. The title line should contain the title of your application. In this case, TouchStart Application – Hello World is our title. The next few lines are where we begin loading the files to create our application, starting with the Sencha Touch files. The first file is the default CSS file for the Sencha Touch library—sencha-touch.css. <link rel="stylesheet" href="lib/resources/css/ext-touch.css"type="text/css"> CSS files CSS or Cascading Style Sheet files contain style information for the page, such as which items are bold or italic, which font sizes to use, and where items are positioned in the display. The Sencha Touch style library is very large and complex. It controls the default display of every single component in Sencha Touch. It should not be edited directly. The next file is the actual Sencha Touch JavaScript library. During development and testing, we use the debug version of the Sencha Touch library, sencha-touchdebug.js: <script type="text/javascript" src="lib/sencha-touch-debug.js"></script> The debug version of the library is not compressed and contains comments and documentation. This can be helpful if an error occurs, as it allows you to see exactly where in the library the error occurred. When you have completed your development and testing, you should edit this line to use sencha-touch.js instead. This alternate file is the version of the library that is optimized for production environments and takes less bandwidth and memory to use; but, it has no comments and is very hard to read. Neither the sencha-touch-debug.js nor the sencha-touch.js files should ever be edited directly. The next two lines are where we begin to include our own application files. The names of these files are totally arbitrary, as long as they match the name of the files you create later, in the next section of this chapter. It's usually a good idea to name the file the same as your application name, but that is entirely up to you. In this case, our files are named TouchStart.js and TouchStart.css. <script type="text/javascript" src="app/TouchStart.js"></script> This first file, TouchStart.js, is the file that will contain our JavaScript application code. The last file we need to include is our own custom CSS file, called TouchStart.css. This file will contain any style information we need for our application. It can also be used to override some of the existing Sencha Touch CSS styles. <link rel="stylesheet" href="resources/css/TouchStart.css"type="text/css"> This closes out the </head> area of the index.html file. The rest of the index.html file contains the <body></body> tags and the closing </html> tag. If you have any experience with traditional web pages, it may seem a bit odd to have empty <body></body> tags, in this fashion. In a traditional web page, this is where all the information for display would normally go. For our Sencha Touch application, the JavaScript we create will populate this area automatically. No further content is needed in the index.html file, and all of our code will live in our TouchStart.js file. So, without further delay, let's write some code!   Starting from scratch with TouchStart.js Let's start by opening the TouchStart.js file and adding the following: new Ext.Application({name: 'TouchStart',launch: function() {var hello = new Ext.Container({fullscreen: true,html: '<div id="hello">Hello World</div>' });this.viewport = hello; }}); This is probably the most basic application you can possibly create: the ubiquitous "Hello World" application. Once you have saved the code, use the Safari web browser to navigate to the TouchStart folder in the root folder of your local web server. The address should look like the following: http://localhost/TouchStart/, on the PC http://127.0.0.1/~username/TouchStart, on the Mac (username should be replaced with the username for your Mac) As you can see, all that this bit of code does is create a single window with the words Hello World. However, there are a few important elements to note in this example. The first line, NewExt.Application({, creates a new application for Sencha Touch. Everything listed between the curly braces is a configuration option of this new application. While there are a number of configuration options for an application, most consist of at least the application's name and a launch function. Namespace One of the biggest problems with using someone else's code is the issue of naming. For example, if the framework you are using has an object called "Application", and you create your own object called "Application", the two functions will conflict. JavaScript uses the concept of namespaces to keep these conflicts from happening. In this case, Sencha Touch uses the namespace Ext. It is simply a way to eliminate potential conflicts between the frameworks' objects and code, and your own objects and code. Sencha will automatically set up a namespace for your own code as part of the new Ext.Application object. Ext is also part of the name of Sencha's web application framework called ExtJS. Sencha Touch uses the same namespace convention to allow developers familiar with one library to easily understand the other. When we create a new application, we need to pass it some configuration options. This will tell the application how to look and what to do. These configuration options are contained within the curly braces ({}) and separated by commas. The first option is as follows: name: 'TouchStart' The launch configuration option is actually a function that will tell the application what to do once it starts up. Let's start backwards on this last bit of code for the launch configuration and explain this.viewport. By default, a new application has a viewport. The viewport is a pseudo-container for your application. It's where you will add everything else for your application. Typically, this viewport will be set to a particular kind of container object. At the beginning of the launch function, we start out by creating a basic container, which we call hello: launch: function() {var hello = new Ext.Container({fullscreen: true,html: '<div id="hello">Hello World</div>' });this.viewport = hello; } Like the Application class, a new Ext.Container class is passed a configuration object consisting of a set of configuration options, contained within the curly braces ({}) and separated by commas. The Container object has over 40 different configuration options, but for this simple example, we only use two: fullscreen sets the size of the container to fill the entire screen (no matter which device is being used). html sets the content of the container itself. As the name implies, this can be a string containing either HTML or plain text. Admittedly, this is a very basic application, without much in the way of style. Let's add something extra using the container's layout configuration option. My application didn't work! When you are writing code, it is an absolute certainty that you will, at some point, encounter errors. Even a simple error can cause your application to behave in a number of interesting and aggravating ways. When this happens, it is important to keep in mind the following: Don't Panic. Retrace your steps and use the tools to track down the error and fix it.  
Read more
  • 0
  • 0
  • 3806

article-image-article-html5-and-the-mobile-web
Packt
31 Jan 2012
16 min read
Save for later

HTML5 and the Mobile Web

Packt
31 Jan 2012
16 min read
(For more resources on this topic, see here.) Introduction Both HTML5 and mobile web are promising technologies. Both have relatively short histories. In this article, most topics we will be covering are relatively basic. This is to help you get started with mobile development quickly and with minimum effort. Both mobile and HTML5 are still evolving in nature and there could be many questions in your mind. We will clear those doubts and set your mind focused on things that matter. The mobile web is growing fast. We now have mobile Safari which is one of the most used apps on the iPhone, allowing developers to build high performance web applications and enhancing users' browsing experience. You do not need a developer account to host and run a mobile site, you don't need to get approval from any app market to launch a mobile website and you can make updates any time you like without the hassle of waiting for approval. All these are benefits of mobile web development, but at the same time, there are challenges such as inconsistencies among browsers, the lack of certain features compared to native apps, and security. We can't tackle them all, but we sure can solve some of them. And we will see, when developing a mobile site, how we can separate the common practices from the best practices. There are literally thousands of smartphones out there; you don't need every single one of them to test your application on. In fact, you may need fewer than 10. If that's still out of your budget, then two devices are good enough. For the rest, you can use simulators/emulators to do the job. This book focuses on six A-grade mobile devices, with focus specifically on iPhone, Android, and Windows Phone: iOS Android Windows Mobile Blackberry v6.0 and above Symbian 60 Palm webOS There are two browsers that are device-independent that will also be covered in this book. They are: Opera Mobile Firefox Mobile Just because other browsers are not in the list does not mean they won't be covered by the issues and techniques we discuss in this book. Identifying your target mobile devices Target browser: all You can't possibly make a mobile site for every single mobile device. No one has the time or energy to do so. Cross-browser mobile web development can be crazy. It's hard to define the scope of the work, as John Resig (creator of jQuery Mobile) pointed out in his presentation slide describing his experience building jQuery Mobile (http://www.slideshare.net/jeresig/testing-mobile-javascript), he asked three questions: Which platforms and browsers are popular? Which browsers are capable of supporting modern scripting? What devices and simulators do I acquire to test with? When you are building a mobile site, you have to ask yourself similar questions, but not the exact same questions, because remember your site should be specifically tailored to your target audience. So your questions should be: Which platforms and browsers are most commonly used by visitors on my website? How many people access my website from a mobile device that is capable of supporting modern scripting? Which devices and simulators do I need for testing? Which platforms and browsers are most commonly used by visitors on my website? Now let's answer the first question. Before building a mobile website, you must first find out who are your target audience, and what mobile devices they use when visiting your site. There are many analytics tools that can help you answer these questions. One of those is Google Analytics. You can sign up for a free Google Analytics account at: http://www.google.com/analytics/. The way to do it is fairly straightforward: most developers are no strangers to Google Analytics. All you have to do is to include the JavaScript snippet from the the Google Analytics site and embed it in your web pages. JavaScript can be rendered in most modern smartphones, so there is really no difference between using it on a desktop site and on a mobile site. How many people access my website from a mobile device that is capable of supporting modern scripting? Now let's answer the second question. One thing you may want to find out is the number of people using mobile browsers to surf your site. And you also want to find out how many people use a relic mobile browser that doesn't support JavaScript at all. This is because if the percentage of people using low-end smartphones is higher than that of people using high-end smartphones, it may not be worthwhile using HTML5 in the first place (although the chance of this is very low). So if your goal is not just to know the number of people using smartphones, but also the number of people who use older versions of mobile phones, Google Analytics for mobile comes to the rescue. You can download the script from: http://code.google.com/mobile/analytics/download.html#Download_the_Google_Analytics_server_side_package Google Analytics for mobile server-side packages currently supports JSP, ASPX, Perl, and PHP. Let's take a look at one of the examples in PHP. All you have to do is to change the ACCOUNT ID GOES HERE with your GA account ID. But remember to replace 'UA-xx' with 'MO-xx'. Unfortunately, when you use the server-side version, you can't use it on pages where you also use the standard JavaScript tracking code, ga.js. Using the server-side version means that you have to give up the JavaScript version. It can be annoying because the JavaScript version provides a lot of dynamic tracking mechanisms that are lacking in the server-side version: <?php  //  Copyright 2009 Google Inc. All Rights Reserved.  $GA_ACCOUNT = "ACCOUNT ID GOES HERE";  $GA_PIXEL = "ga.php";   function googleAnalyticsGetImageUrl() {    global $GA_ACCOUNT, $GA_PIXEL;    $url = "";    $url .= $GA_PIXEL . "?";    $url .= "utmac=" . $GA_ACCOUNT;    $url .= "&utmn=" . rand(0, 0x7fffffff);     $referer = $_SERVER["HTTP_REFERER"];    $query = $_SERVER["QUERY_STRING"];    $path = $_SERVER["REQUEST_URI"];     if (empty($referer)) {      $referer = "-";    }    $url .= "&utmr=" . urlencode($referer);     if (!empty($path)) {      $url .= "&utmp=" . urlencode($path);    }     $url .= "&guid=ON";     return $url;  }?> Alternatives to Google Analytics Google Analytics is not the only mobile analytics service in the market. There are other services providing more specialized services. For example, PercentMobile is a hosted mobile analytics service that makes your mobile audience and opportunities clear. You can find out more about this service at: http://percentmobile.com/ Accuracy of Google Analytics The location reported by mobile devices may not always be accurate; Google Analytics uses IP addresses to determine user location for Map Overlay reports. They are subject to possible inaccuracy, as mobile IPs originate from the wireless carrier gateway which is not necessarily co-located with mobile users. Server loading speed concern Due to the server-side processing, some additional server load may be incurred. Google recommends you first test the snippet on a few of your pages to ensure all is well before rolling out to an entire site. Setting up mobile development tools Target browser: all Now one question still remains unanswered from the previous recipe: which devices and simulators do I need for testing? We will find this out in this article. If you have figured out what major mobile devices you are going to support, now is the time to see how to set them up. Mobile development can be costly if you test on various mobile devices. Although we have all these mobile simulators and emulators available for testing, it's not as good as testing on a real device. Now let's see how we can maximize the testing coverage and minimize the cost. Getting ready We are going to make some assumptions here. Each case is different, but the idea is the same. Let's assume you have a Windows operating system on your desktop, but the top visitors to your site surf using iOS, Android, and Blackberry. How to do it... Your goal is to maximize the coverage and minimize the cost. All three devices have emulators, but not all support different platforms. Name Compatibility iOS simulator Mac Android emulator Windows, Mac, Linux Blackberry simulator Windows As you can see, since iOS simulator only works for Mac, and if you are running a Windows OS, the best and only choice is to purchase an iPhone for testing. For Android and Blackberry, because they both have emulators for Windows, to save budget, you can download the emulators. How it works... List the top mobile devices people use to surf your site. Know the machine OS you are using for the development. Find out the compatibility of each of these device emulators to your development environment. There's more... If you have the budget for more than one mobile device having a different OS, you can think further about screen sizes and the DPI of the mobile device. You may not need to buy two high-end devices. For instance, it's not necessary to own an iPhone4 and an Android Thunderbolt. You can buy a lower-end of Android just to test out how your site would look on a lower-end device. So the idea is to combine your OS, mobile devices, and emulators to maximize the scenarios to cover. Device simulator/emulator download lookup table The following table shows a list of popular mobile device emulators for mobile web design and development testing: Name Type Compatibility URL iOS Simulator Mac https://developer.apple.com/devcenter/ios/index.action#downloads Android Emulator Mac, Win, Linux http://developer.android.com/sdk/index.html HP webOS Virtual Machine Mac, Win, Linux http://developer.palm.com/index.php?option=com_content&view=article&id=1788&Itemid=55 Nokia Symbian Emulator Win http://www.forum.nokia.com/info/sw.nokia.com/id/ec866fab-4b76-49f6-b5a5-af0631419e9c/S60_All_in_One_SDKs.html Blackberry Emulator Win http://us.blackberry.com/developers/resources/simulators.jsp Windows Mobile 7 Emulator Win http://www.microsoft.com/downloads/en/details.aspx?FamilyID=04704acf-a63a-4f97-952c-8b51b34b00ce Browser simulator/emulator download lookup table Apart from device testing tools, we also have tools for platform-independent browsers, notably Opera and Firefox. These are shown in the table below: Name Type Compatibility URL Opera Mobile Emulator Mac, Win, Linux http://www.opera.com/developer/tools/ Opera Mini Simulator Mac, Win, Linux http://www.opera.com/developer/tools/http://www.opera.com/mobile/demo/ Firefox for Mobile Simulator Mac, Win, Linux http://www.mozilla.com/en-US/mobile/download/ Remote testing Apart from emulators and simulators, there are also test frameworks that give you remote access to REAL devices. One of those tools is DeviceAnywhere; one problem is that it is not free. http://www.deviceanywhere.com/ BlackBerry simulator Target Browser: BlackBerry Most mobile device simulators are easy to install and configure if you follow the instructions on their sites, but BlackBerry simulators work differently from other mobile device simulators. For Blackberry device simulators, to connect to Internet, besides downloading the simulators, you also need to download and install BlackBerry Email and MDS Services Simulator. Getting ready Make sure you have chosen a simulator to download from:http://us.blackberry.com/developers/resources/simulators.jsp How to do it... First, go to the page: https://swdownloads.blackberry.com/Downloads/entry.do?code=A8BAA56554F96369AB93E4F3BB068C22&CPID=OTCSOFTWAREDOWNLOADS&cp=OTC-SOFTWAREDOWNLOADS. There you will see a list of products similar to the following screenshot: Now select BlackBerry Email and MDS Services Simulator Package and then click on Next. After downloading and installing the software, you must first launch the service simulator before the Blackberry simulator, in order to allow it to connect to the Internet.The following is a screenshot of a Blackberry simulator: Setting up the mobile development environment Target browser: all Before we start mobile web development, we have to first set up a development environment. Getting ready Set up localhost on your machine. For Windows, Mac, or Linux, the easiest way to set it up is to use the popular and free XAMPP software: (http://www.apachefriends.org/en/index.html). Make sure you have a wireless connection. Also you should have a mobile device with you. Otherwise, use a mobile simulator/emulator. Ensure your mobile device and your desktop are on the same wireless network. How to do it... Create an HTML file and name it ch01r1.html at the root directory of your localhost: Inside ch01r01.html, type in the following: <html>  <head>  <meta name="viewport" content="width=device-width, initial-scale=1.0">  </head>  <body>    <header>    Main Navigation here    </header>  body content here    <footer>    Footer links here    </footer>  </body></html> Now get your IP address. If you are using Windows, you can type the following line in your command prompt: ipconfig Once you have got your IP address, (for example, 192.168.1.16.), enter it in your mobile browser URL address bar. Now you should see the page load with the text displayed: How it works... Within the same network, your mobile device can access your desktop host through your desktop IP address. There's more... If you don't have a mobile device, you can use one of the simulators for testing. But it's recommended to have at least one or two real mobile devices for testing. A simulator could test most things, but not everything, accurately. Testing on a Safari desktop If your main target audience is iPhone mobile Safari users, you can also do testing on a desktop to save time. To do so, open up Safari, go to Preferences, click on the Advanced tab, check Show Develop menu bar as shown next: Now click on the display menu for the current page, choose Develop | User Agent | Mobile Safari 3.1.3 – iPhone: List of community-based collection of emulators/simulators There is a list of emulators and simulators available if you really don't have a Smartphone at hand. You can find the list on a wiki on the Mobile Boilerplate project: https://github.com/h5bp/mobile-boilerplate/wiki/Mobile-Emulators-&-Simulators List of emulators/simulators collection by Firtman Maximiliano Firtman, a mobile and web developer, also an author, maintains a list of emulators on his site at: http://www.mobilexweb.com/emulators Using HTML5 on the mobile web Target browser: all Now we are going to create a simple HTML5 page for your mobile device. If you already have experience with older versions of HTML, HTML5 should be easy to understand. And if you have made a web page for desktop before, it won't be hard for you to make one for a mobile device. Getting ready Create a new file ch01e2.html. How to do it... Save the following code in the file: <!doctype html><html>  <head>  <meta name="viewport" content="width=device-width, initial-scale=1.0">  </head>  <body>    hello to the HTML5 world!  </body></html> Now render this in your mobile browser, and you should see the text render just as expected. How it works... As you can see, the only difference between HTML5 and other HTML pages is the Document Type Definition (DTD) we used: <!doctype html>. You might have seen the code <meta name="viewport" content="width=devicewidth, initial-scale=1.0"> and are wondering what it does. It helps Mobile Safari to know that the page is as wide as the device. Setting initial-scale=1 tells the browser not to zoom in or out. There's more... Here's a little bit of history of HTML5: there are two versions of HTML5 draft, one created by W3C and the other by WHATWG. The W3C is run by a group that is democratic in nature, but super slow in practice. WHATWG is edited by one person, Ian Hickson (who is also working for Google), and a group of people who are not public. As Ian does most of the decision making, it makes WHATWG's version progress faster. HTML5 and version number You might be wondering why HTML5 is being so ambiguous by using a declaration without even a version number. Well there are many reasons to justify this: Version support of HTML doesn't matter much to browsers. What matters is the feature support. In other words, if the browser supports the HTML5 feature you are using, even if you declare the document as HTML4, it will still render the HTML5 element as expected. It's easier to type! Mobile doctypes One question you may ask is whether it is safe to use the HTML5 DTD <!doctype html>. The answer is DTDs were made for validation, not browser rendering. Your next question might be: what about quirks mode? <!doctype html> is the minimum information required to ensure that a browser renders in standards mode. So you are pretty safe to use <!doctype html>. You may have noticed that we use <!doctype html> instead of <!DOCTYPE html>. The reason is HTML5 is not case sensitive, but for consistency with other HTML tags, we will use lowercase throughout the book. Free resources to learn HTML5 There are many excellent and free books, and articles about basic HTML5 tags. If you are unfamiliar with HTML5, you can check out one of the following: HTML5 Doctor: http://html5doctor.com/ Dive Into HTML5: http://diveintohtml5.org/ HTML5 Rocks:http://www.html5rocks.com/ If you are the kind of person who really wants to know every detail about something, you can read the official HTML5 specs. The W3C version of the spec is at: http://dev.w3.org/html5/spec/Overview.html The WHATWG version of HTML Living Standard is at: http://www.whatwg.org/specs/web-apps/current-work/multipage/
Read more
  • 0
  • 0
  • 1866

article-image-article-html5-mobile-setup-and-optimization
Packt
30 Jan 2012
18 min read
Save for later

HTML5: Mobile Setup and Optimization

Packt
30 Jan 2012
18 min read
(For more resources on this topic, see here.) Introduction While there are many operating systems (OS) as well as device makers, inevitably there could be cross-browser issues that cost us a lot of headaches. But on the other hand, we developers love the challenges and set out to tackle them! Throughout this article, we will first focus on cross-browser/browser-specific setup and optimizations you may want to consider. We will then go on to look at a couple of general/ browser-specific features you may want to add at the start of your mobile development. Adding a home screen button icon Target devices: iOS, Android, Symbian On modern smartphones, screens are mostly touch based. The iPhone revolutionized the way we think of mobile by making everything on your device an "app"; even SMS and phone dialing behave like apps with an icon on the home screen. For an HTML web app, things are a bit different; users have to go to a browser app first, type in the address and then launch your site. This can be too much hassle from a user perspective, so on certain smartphones, users can bookmark a home screen icon to a particular web app, so they can launch that particular web app site directly from the icon on their home screen. That sounds cool, right? Yes, it does, but there are also issues associated with it. Not all browsers behave the same way when it comes to touch icons. In this recipe, we will examine the behavior of each browser and how to make home screen icons available to as many mobile browsers as possible. Getting ready First, you have to download the icon sets from the chapter code folder. If you open up the folder, you should be able to see the following:apple-touch-icon.png apple-touch-icon-57x57-precomposed.pngapple-touch-icon-72x72-precomposed.png apple-touch-icon-114x114-precomposed.png apple-touch-icon-precomposed.png These images will be used for different devices. Create an HTML document and name it ch02r01.html. How to do it... In your HTML document, use the following code: <!doctype html><html>  <head>    <title>Mobile Cookbook</title>    <meta charset="utf-8">    <meta name="viewport" content="width= device-width,     initial-scale=1.0">    <link rel="apple-touch-icon-precomposed"     sizes="114x114" href="icons/apple-touch-icon-114x114     -precomposed.png">    <link rel="apple-touch-icon-precomposed"      sizes="72x72" href="icons/apple-touch-icon-72x72     -precomposed.png">    <link rel="apple-touch-icon-precomposed"      href="icons/apple-touch-icon-precomposed.png">    <link rel="shortcut icon" href="     icons/apple-touch-icon.png">  </head>  <body>  </body></html>  How it works... Now let's break down the code: As of iOS 4.2.1, it's possible to specify multiple icons for different device resolutions by using the sizes attribute. <link rel="apple-touch-icon-precomposed" sizes="114x114" href="apple-touch-icon-114x114-precomposed.png"> For high resolution retina displays on iPhone 4, a 114 x 114 icon is used. <link rel="apple-touch-icon-precomposed" sizes="72x72" href="apple-touch-icon-72x72-precomposed.png"> For iPad, a 72 x 72 icon can be used. For non-retina iPhone, Android 2.1+ devices, a 57 x 57 low resolution icon is used. <link rel="apple-touch-icon-precomposed" href="apple-touch-icon-precomposed.png"> For Nokia Symbian 60 devices, a shortcut icon is used in link relation to tell the device about the shortcut icon. <link rel="shortcut icon" href="img/l/apple-touch-icon.png"> Here is what the bookmark looks like on Android: There's more... There must be a couple of questions in your mind after seeing the previous example: Isn't it possible to define more than one value in the rel attribute? So can we combine the last two lines into something as follows? <link rel="shortcut icon apple-touch-icon-precomposed"href="apple-touch-icon-precomposed.png"> It was tested, but somehow mobile browsers couldn't recognize the value. You might have seen people use: <link rel="apple-touch-icon-precomposed" media="screen and (min-resolution: 150dpi)"href="apple-touch-icon-114x114-precomposed.png"> Together with Paul Irish and Divya Manian, we have been working on Mobile Boilerplate (http://www.h5bp.com/mobile) that provides a rock-solid default for frontend mobile development. In Mobile Boilerplate, we have covered all the current scenarios and possible future scenarios: https://github.com/h5bp/mobile-boilerplate/blob/master/index.html#L21 Everything you always wanted to know about touch icons Most ideas presented on this topic are originated from Mathias Bynens. His original article Everything you always wanted to know about touch icons can be found at:http://mathiasbynens.be/notes/touch-icons. Official documentation about Apple touch icons There is a list of official documentation where you can find more information about touch icons for each specific device and browser: Apple touch icon: http://developer.apple.com/library/safari/#documentation/ AppleApplications/Reference/SafariWebContent/ ConfiguringWebApplications/ConfiguringWebApplications.html Official information from WHATWG: http://www.whatwg.org/specs/web-apps/current-work/multipage/ links.html#rel-icon Apple Custom Icon and Image Creation Guidelines Guidelines and articles about how to create a touch icon can be found at the Apple – Custom Icon and Image Creation Guidelines article: http://developer.apple.com/library/ios/#documentation/ userexperience/conceptual/mobilehig/IconsImages/IconsImages html#//apple_ref/doc/uid/TP40006556-CH14-SW11. Preventing text resize Target devices: iOS, Windows Mobile On certain mobile devices like the iPhone and Windows Mobile, browser text may resize when you rotate the device from portrait to landscape mode. This could be problematic to web developers because we want to have full control of the design and rendering of the website. Getting ready Create a new HTML file, and name it ch02r02.html. Enter the following code: <!doctype html><html>  <head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width,  initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">  <style>    figure, figcaption, header {      display:block;      margin:0 auto;      text-align:center;    }  </style>  </head>  <body>    <header>      HTML5 Logo    </header>    <figure>      <img src="HTML5_Badge_128.png" alt="HTML5 Badge" />      <figcaption>        It stands strong and true, resilient and         universal as the markup you write.         It shines as bright and as bold as the         forward-thinking, dedicated web developers         you are.         It's the standard's standard, a pennant for         progress.         And it certainly doesn't use tables for layout.      </figcaption>    </figure>      </body></html> Now render this page in portrait mode in iPhone, as you can see, it will be rendered normally as follows: Now if you render it in the landscape mode, the font size will suddenly increase. As we can see when the page is changed to landscape mode, the text will get resized. This is not the desired behavior. The following shows how it looks: How to do it... You can follow these steps to fix the issue: You can add the following lines to the CSS, and then render the page in landscape again: html {  -webkit-text-size-adjust: none;} As You can see, the text now appears normal: How it works... To solve this issue, you have to add a CSS property named text-size-adjust in WebKit, and assign the value to none to prevent the auto adjust. Setting text-size-adjust to none solves the problem for mobile-specific websites, but if we render this on a desktop screen or other non-mobile browser, the desktop browser text zoom feature will be disabled. To prevent this accessibility issue, we can set text-size-adjust to 100% instead of none. So we can tweak this example to: html {  -webkit-text-size-adjust: 100%;} There's more... Apart from iPhone, other devices also have ways to add the text size adjust property. Windows Mobile Windows Mobile IE uses a different prefix. They originally added the WebKit prefix. The intent was adding support for the WebKit-specific property to make web developers' lives a bit easier by not having to add yet another vendor-prefixed CSS property to their pages to control how text was scaled. Even more specifically, they intuited that the most common use case for this property was to explicitly set it to none in order to tell the browser not to scale a particular section of the text. After hearing the community's feedback on this issue (and a couple of face-plants when they realized the broader implications of implementing other browser vendors' CSS properties) they've decided that it's best to only implement the -ms- prefixed version and not the -webkit- one. So to make the preceding example more complete, you can add: html {  -webkit-text-size-adjust: 100%;  -ms-text-size-adjust: 100%;} Making it future proof To make things more future proof, you can add one more line without any prefix, as follows: html {  -webkit-text-size-adjust: 100%;  -ms-text-size-adjust: 100%;  text-size-adjust: 100%;} px em, which is better? The common debate about using px versus em is less of a problem on mobile. Originally Yahoo! User Interface used ems for the reason that IE6 doesn't support page zoom with pixels. On mobile, there isn't such an issue, and even if we want the page to render well on desktop browsers, the likelihood of someone using IE6 is getting lower and lower, so in most cases, you can save the trouble of using ems and all the calculation, and choose instead to use pixels. Optimizing viewport width Target device: cross-browser Different mobile devices have different default mobile viewport widths. Refer to Appendix X for a complete list of default viewport widths for all mobile devices. If you leave it unset, in most cases, it will cause an unexpected result. For example, in an iPhone if the viewport width is left unset, it will be rendered as 980 px. Getting ready Let's create an HTML document and name it ch02r03.html. How to do it... Here is what we can do to optimize the viewport width: Add the following code to ch02r03.html and render it in your mobile browser: <!doctype html><html>  <head>  <meta charset="utf-8">    </head>  <body>    <header>      HTML5 Logo    </header>    <div id="main">    <h1>Lorem ipsum</h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.    </div>      </body></html> Here is how it's rendered by default: If we render this example, we can see that everything becomes extremely small. Now, let's set the viewport width to the device width: <!doctype html><html>  <head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width">    </head>  <body>    <header>      HTML5 Logo    </header>  <div id="main">  <h1>Lorem ipsum</h1><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>  </div>          </body></html> Now the content width uses the screen width and the text becomes readable: How it works... When we set viewport width to device-width, it will tell the browser not to scale the page to fix the device area. So for iPhone, the device-width is 320 px in portrait mode and 480 px in landscape mode. There's more... For some really old relic mobile browsers, the meta attribute isn't recognized. To deal with these browsers, you need to use: <meta name="HandheldFriendly" content="true"> This is used by older versions of Palm OS, AvantGo, and Blackberry. <meta name="MobileOptimized" content="320"> For Microsoft PocketPC, a MobileOptimized attribute was introduced. So the most complete code should look like: <meta name="HandheldFriendly" content="true"><meta name="MobileOptimized" content="320"><meta name="viewport" content="width=device-width"> IE for Windows Phone viewport blog post On IE for Windows Phone Team Weblog, there is an article about IE Mobile Viewport on Windows Phone 7. In it, the author has explained important information like how Windows Phone 7 implements "device-width", together with much other very useful information in general. You can read the article here: http://blogs.msdn.com/b/iemobile/ archive/2010/11/22/the-ie-mobile-viewport-on-windows-phone-7.aspx Safari documentation Safari has a reference in the developer's library at:http://developer.apple. com/library/safari/#documentation/appleapplications/reference/ SafariHTMLRef/Articles/MetaTags.html Blackberry documentation There is a Blackberry Browser Content Design Guidelines document. It explains Blackberry's use of viewport width: http://docs.blackberry.com/en/developers/ deliverables/4305/BlackBerry_Browser-4.6.0-US.pdf. Fixing Mobile Safari screen re-flow scale Target device: iOS Mobile Safari has an annoying screen re-flow bug: When you rotate the mobile browser from portrait mode to landscape mode, the text will suddenly jump to a bigger size. During the time I was working on building Mobile Boilerplate, Jeremy Keith and I had a long discussion on this issue. The traditional way of fixing this is to add the following scaling attributes to the meta viewport: <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> This solution was first incorporated into Mobile Boilerplate. Jeremy pointed out that this solves the scale jump problem, but at the same time, it causes another issue with accessibility: When you set the values as shown above, you can no longer zoom the page to enlarge it. For people with eyesight problems, the ability to zoom is essential. But if we let the zoom happen, the text jump will annoy the majority of the users. So, for a long time it was an accessibility versus usability debate. I discovered a method that could tackle the issue and we will discuss this next. Getting ready First, let's create an HTML document and name it ch02r04.html, enter the following code in it: <!doctype html><html>  <head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width,  initial-scale=1.0">  </head>  <body>      <div>  <h1>Lorem ipsum</h1><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>  </div>  </body></html> This page renders perfectly fine in portrait mode: But when displayed in landscape mode, things change: How to do it... All we need to do to solve this problem is to dynamically reset the scale factors to default when the user zooms in on the page. Now put the following code in your HTML document: <!doctype html><html>  <head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width,    initial-scale=1.0">  </head>  <body>          <div>  <h1>Lorem ipsum</h1><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>  </div>    <script>      var metas = document.getElementsByTagName      ('meta');      var i;      if (navigator.userAgent.match(/iPhone/i)) {        for (i=0; i<metas.length; i++) {          if (metas[i].name == "viewport") {            metas[i].content = "width=device-width,             minimum-scale=1.0, maximum-scale=1.0";          }        }        document.addEventListener("gesturestart",         gestureStart, false);      }      function gestureStart() {        for (i=0; i<metas.length; i++) {          if (metas[i].name == "viewport") {            metas[i].content = "width=device-width,             minimum-scale=0.25, maximum-scale=1.6";          }        }      }    </script>  </body></html> Now if we rotate the screen from portrait to landscape, the issue should no longer exist, and if we zoom in on the page, it will react as expected: How it works... Let's have a walk through of the code to see how it works. We need to know the default minimum-scale and maximum-scale values. In the iPhone's official documentation, it states that the minimum-scale value is 0.25, and the maximum-scale value is 1.6. So to replace the default value, we need to set: function gestureStart() {  var metas = document.getElementsByTagName ('meta');  var i;  for (i=0; i if (metas[i].name == "viewport") {    metas[i].content = "width=device-width,     minimum-scale=0.25, maximum-scale=1.6";  }} Next, we need to know when to set this. This is very easy: The iPhone has a gesture event listener we can use to target the document body. Here is how to do so: document.addEventListener("gesturestart", gestureStart, false); Finally, we need to make sure this only happens on iPhone. Again this can be easily done using: if (navigator.userAgent.match(/iPhone/i)) {document.addEventListener("gesturestart", gestureStart, false);} There's more... If you are interested to know the whole story and discussion between Jeremy and I, you can read it at http://www.blog.highub.com/mobile-2/a-fix-for-iphone-viewportscale- bug/.Even though this provides a fix for the issue, there are a couple of problems that some people experience: As soon as the user makes a gesture on the document, zooming is enabled again. So if you change the device orientation after that, the zoom bug will still occur. It's reported on iOS4 that users can only effectively start zooming after starting a second gesture. A slightly improved version Mathias Bynens has a revised version with smarter coding. You can see the code at: https://gist.github.com/901295 An even better version John-David Dalton had an even better updated version with smarter and leaner code at:https://gist.github.com/903131 A word for jQuery Mobile Scott Jehl from jQuery Mobile mentioned it might be implemented in jQuery Mobile in the future. Currently, you could see his gist at: https://gist.github.com/1183357
Read more
  • 0
  • 0
  • 1568
article-image-sencha-touch-catering-form-related-needs
Packt
28 Dec 2011
14 min read
Save for later

Sencha Touch: Catering Form Related Needs

Packt
28 Dec 2011
14 min read
(For more resources on Sencha Touch, see here.) Most of the useful applications not only present the data, but also accept inputs from their users. When we think of having a way to accept inputs from the user, send them to the server for further processing, and allow the user to modify them, we think of forms and the form fields. If our application requires users to enter some information, then we go about using the HTML form fields, such as <input>, <select>, and so on, and wrap inside a <form> element. Sencha Touch uses these tags and provides convenient JavaScript classes to work with the form and its fields. It provides field classes such as Url, Toggle, Select, Text, and so on. Each of these classes provides properties to initialize the field, handle the events, and utility methods to manipulate the behavior and the values of the field. On the other side, the form takes care of the rendering of the fields and also handles the data submission. Each field can be created by using the JSON notation (JavaScript Object Notation—http://www.json.org) or by creating an instance of the class. For example, a text field can either be constructed by using the following JSON notation: { xtype: 'textfield', name: 'text', label: 'My Text' } Alternatively, we can use the following class constructor: var txtField = new Ext.form.Text({ name: 'text', label: 'My Text' }); The first approach relies on xtype, which is a type assigned to each of the Sencha Touch components. It is used as shorthand for the class. The basic difference between the two is that the xtype approach is more for the lazy initialization and rendering. The object is created only when it is required. In any application, we would use a combination of these two approaches. In this article, we will go through all the form fields and understand how to make use of them and learn about their specific behaviors. In addition, we will see how to create a form using one or more form fields and handle the form validation and submission. Getting your form ready with FormPanel This recipe shows how to create a basic form using Sencha Touch and implement some of the behaviors such as submitting the form data, handling errors during the submission, and so on. Getting ready Make sure that you have set up your development environment How to do it... Carry out the following steps: Create a ch02 folder in the same folder where we had created the ch01 folder. Create and open a new file named ch02_01.js and paste the following code into it: Ext.setup({ onReady: function() { var form; //form and related fields config var formBase = { //enable vertical scrolling in case the form exceeds the page height scroll: 'vertical', url: 'http://localhost/test.php', items: [{//add a fieldset xtype: 'fieldset', title: 'Personal Info', instructions: 'Please enter the information above.', //apply the common settings to all the child items of the fieldset defaults: { required: true, //required field labelAlign: 'left', labelWidth: '40%' }, items: [ {//add a text field xtype: 'textfield', name : 'name', label: 'Name', useClearIcon: true,//shows the clear icon in the field when user types autoCapitalize : false }, {//add a password field xtype: 'passwordfield', name : 'password', label: 'Password', useClearIcon: false }, { xtype: 'passwordfield', name : 'reenter', label: 'Re-enter Password', useClearIcon: true }, {//add an email field xtype: 'emailfield', name : 'email', label: 'Email', placeHolder: 'you@sencha.com', useClearIcon: true }] } ], listeners : { //listener if the form is submitted, successfully submit : function(form, result){ console.log('success', Ext.toArray(arguments)); }, //listener if the form submission fails exception : function(form, result){ console.log('failure', Ext.toArray(arguments)); } }, //items docked to the bottom of the form dockedItems: [ { xtype: 'toolbar', dock: 'bottom', items: [ { text: 'Reset', handler: function() { form.reset(); //reset the fields } }, { text: 'Save', ui: 'confirm', handler: function() { //submit the form data to the url form.submit(); } } ] } ] }; if (Ext.is.Phone) { formBase.fullscreen = true; } else { //if desktop Ext.apply(formBase, { autoRender: true, floating: true, modal: true, centered: true, hideOnMaskTap: false, height: 385, width: 480 }); } //create form panel form = new Ext.form.FormPanel(formBase); form.show(); //render the form to the body } });   Include the following line in index.html: <script type="text/javascript" charset="utf-8" src="ch02/ch02_01.js"></script> Deploy and access it from the browser. You will see the following screen: How it works... The code creates a form panel, with a field set inside it. The field set has four fields specified as part of its child items. xtype mentioned for each field instructs the Sencha Touch component manager which class to use to instantiate them. form = new Ext.form.FormPanel(formBase) creates the form and the other field components using the config defined as part of the formBase. form.show() renders the form to the body and that is how it will appear on the screen. url contains the URL where the form data will be posted upon submission. The form can be submitted in the following two ways: By hitting Go, on the virtual keyboard or Enter on a field that ends up generating the action event. By clicking on the Save button, which internally calls the submit() method on the form object. form.reset() resets the status of the form and its fields to the original state. Therefore, if you had entered the values in the fields and clicked on the Reset button, all the fields would be cleared. form.submit() posts the form data to the specified url. The data is posted as an Ajax request using the POST method. Use of useClearIcon on the field instructs Sencha Touch whether it should show the clear icon in the field when the user starts entering a value in it. On clicking on this icon, the value in the field is cleared. There's more... In the preceding code, we saw how to construct a form panel, add fields to it, and handle events. We will see what other non-trivial things we may have to do in the project and how we can achieve these using Sencha Touch. Standard submit This is the old and traditional way for form data posting to the server url. If your application need is to use the standard form submit, rather than Ajax, then you will have to set standardSubmit to true on the form panel. This is set to false, by default. The following code snippet shows the usage of this property: var formBase = { scroll: 'vertical', standardSubmit: true, ... After this property is set to true on the FormPanel, form.submit() will load the complete page specified in url. Do not submit on field action As we saw earlier, the form data is automatically posted to the url if the action event (when the Go or Enter key is hit) occurs. In many applications, this default feature may not be desirable. In order to disable this feature, you will have to set submitOnAction to false on the form panel. Post-submission handling Say we posted our data to the url. Now, either the call may fail or it may succeed. In order to handle these specific conditions and act accordingly, we will have to pass additional config options to the form's submit() method. The following code shows the enhanced version of the submit call: form.submit({ success: function(form, result) { Ext.Msg.alert("INFO", "Form submitted!"); }, failure: function(form, result) { Ext.Msg.alert("INFO", "Form submission failed!"); } }); If the Ajax call (to post form data) fails, then the failure callback function is called, and in the case of success, the success callback function is called. This works only if the standardSubmit is set to false. Working with search In this and the subsequent recipes of the article, we will go over each of the form fields and understand how to work with them. This recipe describes the steps required to create and use a search form field. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe. How to do it... Carry out the following steps: Copy ch02_01.js to ch02_02.js. Open a new file named ch02_02.js and replace the definition of formBase with the following code: var formBase = { items: [{ xtype: 'searchfield', name: 'search', label: 'Search' }] }; Include ch02_02.js in place of ch02_01.js in index.html. Deploy and access the application in the browser. You will see a form panel with a search field. How it works... The search field can be constructed by using the Ext.form.Search class instance or by using the xtype—searchfield. The search form field implements HTML5 <input> with type="search". However, the implementation is very limited. For example, the HTML5 search field allows us to associate a data list to it which it can use during the search, whereas this feature is not present in Sencha Touch. Similarly, the W3 search field spec defines a pattern attribute to allow us to specify a regular expression against which a User Agent is meant to check the value, which is not supported yet in Sencha Touch. For more detail, you may refer to the W3 search field (http://www.w3.org/TR/html-markup/input.search.html) and the source code of the Ext.form.Search class. There's more... Often, in the application, for the search fields we do not use a label. Rather, we would like to show a text, such as Search inside the field that will disappear when the focus is on the field. Let's see how we can achieve this. Using a placeholder Placeholders are supported by most of the form fields in Sencha Touch by using the property placeHolder. The placeholder text appears in the field as long as there is no value entered in it and the field does not have the focus. The following code snippet shows the typical usage of it: { xtype: 'searchfield', name: 'search', label: 'Search', placeHolder: 'Search...' } Putting custom validation in the e-mail field This recipe describes how to make use of the e-mail form field provided by Sencha Touch and how to validate the value entered into it to find out whether the entered e-mail passes the validation rule or not. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe in this article. How to do it... Carry out the following steps: Copy ch02_01.js to ch02_03.js. Open a new file named ch02_03.js and replace the definition of formBase with the following code: var formBase = { items: [{ xtype: 'emailfield', name : 'email', label: 'Email', placeHolder: 'you@sencha.com', useClearIcon: true, listeners: { blur: function(thisTxt, eventObj) { var val = thisTxt.getValue(); //validate using the pattern if (val.search("[a-c]+@[a-z]+[.][a-z]+") == -1) Ext.Msg.alert("Error", "Invalid e-mail address!!"); else Ext.Msg.alert("Info", "Valid e-mail address!!"); } } }] }; Include ch02_03.js in place of ch02_02.js in index.html. Deploy and access the application in the browser. How it works... The e-mail field can be constructed by using the Ext.form.Email class instance or by using the xtype: emailfield. The e-mail form field implements HTML5 <input> with type="email". However, as with the search field, the implementation is very limited. For example, the HTML5 e-mail field allows us to specify a regular expression pattern which can be used to validate the value entered in the field. Working with dates using DatePicker This recipe describes how to make use of the date picker form field provided by Sencha Touch which allows the user to select a date. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe in this article. How to do it... Carry out the following steps: Copy ch02_01.js to ch02_04.js. Open a new file named ch02_04.js and replace the definition of formBase with the following code: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date' }] }; Include ch02_04.js in place of ch02_03.js in index.html. Deploy and access the application in the browser. How it works... The date picker field can be constructed by using the Ext.form.DatePicker class instance or by using xtype: datepickerfield. The date picker form field implements HTML <select>. When the user tries to select an entry, it shows the date picker with the month, day, and year slots for selection. After selection, when the user clicks on the Done button, the field is set with the selected value. There's more... Additionally, there are other things that can be done such as setting the date to the current date, or any particular date, or changing the order of appearance of a month, day, and year. Let's see what it takes to accomplish this. Setting the default date to the current date In order to set the default value to the current date, the value property must be set to the current date. The following code shows how to do it: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', value: new Date(), Setting the default date to a particular date The default date is 01/01/1970. Let's assume that you need to set the date to a different date, but not the current date. To do so, you will have to set the value using the year, month, and day properties, as follows: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', value: {year: 2011, month: 6, day: 11}, ... Changing the slot order By default, the slot order is month, day, and year. You can change it by setting the slotOrder property of the picker property of date picker, as shown in the following code: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', picker: {slotOrder: ['day', 'month', 'year']} }] }; Setting the picker date range By default, the date range shown by the picker is 1970 until the current year. For our application need, if we have to alter the year range, we can do so by setting the yearFrom nd yearTo properties of the picker property of the date picker, as follows: var formBase = { items: [{ xtype: 'datepickerfield', name: 'date', label: 'Date', picker: {yearFrom: 2000, yearTo: 2010} }] }; Making a field hidden Often in an application, there would be a need to hide fields which are not needed in a particular context but are required and hence need to be shown in another. In this recipe, we will see how to make a field hidden and show it, conditionally. Getting ready Make sure that you have set up your development environment. Make sure that you have followed the Getting your form ready with FormPanel recipe in this article. How to do it... Carry out the following steps: Edit ch02_04.js and modify the code as follows by adding the hidden property: var formBase = { items: [{ xtype: 'datepickerfield', id: 'datefield-id', name: 'date', hidden: true, label: 'Date'}] }; Deploy and access the application in the browser. How it works... When a field is marked as hidden, Sencha Touch uses the DOM's hide method on the element to hide that particular field. There's more... Let's see how we can programmatically show/hide a field. Showing/Hiding a field at runtime Each component in Sencha Touch supports two methods: show and hide. The show method shows the element and hide hides the element. In order to call these methods, we will have to first find the reference to the component, which can be achieved by either using the object reference or by using the Ext.getCmp() method. Given a component ID, the getCmp method returns us the component. The following code snippet demonstrates how to show an element: var cmp = Ext.getCmp('datefield-id'); cmp.show(); To hide an element, we will have to call cmp.hide();
Read more
  • 0
  • 0
  • 2704

article-image-appcelerator-titanium-creating-animations-transformations-and-understanding-drag-and-d
Packt
22 Dec 2011
10 min read
Save for later

Appcelerator Titanium: Creating Animations, Transformations, and Understanding Drag-and-drop

Packt
22 Dec 2011
10 min read
(For more resources related to this subject, see here.) Animating a View using the "animate" method Any Window, View, or Component in Titanium can be animated using the animate method. This allows you to quickly and confidently create animated objects that can give your applications the "wow" factor. Additionally, you can use animations as a way of holding information or elements off screen until they are actually required. A good example of this would be if you had three different TableViews but only wanted one of those views visible at any one time. Using animations, you could slide those tables in and out of the screen space whenever it suited you, without the complication of creating additional Windows. In the following recipe, we will create the basic structure of our application by laying out a number of different components and then get down to animating four different ImageViews. These will each contain a different image to use as our "Funny Face" character. Complete source code for this recipe can be found in the /Chapter 7/Recipe 1 folder. Getting ready To prepare for this recipe, open up Titanium Studio and log in if you have not already done so. If you need to register a new account, you can do so for free directly from within the application. Once you are logged in, click on New Project, and the details window for creating a new project will appear. Enter in FunnyFaces as the name of the app, and fill in the rest of the details with your own information. Pay attention to the app identifier, which is written normally in reverse domain notation (that is, com.packtpub.funnyfaces). This identifier cannot be easily changed after the project is created and you will need to match it exactly when creating provisioning profiles for distributing your apps later on. The first thing to do is copy all of the required images into an images folder under your project's Resources folder. Then, open the app.js file in your IDE and replace its contents with the following code. This code will form the basis of our FunnyFaces application layout. // this sets the background color of the master UIView Titanium.UI.setBackgroundColor('#fff');////create root window//var win1 = Titanium.UI.createWindow({ title:'Funny Faces', backgroundColor:'#fff'});//this will determine whether we load the 4 funny face//images or whether one is selected alreadyvar imageSelected = false;//the 4 image face objects, yet to be instantiatedvar image1;var image2;var image3;var image4;var imageViewMe = Titanium.UI.createImageView({ image: 'images/me.png', width: 320, height: 480, zIndex: 0 left: 0, top: 0, zIndex: 0, visible: false});win1.add(imageViewMe);var imageViewFace = Titanium.UI.createImageView({ image: 'images/choose.png', width: 320, height: 480, zIndex: 1});imageViewFace.addEventListener('click', function(e){ if(imageSelected == false){ //transform our 4 image views onto screen so //the user can choose one! }});win1.add(imageViewFace);//this footer will hold our save button and zoom slider objectsvar footer = Titanium.UI.createView({ height: 40, backgroundColor: '#000', bottom: 0, left: 0, zIndex: 2});var btnSave = Titanium.UI.createButton({ title: 'Save Photo', width: 100, left: 10, height: 34, top: 3});footer.add(btnSave);var zoomSlider = Titanium.UI.createSlider({ left: 125, top: 8, height: 30, width: 180});footer.add(zoomSlider);win1.add(footer);//open root windowwin1.open(); Build and run your application in the emulator for the first time, and you should end up with a screen that looks just similar to the following example: How to do it… Now, back in the app.js file, we are going to animate the four ImageViews which will each provide an option for our funny face image. Inside the declaration of the imageViewFace object's event handler, type in the following code: imageViewFace.addEventListener('click', function(e){ if(imageSelected == false){ //transform our 4 image views onto screen so //the user can choose one! image1 = Titanium.UI.createImageView({ backgroundImage: 'images/clown.png', left: -160, top: -140, width: 160, height: 220, zIndex: 2 }); image1.addEventListener('click', setChosenImage); win1.add(image1); image2 = Titanium.UI.createImageView({ backgroundImage: 'images/policewoman.png', left: 321, top: -140, width: 160, height: 220, zIndex: 2 }); image2.addEventListener('click', setChosenImage); win1.add(image2); image3 = Titanium.UI.createImageView({ backgroundImage: 'images/vampire.png', left: -160, bottom: -220, width: 160, height: 220, zIndex: 2 }); image3.addEventListener('click', setChosenImage); win1.add(image3); image4 = Titanium.UI.createImageView({ backgroundImage: 'images/monk.png', left: 321, bottom: -220, width: 160, height: 220, zIndex: 2 }); image4.addEventListener('click', setChosenImage); win1.add(image4); image1.animate({ left: 0, top: 0, duration: 500, curve: Titanium.UI.ANIMATION_CURVE_EASE_IN }); image2.animate({ left: 160, top: 0, duration: 500, curve: Titanium.UI.ANIMATION_CURVE_EASE_OUT }); image3.animate({ left: 0, bottom: 20, duration: 500, curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT }); image4.animate({ left: 160, bottom: 20, duration: 500, curve: Titanium.UI.ANIMATION_CURVE_LINEAR }); }}); Now launch the emulator from Titanium Studio and you should see the initial layout with our "Tap To Choose An Image" view visible. Tapping the choose ImageView should now animate our four funny face options onto the screen, as seen in the following screenshot: How it works… The first block of code creates the basic layout for our application, which consists of a couple of ImageViews, a footer view holding our "save" button, and the Slider control, which we'll use later on to increase the zoom scale of our own photograph. Our second block of code is where it gets interesting. Here, we're doing a simple check that the user hasn't already selected an image using the imageSelected Boolean, before getting into our animated ImageViews, named image1, image2, image3, and image4. The concept behind the animation of these four ImageViews is pretty simple. All we're essentially doing is changing the properties of our control over a period of time, defined by us in milliseconds. Here, we are changing the top and left properties of all of our images over a period of half a second so that we get an effect of them sliding into place on our screen. You can further enhance these animations by adding more properties to animate, for example, if we wanted to change the opacity of image1 from 50 percent to 100 percent as it slides into place, we could change the code to look something similar to the following: image1 = Titanium.UI.createImageView({ backgroundImage: 'images/clown.png', left: -160, top: -140, width: 160, height: 220, zIndex: 2, opacity: 0.5});image1.addEventListener('click', setChosenImage);win1.add(image1);image1.animate({ left: 0, top: 0, duration: 500, curve: Titanium.UI.ANIMATION_CURVE_EASE_IN, opacity: 1.0}); Finally, the curve property of animate() allows you to adjust the easing of your animated component. Here, we used all four animation-curve constants on each of our ImageViews. They are: Titanium.UI.ANIMATION_CURVE_EASE_IN: Accelerate the animation slowly Titanium.UI.ANIMATION_CURVE_EASE_OUT: Decelerate the animation slowly Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT: Accelerate and decelerate the animation slowly Titanium.UI.ANIMATION_CURVE_LINEAR: Make the animation speed constant throughout the animation cycles Animating a View using 2D matrix and 3D matrix transforms You may have noticed that each of our ImageViews in the previous recipe had a click event listener attached to them, calling an event handler named setChosenImage. This event handler is going to handle setting our chosen "funny face" image to the imageViewFace control. It will then animate all four "funny face" ImageView objects on our screen area using a number of different 2D and 3D matrix transforms. Complete source code for this recipe can be found in the /Chapter 7/Recipe 2 folder. How to do it… Replace the existing setChosenImage function, which currently stands empty, with the following source code: //this function sets the chosen image and removes the 4//funny faces from the screenfunction setChosenImage(e){ imageViewFace.image = e.source.backgroundImage; imageViewMe.visible = true; //create the first transform var transform1 = Titanium.UI.create2DMatrix(); transform1 = transform1.rotate(-180); var animation1 = Titanium.UI.createAnimation({ transform: transform1, duration: 500, curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT }); image1.animate(animation1); animation1.addEventListener('complete',function(e){ //remove our image selection from win1 win1.remove(image1); }); //create the second transform var transform2 = Titanium.UI.create2DMatrix(); transform2 = transform2.scale(0); var animation2 = Titanium.UI.createAnimation({ transform: transform2, duration: 500, curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT }); image2.animate(animation2); animation2.addEventListener('complete',function(e){ //remove our image selection from win1 win1.remove(image2); }); //create the third transform var transform3 = Titanium.UI.create2DMatrix(); transform3 = transform3.rotate(180); transform3 = transform3.scale(0); var animation3 = Titanium.UI.createAnimation({ transform: transform3, duration: 1000, curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT }); image3.animate(animation3); animation3.addEventListener('complete',function(e){ //remove our image selection from win1 win1.remove(image3); }); //create the fourth and final transform var transform4 = Titanium.UI.create3DMatrix(); transform4 = transform4.rotate(200,0,1,1); transform4 = transform4.scale(2); transform4 = transform4.translate(20,50,170); //the m34 property controls the perspective of the 3D view transform4.m34 = 1.0/-3000; //m34 is the position at [3,4] //in the matrix var animation4 = Titanium.UI.createAnimation({ transform: transform4, duration: 1500, curve: Titanium.UI.ANIMATION_CURVE_EASE_IN_OUT }); image4.animate(animation4); animation4.addEventListener('complete',function(e){ //remove our image selection from win1 win1.remove(image4); }); //change the status of the imageSelected variable imageSelected = true;} How it works… Again, we are creating animations for each of the four ImageViews, but this time in a slightly different way. Instead of using the built-in animate method, we are creating a separate animation object for each ImageView, before calling the ImageView's animate method and passing this animation object to it. This method of creating animations allows you to have finer control over them, including the use of transforms. Transforms have a couple of shortcuts to help you perform some of the most common animation types quickly and easily. The image1 and image2 transforms, as shown in the previous code, use the rotate and scale methods respectively. Scale and rotate in this case are 2D matrix transforms, meaning they only transform the object in two-dimensional space along its X-axis and Y-axis. Each of these transformation types takes a single integer parameter; for scale, it is 0-100 percent and for rotate, the number of it is 0-360 degrees. Another advantage of using transforms for your animations is that you can easily chain them together to perform a more complex animation style. In the previous code, you can see that both a scale and a rotate transform are transforming the image3 component. When you run the application in the emulator or on your device, you should notice that both of these transform animations are applied to the image3 control! Finally, the image4 control also has a transform animation applied to it, but this time we are using a 3D matrix transform instead of the 2D matrix transforms used for the other three ImageViews. These work the same way as regular 2D matrix transforms, except that you can also animate your control in 3D space, along the Z-axis. It's important to note that animations have two event listeners: start and complete. These event handlers allow you to perform actions based on the beginning or ending of your animation's life cycle. As an example, you could chain animations together by using the complete event to add a new animation or transform to an object after the previous animation has finished. In our previous example, we are using this complete event to remove our ImageView from the Window once its animation has finished.
Read more
  • 0
  • 0
  • 6530