[L4] Cross-platform - Titanium UI
Transcript
[L4] Cross-platform - Titanium UI
Università Degli Studi di Parma Distributed Systems Group Cross-platform Programming Lecture 4 Titanium User Interface Alessandro Grazioli http://dsg.ce.unipr.it/?q=node/37 http://dsg.ce.unipr.it/ Alessandro Grazioli [email protected] 2015 - Parma Università Degli Studi di Parma Distributed Systems Group Overview User Interface elements ‣ View ‣ Window ‣ Animation ‣ Label ‣ WebView ‣ Picker ‣ TableView, TableViewRow and SearchBar ‣ ListView ‣ ProgressBar ‣ ScrollView ‣ Tab and TabGroup Alessandro Grazioli ‣ Forms ‣ Switch ‣ Slider ‣ ListView, ListSection, ListItem ‣ ImageView ‣ EmailDialog ‣ Button ‣ AlertDialog ‣ Icons and splash screen ‣ Hiding the title bar on Android 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Elements - UI elements are defined in Titanium.Proxy → Titanium.Module → Titanium.UI ‣ Titanium.Proxy is the base for all Titanium objects • ‣ A proxy represents a wrapper around a native object and thus, calling a method on a proxy object results in a method invocation on a native object Titanium.Module is the base for all Titanium modules - The UI module is responsible for native user-interface components and interaction among them - You can test all UI elements from Appcelerator KitchenSink app available at: The goal of the UI module is to provide a native experience along with native performance by compiling JavaScript code into their native counterparts https://github.com/appcelerator/KitchenSink Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Module - The UI module is divided into 3 major areas: ‣ Views ‣ Windows ‣ Controls - UI objects are optimized not to be created into the native drawing context and placed into the device UI surface until actually needed - i.e., they are drawn just when it is necessary and not as soon as you define them - Titanium is optimized to also release memory once a view is no longer needed on-screen Be careful with views defined in app.js because it is a global-scope file so objects defined in it are not garbage-collected until the application exits Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI elements portability - Titanium components are designed to be portable across as many platforms as it supports However, there are cases where a device does not support a specific feature or capability - - For example: • Notifications are Android-only toast pop-up messages • Coverflow is an iOS-only presentation view There are also cases where a device supports additional functionality ‣ Such capabilities are usually placed in a separate namespace, such as Titanium.UI.iPhone ‣ E.g., iOS natively supports animations when closing a window, so you can specify an available effect such us the curl-up window.close({transition: Titanium.UI.iPhone.AnimationStyle.CURL_UP}); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Views - Views are defined in Titanium.Proxy → Titanium.UI.View - Views can have their properties customized, such as their border’s color and radius View is the base type for all UI widgets in Titanium Views are containers that host visual elements, such as controls or other views, in a hierarchical structure of children Views can fire events, such as swipe or touch Titanium provides a set of specialized views which are meant to perform both visual and interactive functions, such as TableView or CoverflowView ‣ Specialized views are always named with the suffix View Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Events - Events are actions that can be detected by JavaScript Such actions might be user-initiated, like taps or swipes, or system-initiated, like when an app is paused Views can fire, listen and react to events You specify which components in your app should listen for events with the addEventListener() method var self = Ti.UI.createView({ backgroundColor :'#ffffff' }); var v = Ti.UI.createView({ backgroundColor:'red', width :'500', height :'500' }); self.add(v); function eventOccurred (e) { Ti.API.info('Event ' + e.type + ' occurred'); } v.addEventListener('click', eventOccurred); Alessandro Grazioli Add a View to another View addEventListener()‘s first parameter specifies the type of event for which the View is listening. The second parameter is a callback function executed whenever the event is fired. Within it, an event object e is created. It has a list of properties which depends on the event. Event properties are listed on the API docs page for any given module or component. 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Events - Common event types include click, swipe (a touch and drag action), scroll, dblclick and its synonym doubletap, touchstart, touchmove, touchend - In addition to such common event types, some modules have their own special events ‣ The location services API includes events for heading and location changes ‣ The gestures module enables listening for the shake event - Common event properties include ‣ x and y which describe the (x, y) coordinates of the event in the view's coordinates (i.e. distance from the top-left corner of the view which generated the event) ‣ type which is the name of the event type ‣ source which is a reference to the object on which the event was fired Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Event bubbling UI events can bubble up (i.e. propagate) from the view that was actually touched through parent views var self = Ti.UI.createView({ backgroundColor :'#ffffff', my_name : 'self' }); var parent = Ti.UI.createView({ backgroundColor:'red', width :'500', height :'500', my_name : 'parent' }); var child= Ti.UI.createView({ backgroundColor:'red', width :'250', height :'250', my_name : 'child' }); This means that the event propagates from the innermost (the top view) to the outermost (the container of all the other views) element in the hierarchy Running the example on the right and clicking on child View, all handlers functions are executed in the order child - parent - self (i.e., from the innermost to the outermost) self.add(parent); self.add(child); The source is always the innermost View Alessandro Grazioli function eventOccurredParent (e) { Ti.API.info('Parent: event generated by ' + e.source.my_name); } function eventOccurredChild (e) { Ti.API.info('Child: event generated by ' + e.source.my_name); } function eventOccurredSelf (e) { Ti.API.info('Self: event generated by ' + e.source.my_name); } parent.addEventListener('click', eventOccurredParent); child.addEventListener('click', eventOccurredChild); self.addEventListener('click', eventOccurredSelf); 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Event bubbling Events that represent user input are bubbling events The following events bubble: click, dblclick, doubletap, longclick, longpress, pinch, singletap, swipe, touchcancel, touchend, touchmove, touchstart, twofingertap Other events, such as focus and scroll, are view-specific. They represent views reacting to user input, and they do not bubble The example on the right defines a ScrollView and adds to it a View and a TableView. You can independently scroll the TableView or the whole ScrollView because TableView’s scroll event do not bubble to the ScrollView Alessandro Grazioli var self = Ti.UI.createView({ backgroundColor: '#ffffff' }); var v = Ti.UI.createView({ backgroundColor: 'red', width: '500', height: '500' }); var scrollView = Titanium.UI.createScrollView({ layout:'vertical' }); scrollView.add(v); var tableData = []; for (var i = 0; i < 20; i++) { var tableViewRow = Ti.UI.createTableViewRow({ selectionStyle:'none', height:'7%' }); var tableViewRowView = Ti.UI.createView({ width: "100%", height: "100%", backgroundColor: "transparent" }); var tableViewRowViewLabel = Ti.UI.createLabel({ color: '#000', font: { fontSize:18 }, text: 'Row number ' + i }); tableViewRowView.add(tableViewRowViewLabel); tableViewRow.add(tableViewRowView); tableData.push(tableViewRow); } var table = Titanium.UI.createTableView({ data:tableData, width:'80%', height:'100%', backgroundColor:'transparent' }); scrollView.add(table); self.add(scrollView); 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Event firing - To fire an event, use fireEvent() function which takes the name of the event to be fired as parameter You can add, as a second parameter, a JSON-serializable list of data to be passed along with the event You can set bubbling by using bubbles boolean property view.fireEvent('event_name', { bubbles: false, custom_attribute: 'custom_value' }); - For example, you might fire a custom event when a database is updated ‣ Any components that depend on the database – a table, for example – could listen for that event and update themselves deleteButton.addEventListener('click', function(e) { // Delete a record identified by a custom property of the event source database.doDelete(e.whichRecord); // Fire an event to inform that the database has just changed theTable.fireEvent('db_updated'); }); theTable.addEventListener('db_updated', function(e) { // Update data for a TableView theTable.setData(database.getCurrentRecords()); }); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Event firing - Events can be local to a JS file or global to the app ‣ Events fired with view.fireEvent are only visible to elements defined in the JavaScript file where the event gets fired ‣ App-level events, fired as Ti.App.fireEvent('event_name', {custom_attribute: 'value'}), are instead global - To listen to global events, use Ti.App.AddEventListener('event_name', handler) Listeners to global events remain in scope the entire time your app is running ‣ Any local variable you reference within such listeners remains in memory while your app is running and this can cause memory leaks (objects you do not need anymore are not garbage-collected) ‣ Remember to remove global events listeners when you do not need them anymore Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Event listener removal - You can remove an event listener thus preventing the associated component from reacting to such an event in the future For example you may have a button that should only be active when one or more list items are checked ‣ When the user checks the first item, you can add the click event listener to the button ‣ If the user clears the last of the check marks, you can remove the event listener from the button. - To remove an event listener, you have to pass a reference to the handler to the function that was specified when you added the event listener ‣ The easiest way to do this is to use a named function in the addEventListener() statement so you can also pass that same function name to remove the listener function handlerFunction(e) { // Code executed by listeners when the event gets fired } view.addEventListener('event_name', handlerFunction}); view.removeEventListener('event_name', handlerFunction}); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Colors - Views have properties to control their color, such as backgroundColor and color - An additional alpha channel is supported as a prefix to the hex triplet (e.g., to make the orange-like color #FFAA00 semi-opaque, you could use an alpha value of 55, giving, #55FFAA00 or #5FA0) - iOS also accepts colors specified in the form rgb(R,G,B) and rgba(R,G,B,A), with the color channels represented by integer numbers between 0 and 255 and the alpha channel by a float number from 0 to 1.0 (transparent to opaque) - Alternatively, the following set of color names is recognized: Colors may be specified as a hex triplet to determine the red, green and blue channels such as #000000, #FF0000, #00FF00, #0000FF (a channel may be abbreviated when its two hex digits are identical, such as #F00, #0F0, #00F) 'transparent', 'aqua', 'black', 'blue', 'brown', 'cyan', 'darkgray', 'fuchsia', 'gray', 'green', 'lightgray', 'lime', 'magenta', 'maroon', 'navy', 'olive', 'orange', 'pink', 'purple', 'red', 'silver', 'teal', 'white', 'yellow' - On Android, if you want to create a semi-transparent window, set the opacity property before opening the window If a color property is undefined, the default color of the particular UI element is applied var view = Ti.UI.createView({ backgroundColor : rgba(255, 255, 255, 0.8), color : 'black' }); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI units & coordinates - - You can specify size and coordinates for a view as: ‣ a number with no unit (e.g., { width: 50 }) - in this case the unit is set as the default one for the system (pixels on Android and dips on iOS, mobile web and Tizen) ‣ a string consisting of a percentage referring to the view’s parent size - e.g., { width: '50%' } ‣ a string consisting of a number followed by a unit specifier - e.g., { width: '50px' } Supported units are: Unit Specifier pixels px Note On Android, one DIP corresponds to one pixel on a 160DPI display. On iOS, one DIP corresponds to one pixel on a non-Retina display, which is 163DPI for iPhone/iPod touch and 132DPI for the iPad. A DIP corresponds to 2 pixels of width or height on a Retina display density-independent pixels dip inches in millimiters mm Android, iOS only centimeters cm Android, iOS only points pt Typographical points of 1/72 of an inch Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI units & coordinates - On Android and iOS, the default unit can be overriden on a per-application level by setting the ti.ui.defaultunit property in tiapp.xml - For example, to use DIPs as the default on all platforms, set defaultunit property in ti.app element to dip <property name="ti.ui.defaultunit" type="string">dip</property> - If you set such a property you should always express sizes as numbers with no unit On iOS, font sizes are always treated as typographical points (i.e., pt) Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI units & coordinates - best practice - You should use density-independent pixels (dip) If you want to get the screen width and height, you should do the following ‣ set the ti.ui.defaultunit property to ti.app file <property name="ti.ui.defaultunit" type="string">dip</property> ‣ use Ti.Platform.displayCaps object to get the width and height of your screen ‣ However by default Ti.Platform.displayCaps returns the screen caps in pixels for Android and in dip for iOS ‣ So it is necessary to divide such values by the logical density factor only on Android to convert it to dip Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI units & coordinates - best practice - If you do not perform the division, a View set as large as the whole screen may not look properly on Android - displayCaps returns 1080 px for the width and 1776 px for the Nexus’ height, but since we set the default unit to dip, when setting the View size such values are considered as dip The image shows that, while an app is properly displayed on an iPhone 6, its size is 3 times bigger on a Nexus 5 since on that device 1 dip represents 3 pixels ‣ The View’s width is then set to 1776 dip, that is 5328 px, and its height is set to 1080 dip, that is 3240 px since 1 dip = 3 px Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI size and position - Views are positioned using the left, right, top, bottom and center properties, and sized using the width and height properties ‣ - - center allows to position the center of a View with respect to the top-left corner of its parent The height and width properties also accept special values: ‣ Titanium.UI.FILL specifies that the view should fill the parent in that dimension ‣ Titanium.UI.SIZE specifies that the view should adjust this size to fit its contents, such as a label's text or a view's children ‣ The string 'auto' specifies that the view should choose either Titanium.UI.FILL or Titanium.UI.SIZE based on the default behavior for the View type Sizes and positions can also be specified as a percentage of the parent's size as previously shown (i.e., { width: '50%' }) Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI size and position - The following example defines a Window including a red View which includes a child green View The center of the green View is positioned (x, y) pixels far from the top-left corner of parent’s View To center the child, use coordinates (x, y) = (childwidth, childheight) (a) To put the child in the top-left corner, use coordinates (x, y) = (childwidth / 2, childheight / 2) (b) var self = Ti.UI.createView({ backgroundColor:'#ffffff' }); var parent = Ti.UI.createView({ backgroundColor:'red', width: 100, height: 100 }); var child = Ti.UI.createView({ backgroundColor:'green', width: 50, height: 50, center:{x:60, y:60} }); parent.add(child); self.add(parent); Alessandro Grazioli 50 c 50 (a) (b) 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI size and position - Size and position properties are interpreted depending on the value of the view's layout property Such a property specifies how the view positions its children and assumes a value of: ‣ 'composite' or 'absolute': a child view is positioned based on its positioning properties (top, bottom, left, right and center) - if no positioning properties are specified, the child is centered ‣ 'vertical': children are laid out vertically from top to bottom. ‣ • The first child is laid out top units from its parent's bounding box and each subsequent child is laid out below the previous one. • The space between children is equal to the upper child's bottom value plus the lower child's top value 'horizontal': the children are laid out horizontally from left to right in rows. • If a child requires more horizontal space than exists in the current row, it is wrapped to a new row • The height of each row is equal to the maximum height of the children in that row Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI size and position - Two read-only dictionaries useful to get size and position information on a drawn View are: ‣ size: it has x, y, width and height properties and provides the rendered size and position of the view ‣ rect: has x, y, width and height properties and provides the rendered size and position of the bounding box of the view - Both are only available after the View has been fully drawn, so if you want to use them, you need to set a listener for postlayout event, which is fired at the end of a layout cycle var self = Ti.UI.createView({ backgroundColor: '#ffffff' }); var v = Ti.UI.createView({ backgroundColor: 'red', width : '100', height : '100' }); self.add(v); function printRect(e) { Ti.API.info(v.rect.x + ', ' + v.rect.y); } self.addEventListener('postlayout', printRect); Alessandro Grazioli Trying to print rect properties before View v has been drawn will return (0, 0) 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI size and position - - Mobile OS include an accessibility service to aid impaired people View provides 4 accessibility-related properties ‣ accessibilityLabel: a label identifying the view for the device's accessibility service ‣ accessibilityValue: a string describing the value (if any) of the view for the device's accessibility service ‣ accessibilityHint: briefly describes what performing an action (such as a click) on the view will do ‣ accessibilityHidden: whether the view should be ignored by the accessibility service The first 3 represent text that the accessibility service (such as TalkBack on Android and VoiceOver on iOS) reads to the user Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Window - Windows are top-level visual constructs that are the main part of the app’s UI (an app always has at least one window) A Window can ‣ take different shapes and sizes ‣ have display and interaction properties such as fullscreen or modal ‣ be customized, such as changing its opacity or background color - Windows themselves are Views and also inherit View’s properties, functions and events var newWin = Titanium.UI.createWindow({ backgroundColor : '#FFF' }); newWin.open(); - open method takes as optional parameter an opening animation ‣ You can use transform methods provided by Ti.UI.create2DMatrix() to define different animations such as scale or rotate Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group Opening and closing with scale animation function FirstWindow() { var self = Ti.UI.createWindow({ backgroundColor: '#FFF' }); - The opening animation is set to scale from 0% to 100% of second window’s size (param 0 of FirstWindow’s create2DMatrix method) - In SecondWindow’s createWindow method, the scaling is set to 0 so that the animation can occur var b = Ti.UI.createButton({ title: 'Close', backgroundColor: '#EEE', width: 100, height: 50, top: 100 }); ui/common/FirstWindow.js file Since the opening animation defined by FirstWindow is a scaling from 0 to 1, the initial scale value of the Window is set to 0 b.addEventListener('click', function() { var duration = 300; var animateIn = Ti.UI.createAnimation({ transform: Ti.UI.create2DMatrix.scale(1), duration: duration }); Specify a scaling to 100% animation function SecondWindow() { var self = Ti.UI.createWindow({ backgroundColor: '#FFF', transform: Ti.UI.create2DMatrix.scale(0) }); var Window = require('ui/common/SecondWindow'); new Window().open(animateIn); }); return self; } self.add(b); Specify the opening animation ui/common/SecondWindow.js file module.exports = SecondWindow; return self; } module.exports = FirstWindow; Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Window - Window objects can also be opened without referring to a CommonJS module with the url property pointing to the JS file with the Window’s structure var w = Ti.UI.currentWindow; var v = Ti.UI.createView({ backgroundColor : 'white', width : '100%', height : '100%', layout : 'vertical' }); var newWin = Titanium.UI.createWindow({ url: 'ui/common/new_win.js' }); newWin.open(); - - Such Window object runs in a separate JavaScript context and thread var b1 = Ti.UI.createButton({ title : 'Click Me', backgroundColor : '#EEE', width : 100, height : 50, top : 20 }); ‣ This provides a way to decompose your application into smaller units ‣ When the window is closed, resources allocated in the window's context gets cleaned up, saving memory and CPU cycles b1.addEventListener('click', function() { Ti.API.info('Clicked button'); }); When you DON'T use the url argument you have a single-context application (variables are available globally as long as they are in scope different windows defined in the same file can access file-global variables) var b2 = Ti.UI.createButton({ title : 'Close', backgroundColor : '#EEE', width : 100, height : 50, top : 2 }); When you DO use the url argument you have a multi-context application variables are only available within their own context (i.e., window), and passing data between windows must be done via custom events or a shared resource such as a database or app properties Alessandro Grazioli b2.addEventListener('click', function() { w.close(); }); v.add(b1); v.add(b2); w.add(v); new_win.js file 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Animations - Views can be animated by using the Animation object Animations can be set to reverse themselves automatically on completion, and to repeat a given number of times Multiple animations can be combined in sequence, starting the following when the previous completes Use the Titanium.UI.createAnimation method to create an animation object iOS supports both 2D and 3D matrix transformations in animations and you can also specify an animation curve (or easing) function to control the pace of the animation by setting the animation's curve property to one of the ANIMATION_CURVE constants defined in Titanium.UI. ‣ - E.g., ANIMATION_CURVE_EASE_IN specifies an animation that starts slowly and then speeds up iOS also supports transitions between windows or views Android supports 2D matrix transformations The example defines a 2D matrix that rotates translates and scales both horizontally and vertically a View in2 seconds. The View’s opacity is also reduced to 60% during the animation. Alessandro Grazioli var box = Ti.UI.createView({ backgroundColor : 'red', height : '100', width : '100' }); win.add(box); box.addEventListener('click', function() { var matrix = Ti.UI.create2DMatrix() matrix = matrix.rotate(180).translate(10,10).scale(2, 2); var a = Ti.UI.createAnimation({ transform : matrix, duration : 2000, opacity : 0.6, autoreverse : true, repeat : 3 }); box.animate(a); }); 2015 - Parma Università Degli Studi di Parma Distributed Systems Group CommonJS modules - The recommended way to define a Window is by using CommonJS syntax - The file in which the new Window is defined, is a CommonJS module CommonJS is a project whose goal is the definition of an ecosystem for JavaScript outside of the browser (that is, defining specifics that desktop or server-side programs should follow) ‣ It includes the constructor function, having the same name of the file, that has to be invoked to create the Window ‣ The constructor includes the definition of the Window, of its elements and of their behavior, and returns the Window itself ‣ ‣ After the constructor, CommonJS module.exports special variable is used to make the constructor public to the app Use the constructor anywhere by invoking CommonJS require() method, and open a Window using CommonJS open() method var MyNewWindow = require('ui/common/NewWindow'); new MyNewWindow().open(); Alessandro Grazioli // Window Component Constructor function NewWindow() { // Create component instance var self = Ti.UI.createWindow({ backgroundColor:'#fff' }); var view = Ti.UI.createView({ backgroundColor:'#0F0' }); self.add(view); } return self; // Make constructor function public module.exports = NewWindow; ui/common/NewWindow.js file 2015 - Parma Università Degli Studi di Parma Distributed Systems Group CommonJS modules - You can use use CommonJS modules to define any kind of View and refer to it from anywhere in your app by invoking CommonJS require() method - The example is an extension to the previous one, where the content of self Window is loaded from MyViews.js file function NewWindow() { var self = Ti.UI.createWindow({ backgroundColor: '#ffffff' }); function Views() { var self = Ti.UI.createView(); var label = Ti.UI.createLabel({ color: '#000000', text: 'Title', height:'auto', width:'auto' }); var Views = require('ui/common/Views'); var selfViews = new Views(); self.add(selfViews); self.add(label); return self; } ui/common/NewWindow.js file label.addEventListener('click', function(e) { alert(e.source.text); }); module.exports = NewWindow; return self; ui/common/Views.js file } module.exports = Views; Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group Passing data among Windows - You can pass context (data) among Windows by using a CommonJS module which provides variables and objects that can be initialized and retrieved - The example is a module which provides an object, a variable and getter/setter methods - The first window of the project loads the module and uses the setter methods to store some data before opening the second window - The second window loads the same module and is able to retrieve the content saved by the first one with the getter methods Alessandro Grazioli var dataObject = {}; var dataVariable; ui/common/PassData.js file function setDataObject (obj) { dataObject = obj; } function getDataObject () { return dataObject; } function setDataVariable (variable) { dataVariable = variable; } function getDataVariable () { return dataVariable; } exports.setDataObject = exports.getDataObject = exports.setDataVariable exports.getDataVariable setDataObject; getDataObject; = setDataVariable; = getDataVariable; 2015 - Parma Università Degli Studi di Parma Distributed Systems Group Passing data among Windows - sliding animation function FirstWindow() { var self = Ti.UI.createWindow({ backgroundColor: '#FFF' }); var animationIn = {}; var animationOut; - FirstWindow defines an opening and closing animation for SecondWindow and stores them in PassData module function setAnimationIn (obj) { animationIn = obj; } var b = Ti.UI.createButton({ function getAnimationIn () { title: 'Close', return animationIn; backgroundColor: '#EEE', } width: 100, function setAnimationOut (obj) { height: 50, animationOut = obj; top: 100 } }); function getAnimationOut () { b.addEventListener('click', function() { return animationOut; var duration = 300; } var animateIn = Ti.UI.createAnimation({ exports.setAnimationIn = setAnimationIn; left: 0, exports.getAnimationIn = getAnimationIn; duration: duration exports.setAnimationOut = setAnimationOut; }); var animateOut = Ti.UI.createAnimation({ exports.getAnimationOut = getAnimationOut; left: 0, - SecondWindow retrieves the closing animation from PassData module and uses it function SecondWindow() { var self = Ti.UI.createWindow({ backgroundColor: ‘#FFF', left: Titanium.Platform.displayCaps.platformWidth }); var passModule = require('ui/common/PassData'); var b = Ti.UI.createButton({ title : 'Close', Since the opening animation defined by backgroundColor : '#EEE', FirstWindow is a sliding from current width : 100, left to 0, the initial left value of the height : 50, Window is set to the screen size, thus the top : 100 Window is placed outside of the viewport }); ui/common/PassData.js file duration: duration }); var passModule = require('ui/common/PassData'); passModule.setAnimationIn(animateIn); passModule.setAnimationOut(animateOut); var Window = require('ui/common/SecondWindow'); new Window().open(passModule.getAnimationIn()); }); Specify a sliding animation b.addEventListener('click', function() { self.close(passModule.getAnimationOut()); }); self.add(b); self.add(b); return self; Specify opening animation } module.exports = FirstWindow; Alessandro Grazioli Retrieve closing animation ui/common/FirstWindow.js file return self; ui/common/SecondWindow.js file } module.exports = SecondWindow; 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Specialized Windows - Titanium provides some specialized views that manage other windows: ‣ NavigationWindow (iOS only) ‣ ‣ ‣ SplitWindow (iOS only) TabGroup Tab Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI NavigationWindow (iOS only) NavigationWindow is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Window → Titanium.UI.iOS.NavigationWindow function NavigationWindow() { var win2 = Titanium.UI.createWindow({ backgroundColor: 'red', title: 'Red Window' }); var win1 = Titanium.UI.iOS.createNavigationWindow({ window: win2 }); NavigationWindow is a native iOS view that manages the navigation of hierarchical content from a first Window (root) to other Windows var win3 = Titanium.UI.createWindow({ backgroundColor : 'blue', title : 'Blue Window' }); var button = Titanium.UI.createButton({ title: 'Open Blue Window' }); A bar is displayed on top, with a link to return to the previous Window in the hierarchy One more windows is defined, win3 button.addEventListener('click', function() { win1.openWindow(win3, { animated: true win2 contains }); when clicked, }); In the example’s CommonJS module, win2 is the root Window (the first one displayed) - it has to be defined before the NavigationWindow, so that it can be set as its window property win2.add(button); a button that, opens win3 inside the NavigationWindow with a standard animation var button2 = Titanium.UI.createButton({ title: 'Close Blue Window' }); button2.addEventListener('click', function() { win1.closeWindow(win3, { animated: true }); }); win1 and win3 presents respectively an open and a close button Opening and closing of windows are performed by NavigationWindow NavigationWindow’s content is win2 win3.add(button2); } return win1; module.exports = NavigationWindow; Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI SplitWindow (iPad only) SplitWindow is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Window → Titanium.UI.iPad.SplitWindow function SplitWindow() { var detail = Ti.UI.createWindow({ backgroundColor: 'white' }); var label1 = Ti.UI.createLabel({ text: 'Detail View' }); detail.add(label1); SplitWindow is a native iOS view, only for the iPad, that manages manages the presentation of two side-by-side view controllers var detailNav = Ti.UI.iOS.createNavigationWindow({ window: detail }); var master = Ti.UI.createWindow({ backgroundColor: 'gray' }); var label2 = Ti.UI.createLabel({ text: 'Master View' }); master.add(label2); You use this class to implement a master-detail interface, in which the leftside view presents a list of items and the right-side presents details of the selected item The master view is also a NavigationWindow whose content is a normal Window var masterNav = Ti.UI.iOS.createNavigationWindow({ window: master }); By default, the SplitWindow shows both master and detail views in landscape orientation var splitWin = Ti.UI.iPad.createSplitWindow({ detailView: detailNav, Setting master and detail masterView: masterNav }); the SplitWindow When the device switches into portrait orientation, the detail view occupies the entire screen and the user can click a button to bring up the master view as a floating view (to show the master view in both orientations, set showMasterInPortrait property to true) In the example’s CommonJS module, the detail View is a NavigationWindow so that you can present a hierarchy of Windows The detail view is a NavigationWindow whose content is a normal Window for splitWin.addEventListener('visible', function(e) { if (e.view == 'detail') { e.button.title = "Master"; detail.leftNavButton = e.button; } else if (e.view == 'master') { detail.leftNavButton = null; } }); return splitWin; } module.exports = SplitWindow; Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Tab & TabGroup - Tab is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Tab - The behavior of Tabs and TabGroups follows the platform's native navigation style, which varies significantly between platforms Tabs are instances for a TabGroup - each Tab represents a window (or a hierarchy of windows starting from a root) inside the group ‣ On iOS, a Tab maintains a stack of windows. Calling open opens a new window on top of the stack. When a window is closed, it is removed from the stack making the previous window visible - the root tab window cannot be removed ‣ On Android, a Tab does not maintain a stack of open windows, so if you press the Back button on your device, the whole TabGroup is removed from the stack of Activities and the system presents you the Activity that was just under the TabGroup in the stack - The example presents a CommonJS module representing a Tab ‣ You have to define the root window in the Tab (tabWindow) and the Tab itself (tab) specifying the root window as its window property Alessandro Grazioli var tabWindow = Titanium.UI.createWindow({ title: 'Window 1 Title', backgroundColor: 'white' }); var tab = Titanium.UI.createTab({ window: tabWindow, title: 'Tab 1' }); return tabWindow; 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Tab & TabGroup function MyTabGroup() { var self = Ti.UI.createTabGroup(); TabGroup is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Window var TabContent1 = require('ui/common/Win1'); var TabContent2 = require('ui/common/Win2'); A TabGroup can be defined to create a window with a number of Tabs, each referred to from a button in a bar displayed under the tabs window in iOS, and on top of the tabs window in Android var win1 = new TabContent1(self); var win2 = new TabContent2(); 1. Specify the modules containing the window code for each tab var tab1 = Ti.UI.createTab({ title: 'Tab 1', window: win1 }); var tab2 = Ti.UI.createTab({ title: 'Tab 2', window: win2 }); 2. Create the Windows from the code required at previous step by calling constructors specified in the files self.addTab(tab1); self.addTab(tab2); 3. Create the tabs and specify their root window 4. Add the tabs to the TabGroup }; return self; If you want Tabs to interact with the TabGroup - for example to add to a Tab a button that closes the group - you have to pass to the Tab’s constructor a reference to the TabGroup (container in Win1 is a reference to self) Alessandro Grazioli module.exports = MyTabGroup; function Win1(container) { var self = Ti.UI.createWindow(); return self; }; module.exports = Win1; 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Controls - Controls or Widgets are visual elements such as sliders, buttons and switches They provide a visual element which has a defined behavior and special configuration and events Controls are Views and therefore inherit View’s properties, functions and events Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Controls - Switch - A Switch is an on/off controller defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Switch - On Android, a switch can have text associated with it (add attributes titleOn and/or titleOff), and appears as either a toggle button or a checkbox (add attribute style:Ti.UI.Android.SWITCH_STYLE_CHECKBOX) - On iOS, the switch appears as an iOS on/off switch and doesn't have any text associated with it - The example adds a switch to a Window and sets a change event listener for it var self = Ti.UI.createView({ backgroundColor: '#ffffff' }); The value property must be specified in the definition for iOS compatibility var switchButton = Ti.UI.createSwitch({ value: true }); The change event gets fired each time the user taps the switch switchButton.addEventListener('change',function(e) { alert('Switch value: ' + switchButton.value); }); self.add(switchButton); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Controls - Slider - A Slider is a component with a draggable thumb defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Slider - A slider lets the user select from a range of values by dragging the slider thumb (e.g. the volume level selector in music players is implemented as a slider) var win = Ti.UI.createWindow({ backgroundColor: 'white' - On Android, both min and max values must be specified for the slider to work properly - The example adds a slider and a label to a Window and sets a change event listener for the slider ‣ The text of the label is set to the slider value each time the user drags the thumb, in the format 3 integer and 1 decimal digits (%3.1f) Alessandro Grazioli }); var slider = Titanium.UI.createSlider({ top: 50, min: 0, max: 100, width: '100%', value: 50 }); var label = Ti.UI.createLabel({ text: slider.value, width: '100%', height: 'auto', top: 30, left: 0, textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER }); slider.addEventListener('change', function(e) { label.text = String.format("%3.1f", e.value); }); win.add(slider); win.add(sliderLabel); 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Controls - Button - A Button is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Button It has 4 states: ‣ ‣ disabled ‣ focused (only used on Android devices that have navigation keys or a keyboard, to indicate which button has input focus) ‣ - normal selected (being pressed) var button = Titanium.UI.createButton({ title: 'Button Text', top: 10, width: 100, height: 50 }); button.addEventListener('click',function(e) { Titanium.API.info("You clicked the button"); }); The example presents the definition of a button and the associated listener for the click event Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Controls - Button - You can specify background images and text for each state While Android provides default images for the 4 states, iOS does not iOS provides the style special property which can be set to one of the values defined in Titanium.UI.iPhone.SystemButtonStyle ‣ PLAIN: default style ‣ BORDERED: like plain but with a thick border ‣ BAR: if you do not use style property and do not provide a custom background image, the button will have a transparent background - iOS also provides systemButton special property which lets you create a predefined system-defined button, such as the Camera or Add buttons - it just sets the look, not the behavior (i.e. a Camera button does not open the Camera app, but it behaves just according to what the developer set) var cancelButton = Ti.UI.createButton({systemButton: Ti.UI.iPhone.SystemButton.CANCEL}); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Controls - ButtonBar (iOS only) - A ButtonBar is a native iOS controller View defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.ButtonBar - The ButtonBar is a set of buttons joined into a single control Each button can have a text label or an icon, but not both TabbedBar is a specialized ButtonBar where the last selected button maintains a pressed or selected state The buttons share a common style, defined by the style property that can be set to one of the constants defined in Titanium.UI.iPhone.SystemButtonStyle (PLAIN, BORDERED, BAR) - If you want the background color, or background gradient, of the button bar to show through, the style must be set to BAR (buttons with translucent background) - The example presents the definition of a ButtonBar and the associated listener for the click event ‣ Buttons are identified by their label so the event handler uses e.source.label to recognize the clicked button var bb = Titanium.UI.createButtonBar({ labels:['One', 'Two', 'Three'], backgroundColor: '#336699', top: 50, style: Titanium.UI.iPhone.SystemButtonStyle.BAR, height: 25, width:200 }); bb.addEventListener('click', function(e) { Ti.API.info('Clicked ' + e.source.label); }); win.add(bb); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Forms - Forms allow the user to provide input to the application Titanium provides a number of specialized Views to this purpose - TextField SearchBar TextArea Picker EmailDialog Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Forms - TextField - A TextField is a single-line text field defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.TextField var win = Ti.UI.createWindow({ backgroundColor: 'white' }); The example on the right presents a basic TextField with rounded corners var win = Ti.UI.createWindow({ backgroundColor: 'white' }); var send = Titanium.UI.createButton({ title: 'Send', style: Titanium.UI.iPhone.SystemButtonStyle.DONE }); var camera = Titanium.UI.createButton({ title: 'Camera', style: Titanium.UI.iPhone.SystemButtonStyle.DONE }); var cancel = Titanium.UI.createButton({ title: 'Send', style: Titanium.UI.iPhone.SystemButtonStyle.DONE }); var textField = Titanium.UI.createTextField({ borderStyle : Titanium.UI.INPUT_BORDERSTYLE_BEZEL, hintText: 'Focus to see keyboard with toolbar', keyboardToolbar : [cancel, camera, send], keyboardToolbarColor: '#999', keyboardToolbarHeight: 40, top: 10, width: 300, height: 35 }); win.add(textField); Alessandro Grazioli var textField = Ti.UI.createTextField({ borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED, top: 10, left: 10, width: 250, height: 60 }); win.add(textField); - On iOS, a configurable toolbar can be displayed above the virtual keyboard - The example on the left defines a TextField and a custom toolbar with 3 buttons 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Forms - SearchBar - A SearchBar is a specialized text field for entering search text defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.SearchBar - The SearchBar object is closely modeled on the iOS native search bar therefore not all features are supported on other platforms var search = Titanium.UI.createSearchBar({ showCancel: true, eight: 43, value: 'Enter a string' }); search.addEventListener('return', function(e) { Ti.API.Info('Inserted value is: ' + e.source.value); }); search.addEventListener('focus', function(e) { search.value = ''; }); win.add(search); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Forms - TextArea - A TextArea is a multiline text field that supports editing and scrolling defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.TextArea - The example on the right presents a customized TextArea (returnKeyType specifies the text to display on the keyboard Return key when the text area is focused) var win = Ti.UI.createWindow({ backgroundColor: 'white' }); var textArea = Ti.UI.createTextArea({ borderWidth: 2, borderColor: '#BBB', borderRadius: 5, color: '#888', font: {fontSize: 20, fontWeight: 'bold'}, keyboardType: Ti.UI.KEYBOARD_NUMBER_PAD, returnKeyType: Ti.UI.RETURNKEY_GO, textAlign: 'left', value: 'I am a textarea', top: 60, width: 300, height : 70 }); win.add(textArea); var win = Ti.UI.createWindow({ backgroundColor: 'white' }); var send = Titanium.UI.createButton({ title: 'Send', style: Titanium.UI.iPhone.SystemButtonStyle.DONE }); var camera = Titanium.UI.createButton({ title: 'Camera', style: Titanium.UI.iPhone.SystemButtonStyle.DONE }); var cancel = Titanium.UI.createButton({ title: 'Send', style: Titanium.UI.iPhone.SystemButtonStyle.DONE }); var textArea = Ti.UI.createTextArea({ borderColor : '#000', color : '#000', keyboardToolbar : [cancel, camera, send], keyboardToolbarColor : '#999', keyboardToolbarHeight : 40, value : 'Focus to see keyboard with toolbar', top : 10, width : 300, height : 120 }); win.add(textArea); Alessandro Grazioli - As for the TextField, on iOS a configurable toolbar can be displayed above the virtual keyboard - The example on the left defines a TextField and a custom toolbar with 3 buttons 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Forms - Picker - If you want to use a single-column Picker, define an array of PickerRow elements and use the add method to add it to the Picker (left-side example) - If you want to use a multi-column Picker, define an array of N PickerColumn elements, M arrays of PickerRow elements, add each array of PickerRows to a PickerColumn and add the array of PickerColumn to the Picker (right-side example) setSelectedRow takes as var win = Ti.UI.createWindow({ parameters the row and column of backgroundColor : 'white' the selected item. The 3rd param is }); var picker = Ti.UI.createPicker({ a boolean specifying if the selection top : 50 should be animated. }); var data = []; data[0]=Ti.UI.createPickerRow({title:'Bananas'}); data[1]=Ti.UI.createPickerRow({title:'Strawberries'}); data[2]=Ti.UI.createPickerRow({title:'Mangos'}); data[3]=Ti.UI.createPickerRow({title:'Grapes'}); var win = Ti.UI.createWindow({ backgroundColor : 'white' }); var picker = Ti.UI.createPicker({ top : 50, useSpinner : true }); picker.selectionIndicator = true; var fruit = [ 'Bananas', 'Strawberries', 'Mangos', 'Grapes' ]; var color = [ 'red', 'green', 'blue', 'orange' ]; var column1 = Ti.UI.createPickerColumn(); for(var i = 0, ilen = fruit.length; i < ilen; i++){ var row = Ti.UI.createPickerRow({ title: fruit[i] }); column1.addRow(row); } var column2 = Ti.UI.createPickerColumn(); for(var i = 0, ilen=color.length; i < ilen; i++){ var row = Ti.UI.createPickerRow({ title: color[i] }); column2.addRow(row); } picker.add([column1,column2]); win.add(picker); win.open(); picker.add(data); picker.selectionIndicator = true; win.add(picker); win.open(); // Must after picker has been displayed picker.setSelectedRow(0, 2, false); // select Mangos Alessandro Grazioli // Must be after picker has been displayed picker.setSelectedRow(0, 2, false); // select Mangos picker.setSelectedRow(1, 3, false); // select Orange 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI Forms - EmailDialog - An EmailDialog is a modal window that allows users to compose and send an email and is defined in Titanium.Proxy → Titanium.UI.EmailDialog - The example presents the definition of an email dialog with a listener for the complete event which gets fired when the dialog has completed sending an e-mail function buttonListenerHandler(e) { - var emailDialog = Ti.UI.createEmailDialog(); emailDialog.subject = "Hello"; emailDialog.toRecipients = ['[email protected]']; emailDialog.messageBody = '<b>This is the body</b>'; var f = Ti.Filesystem.getFile('file.wav'); emailDialog.addAttachment(f); emailDialog.open(); The code also adds an attachement file emailDialog.addEventListener('complete', function(e) { alert('e-mail sent'); }); } var button = Ti.UI.createButton({ title: 'Send e-mail', backgroundColor: '#EEE', width: 200, height: 50, top: 30 }); button.addEventListener('click', button5ListenerHandler); win.add(button); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI TableView - A TableView is used to present information, organized in sections and rows, in a vertically-scrolling view and is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.TableView - A TableView object is a container for TableViewSection objects that are, in turn, containers for TableViewRow objects You can add a SearchBar to look for elements ‣ - var win = Ti.UI.createWindow(); The considered property is specified by filterAttribute var searchBar = Titanium.UI.createSearchBar({ barColor: '#000', showCancel: false }); There are 3 approaches to the creation of a TableView 1. The simplest approach: var tableData = [ {title : 'Apples'}, {title : 'Bananas'}, {title : 'Carrots'}, {title : 'Potatoes'} ]; • • create an array of JSON-serialized properties for TableViewRow properties, such as title, backgroundColor, color, … the rows are implicitly created, added to a single TableViewSection, and then added to the TableView as presented on the right var table = Ti.UI.createTableView({ search: searchBar, data: tableData, filterAttribute: 'title' }); win.add(table); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma UI TableView Distributed Systems Group function MyTableView() { var win = Ti.UI.createWindow({ backgroundColor : 'white' }); // generate random number, used to make each row appear distinct function randomInt(max) { return Math.floor(Math.random() * max) + 1; } 2. The second approach is to explicitly create TableViewRow objects var defaultFontSize = Ti.Platform.name === 'android' ? 16 : 14; var tableData = []; • Such an approach provides more control over the layout of the rows since you can add child views (e.g., labels, images, buttons, …) for (var i = 1; i <= 20; i++) { var row = Ti.UI.createTableViewRow({ selectedBackgroundColor: 'white', rowIndex: i, // custom property to determine the row during events height: 60 }); • When passed to the TableView, a single TableViewSection is automatically created • The TableViewRow has a custom property named rowIndex which can be used to identify the clicked row in a listener for click event as e.source.rowIndex var imageAvatar = Ti.UI.createImageView({ image: 'images/user.png', left: 10, top: 5, width: 50, height: 50 }); row.add(imageAvatar); var labelUserName = Ti.UI.createLabel({ color: '#576996', font: { fontFamily : 'Arial', fontSize : defaultFontSize + 6, fontWeight : 'bold' }, text: 'Name Surname ' + i, left: 70, top: 6, width : 200, height : 30 }); row.add(labelUserName); tableData.push(row); } var tableView = Ti.UI.createTableView({ backgroundColor : 'white', data : tableData }); win.add(tableView); return win; } module.exports = MyTableView; Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma UI TableView Distributed Systems Group function MyTableView() { var win = Ti.UI.createWindow({ backgroundColor: 'white' }); var sectionFruit = Ti.UI.createTableViewSection({ headerTitle: 'Fruit' }); 3. The third approach is to explicitly create sets of TableViewRow and add them to their own TableViewSection objects, which are then added to a TableView, to enable the rows to be organized sectionFruit.add(Ti.UI.createTableViewRow({ title: 'Apples', foodType : 'fruit' })); sectionFruit.add(Ti.UI.createTableViewRow({ title: 'Bananas', foodType : 'fruit' })); • var sectionVeg = Ti.UI.createTableViewSection({ headerTitle: 'Vegetables' }); Headers or footers titles must be configured in order for the sections to be visible sectionVeg.add(Ti.UI.createTableViewRow({ title: 'Carrots', foodType : 'vegetable' })); sectionVeg.add(Ti.UI.createTableViewRow({ title: 'Potatoes', foodType : 'vegetable' })); var table = Ti.UI.createTableView({ data: [sectionFruit, sectionVeg] }); table.addEventListener('click', function(e){ alert(e.source.title + ' are of type ' + e.source.foodType); }); win.add(table); return win; } module.exports = MyTableView; Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI TableView search filtering - As previously described, you can add a SearchBar to look for items in a TableView - SearchBar compares the inserted text with filterAttribute property values of each TableViewRow - - var viewContainingTableView = Titanium.UI.createView({ width:'100%', height:'100%', top:0, left:0, layout:'vertical' }); var table = Ti.UI.createTableView({ filterAttribute: 'title', The width of data: [sectionFruit, sectionVeg] each button is }); set as the width // Labels for the buttons bar of the screen var buttonObjects = [ {‘Title'}, {'Food type’} ]; If you want to allow filtering different parameters, you need to provide a way for the user to choose the parameter and subsequently modify the TableView’s filterAttribute ‣ E.g., if your app includes a list of customers, each having a name and an address, you can add 2 buttons that specify the parameter the user wants to look for ‣ When the user taps a button, the associated property is set as filterAttribute The examples defines a View including 2 buttons that allow specifying if the SearchBar has to look the title or the foodType attribute of each TableViewRow Alessandro Grazioli divided by the number of buttons. The buttons’ width is set accordingly var searchBb = Titanium.UI.createView({ top: 0, left: 0, height: 35, width:'100%', index: 0 // Default to first button selected }); for (var i = 0; i < buttonObjects.length; i++) { var button = Ti.UI.createButton({ top : 0, title : buttonObjects[i], width : Math.floor(Titanium.Platform.displayCaps.platformWidth / buttonObjects.length), left : i * (Titanium.Platform.displayCaps.platformWidth / buttonObjects.length), index : i // The index is used to detect the pressed button }); button.addEventListener('click', function(e) { if(e.source.index == 0) table.filterAttribute = 'title'; else if(e.source.index == 1) table.filterAttribute = 'foodType'; }); searchBarButtons.add(button); } viewContainingTableView.add(searchBb); viewContainingTableView.add(table); 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI ListView - - A ListView is an alternative to TableView to present information organized in sections and rows in a vertically-scrolling view, and is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.ListView ‣ ListView corresponds to TableView ‣ ListSection corresponds to TableViewSection ‣ ListItem corresponds to TableViewRow The difference is that ListView uses a data-oriented approach while TableView uses a vieworiented approach Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI ListView - ListView is designed for performance, but one side-effect is that you cannot directly manipulate the View (i.e., add children, set view properties and bind event callbacks) as you can in a TableView - With TableView: ‣ you can directly add TableViewRow to a table using the data property ‣ you can directly create a TableViewRow and customize its styling by setting properties ‣ you can add view subcomponents to a TableViewRow by using the add method - With ListView: ‣ you need to explicitly create a ListSection to add a ListItem to a ListView (in a TableView, a TableViewRow can be directly added to a TableView since a TableViewSection is implicitly created) ‣ you cannot add views to a ListItem using the add method - to do so, define an ItemTemplate object, which is bound to a list data item using the template property ‣ You cannot explicitly bind events for a ListItem - to do so, use the events dictionary of ItemTemplate Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma - Distributed Systems Group UI ListView The template (left) uses bindId values to set custom style for the elements // A custom template that displays an image on the // left and a title next to it with a subtitle below var myTemplate = { childTemplates : [{ type: 'Ti.UI.ImageView', bindId: 'pic', properties: {// Sets the ImageView properties width: '50dp', height: '50dp', left: 0 } }, {// Title type: 'Ti.UI.Label', bindId: 'info', properties: {// Sets the Label properties color: 'black', font: { fontFamily: 'Arial', fontSize: '20dp', fontWeight: 'bold' }, left: '60dp', top: 0, } }, {// Subtitle type: 'Ti.UI.Label', bindId: 'it_info', properties: {// Sets the Label properties color: 'gray', font: { fontFamily: 'Arial', fontSize: '14dp' }, left: '60dp', top: '25dp' } }] }; Alessandro Grazioli function MyListView() { var win = Ti.UI.createWindow({ backgroundColor: 'white' }); var listView = Ti.UI.createListView({ templates: { 'template': myTemplate }, defaultItemTemplate : 'template' }); var sections = []; var fruitSection = Ti.UI.createListSection({ headerTitle: 'Fruits / Frutta' }); var fruitDataSet = [{ pic: { image: 'apple.png' }, info: { text: 'Apple' }, it_info: { text: 'Mela' } }]; fruitSection.setItems(fruitDataSet); sections.push(fruitSection); var vegSection = Ti.UI.createListSection({ headerTitle: 'Vegetables / Verdure' }); var vegDataSet = [{ pic: { image: 'carrot.png' }, info: { text: 'Carrot' }, it_info: { text: 'Carota' } }]; vegSection.setItems(vegDataSet); sections.push(vegSection); var vegSection = Ti.UI.createListSection({ headerTitle: 'Vegetables / Verdure' }); var vegDataSet = [{ pic: { image: 'carrot.png' }, info: { text : 'Carrot' }, it_info: { text: 'Carota' } }]; vegSection.setItems(vegDataSet); sections.push(vegSection); var grainSection = Ti.UI.createListSection({ headerTitle: 'Grains / Grani' }); var grainDataSet = [{ pic: { image: 'corn.png' }, info: { text: 'Corn' }, it_info: { text: 'Mais' } }]; grainSection.setItems(grainDataSet); sections.push(grainSection); listView.setSections(sections); win.add(listView); return win; } module.exports = MyListView; 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI ImageView & Label - ImageView is a View which allows displaying an image var win = Ti.UI.createWindow({ backgroundColor: 'white' }); It is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.ImageView var image = Ti.UI.createImageView({ image: '/images/myimage.png' }); win.add(image); - A Label allows the definition of a text label, with optional background image - It is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.Label - You can add Unicode 8-bit characters You should store UI strings in i18n/LANGUAGE/strings.xml and access them using L('STRING_NAME') var label = Ti.UI.createLabel({ color: 'blue', text: 'A label with\na few line breaks\nand unicode (UTF8)\nsymbols such as\na white chess piece \u2655\nand the euro symbol \u20ac\nlooks like this!\n', textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT, top: 30, width: 300, height: 200 }); win.add(label); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma UI ScrollView Distributed Systems Group - A ScrollView is a View that contains a horizontally and/or vertically -scrollable region of content and is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.ScrollView function MyScrollView() { var self = Ti.UI.createView({ backgroundColor: '#ffffff’ }); var view = Ti.UI.createView({ backgroundColor: 'red', width: '500', height: '500' }); var scrollView = Titanium.UI.createScrollView({ layout: 'vertical', }); - Views added to the ScrollView will be scrolled based on their size - if they fit within the size of their ScrollView, they will not scroll - The example creates a ScrollView including a View followed by a TableView scrollView.add(view); var tableData = []; for (var i = 0; i < 20; i++) { var tableViewRow = Ti.UI.createTableViewRow({ selectionStyle : 'none', height: '7%' }); var tableViewRowView = Ti.UI.createView({ width: "100%", height: "100%", backgroundColor : "transparent", }); var tableViewRowViewLabel = Ti.UI.createLabel({ color: '#000', font: { fontSize : 18 }, text: 'Row number ' + i }); tableViewRowView.add(tableViewRowViewLabel); tableViewRow.add(tableViewRowView); tableData.push(tableViewRow); } var table = Titanium.UI.createTableView({ data: tableData, width: '80%', height: '100%', backgroundColor: 'transparent' }); scrollView.add(table); self.add(scrollView); return self; } module.exports = MyScrollView; Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI WebView - A WebView allows opening an HTML5-based View which can load either local or remote content, and is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.WebView - The content can be any valid web content such as HTML, PDF, SVG or other WebKit-supported content types JavaScript in the WebView executes in its own context When running local web content (that is, web pages inside the application's resources and not downloaded from the internet), scripts have access to the Titanium namespace ‣ - You can use Titanium.App.addEventListener and Titanium.App.fireEvent to receive and send application-level events Scripts downloaded from remote web servers cannot access the Titanium namespace Alessandro Grazioli function MyWebView() { var self = Ti.UI.createView({ backgroundColor : '#ffffff' }); var webview = Titanium.UI.createWebView({url:'http://www.yoursite.com'}); self.add(webview); return self; } module.exports = MyWebView; 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI WebView - Having images in the Resources folder (or sub-folders thereof) included in a HTML page on an Android WebView is problematic - It may work fine in the emulator, and it may work debugging on the device, but once packaged up for store release images will not show at all - The solution is: ‣ First, for the webview URL use the usual format: Titanium.Filesystem.resourcesDirectory + 'page.html' ‣ However, within your HTML page use absolute paths for all assets file:///android_asset/Resources/someimage.png file:///android_asset/Resources/ path/to/someImage.png Note the file:/// has 3 slashes, not 2 Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI WebView - Let’s suppose that index.html and myImage.png are both in Resources/html folder The code in the top block shows how to set the page’s path The code in the bottom block shows how to link the image in the html page var webView = Ti.UI.createWebView({ width : '100%', height : '100%', top : 0, left : 0, url : Titanium.Filesystem.resourcesDirectory + 'html/index.html', opacity : 1 }); <html> <body> <div style="background-image: 'url(\'file:///android_asset/Resources/html/myImage.png\')'">Some text</div> </body> </html> index.html Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI ProgressBar - A ProgressBar is used for displaying an ongoing activity with a defined progression and is defined in Titanium.Proxy → Titanium.UI.View → Titanium.UI.ProgressBar - min and max represent the progress limits Changing the value property causes the displayed progress bar to update var win = Ti.UI.createWindow({ backgroundColor : 'white' }); var pb = Titanium.UI.createProgressBar({ top : 10, width : 250, height : 'auto', min : 0, max : 10, value : 0, color : '#fff', message : 'Downloading 0 of 10', font : { fontSize : 14, fontWeight : 'bold' }, style:Titanium.UI.iPhone.ProgressBarStyle.PLAIN }); win.add(pb); Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI AlertDialog - An AlertDialog is a modal view that includes an optional title, a message and buttons, positioned in the middle of the display, and is defined in Titanium.Proxy → Titanium.UI.AlertDialog - You can display a very simple dialog by using alert('Message'); - You can define a custom dialog with at most 3 buttons to allow user interaction, specified in buttonNames property - The example shows an Add to favorite button which, when pressed, presents a dialog to ask for confirm before saving - The cancel property is the index of the button in buttonNames list that removes the dialog (1 is 'No' in the example) Alessandro Grazioli var favButton = Titanium.UI.createButton({ color : '#000', top : 25, left : '1%', width : 30, height : 30 }); favButton.addEventListener('click', function(e) { var dialog = Ti.UI.createAlertDialog({ cancel : 1, buttonNames : ['Yes', 'No'], message : 'Would you like to add to the favorites?', title : 'Add to fav' }); dialog.addEventListener('click', function(e) { if (e.index === e.source.cancel) { } if (e.index == 0) { alert("Added"); } }); dialog.show(); }); 2015 - Parma Università Degli Studi di Parma Distributed Systems Group UI icons and splash screen - Generally, graphics files can all be placed in the Resources directory - By default, iOS will modify the icon graphic you supply to add rounded corners - If you're providing Android density-specific versions of splash screens, you'll need to put them within the correct folder in the Resources/android/images hierarchy However, if you want to provide different app icons for iOS and Android (both must be called appicon.png), you can put them into the Resources/android and Resources/iphone directories You cannot remove the splash screen and for Android it must be name default.png, on iOS Default.png (casesensitive) ‣ Check the following link for folders’ names and images’ sizes http://docs.appcelerator.com/titanium/3.0/#!/guide/Icons_and_Splash_Screens Alessandro Grazioli 2015 - Parma Università Degli Studi di Parma Distributed Systems Group Hiding the action bar on Android - To automatically hide the action bar when opening a window or tab group, you need to set a listener for the open event in that the bar can be hidden only after the window has been displayed - You have to do that for every Window in your app for which you want to hide the action bar in the associated Activity function MainView() { var isAndroid = (osname == 'android') ? true : false; var self = Ti.UI.createWindow({}); // // // // if var Window = require('ui/common/NewView'); new Window().open(); - Get a reference to the activity associated to the Window and hide the bar Hide the action bar on Android devices The action bar can be hidden only after the window has been opened, so set a listener for the 'open' event (isAndroid) { self.addEventListener('open', function(e) { self.activity.actionBar.hide(); }); } return self; } module.exports = MainView; Alessandro Grazioli ui/common/NewView.js 2015 - Parma