• No results found

Titanium Alloy Tutorial

N/A
N/A
Protected

Academic year: 2021

Share "Titanium Alloy Tutorial"

Copied!
20
0
0

Loading.... (view fulltext now)

Full text

(1)

Università

Degli

Studi di Parma

2015 - Parma Alessandro Grazioli

Distributed Systems Group

Cross-platform Programming

Titanium Alloy Tutorial

http://dsg.ce.unipr.it/

Alessandro Grazioli

http://dsg.ce.unipr.it/?q=node/37 [email protected]

(2)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Create a new project by selecting File → New → Project

-

Select Alloy on the left section of the wizard and choose Mobile App Project → Default Alloy Project
(3)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

We’ll add 2 tabs to our app

Create files

tabOneView.xml

and

tabTwoView.xml

for the tab views in

app/views

<Alloy>

<Tab id='first_tab' title='Tab 1' icon="KS_nav_views.png">

<Window title='Tab view one' class='container'>

<Label>I am Window 1</Label>

<Button id='open_button'>Open Child Window</Button> </Window>

</Tab></Alloy>

<Alloy>

<Tab id='second_tab' title='Tab 2' icon="KS_nav_ui.png">

<Window title='Tab view two' class='container'>

<TableView id='contactsTable'></TableView> </Window>

</Tab></Alloy>

Create a new project

tabOneView.xml code

(4)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Each element of the view requires an

id

so that the controller can access it

-

Images are taken from

app/images

directory

-

The view includes a

Tab

with a nested

Window

including a button

-

We’ll now define the first tab’s child window

Create file

tabOneViewChild.xml

in

app/views

<Alloy>

<Window id="first_tab_child_window" title='Tab view one child' class='container'>

</Window></Alloy>

(5)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

The style properties for a view have to be specified in a file having the same name of the view file and .tss extension (e.g., the style for index.xml view must be specified in index.tss file)

-

Style files have to be placed in app/styles directory

-

You can also define a file named app.tss to include all the styles that have to be applied to every element in the app

Create such a file and add the following code


".container": {

backgroundColor:"white"

}

-

Now, every element having class container will have a white background, regardless of the file where it has been defined

-

If you want to specify the position of tabOneView’s button, you can create tabOneView.tss file and set its layout as:


"#open_button": {

position: 'absolute', top: '20px'

}

(6)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Now that the tab views are ready, we’ll build the tab group to contain them in

index.xml

-

To specify the views to be included, use the

Require

element

<

Alloy

>

<

TabGroup

>

<

Require

src

=

"tabOneView" />

<

Require

src

=

"tabTwoView"

/>

</

TabGroup

>

</

Alloy

>

-

The tabs code can of course be included directly in

index.xml

file, but the use of

Require

elements

makes the code more modular since the functionality for each component is separated into a

specific controller file

(7)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Now you can implement the controllers in

app/controllers

-

index.js

is the controller for the view defined in

index.html

-

You can create the controller for

tabOneView

view in file

tabOneView.js

and add a function executed when

the user presses the button

$.open_button.addEventListener('click', function(e) {

var tabViewOneChildController = Alloy.createController('tabOneViewChild'); tabViewOneChildController.openMainWindow($.first_tab);

});

-

The event listener

$.open_button

is called when the user presses the button having id

open_button

(the

$.

notation allows you to access elements by id)

-

Alloy.createController(‘ID’)

returns the controller for the view whose id is passed as a parameter

Defining controllers

The code refers to a function defined in

tabOneViewChild.js which takes a Tab

as param and opens a window in it. Such a function is exported with

(8)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

We’ll now define

openMainWindow

method

Create

app/controllers/tabOneViewChild.js

file and add the following content

function

openMainWindow

(tab){

tab.open($.first_tab_child_window)

;

}

exports.openMainWindow

=

openMainWindow

;

The code defines a function which takes a

Tab

as parameter and opens a window in it (the type

Tab

is required to be able to call method

open

for the parameter)

The function is exported with name

openMainWindow

(9)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

We’ll now add a map and a text field to look for locations to tabOneViewChild by editing tabOneView.xml as follows


<Alloy>

<Window id="first_tab_child_window" title='Tab view one child' class='container'>

<Require src="addressField" id="addressField" />

<Require src="map" id="map" />

</Window></Alloy>

-

The code requires two additional views to:

display a map (map.xml)

search for an address and center the map on it (addressField.xml)

-

In the following we define the required views
(10)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

The map is not part of Titanium, so we need to use a proper module named Ti.Map (we will add it to the project in 4 slides)

-

Create file app/views/map.xml and add the following code


<Alloy>

<View id="map" ns="Ti.Map" >

<Require src="annotation" title="Annotation" />

</View></Alloy>

-

The map view requires a view, named annotation, which represents a labeled point of interest (POI) the user can click

Create file app/views/annotation.xml and add the following code


<Alloy>

<Annotation id="annotation" /></Alloy>

-

Create app/views/addressField.xml file and add the following code


<Alloy>

<View class="addressField">

<TextField id="textField" hintText="Enter an address" />

<Button id="searchButton" title="Search" />

</View></Alloy>

Specifying the map module

All UI components specified in the views are prefixed with Titanium.UI for convenience.

However, to use a component not part of the

Titanium.UI namespace, you need to use

the ns attribute – Ti.Map will be used to interact with the map

(11)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

We’ll now define the style for the required views

-

Create file app/styles/map.tss and add the following code


"#map" : { mapType : 'Ti.Map.STANDARD_TYPE', top : '50dp', animate : true, regionFit : true, userLocation : true, region : { latitude : Alloy.Globals.LATITUDE_BASE, longitude : Alloy.Globals.LONGITUDE_BASE, latitudeDelta : 0.1, longitudeDelta : 0.1 }}

-

Create file app/styles/annotation.tss and add the following code


"Annotation" : {

animate : true,

pincolor : Titanium.Map.ANNOTATION_RED}

Setting map’s views style

Map’s properties are

described in

2

slides

(12)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Create file

app/styles/addressField.tss

and add the following code

"TextField" : { height : '40dp', top : '5dp', left : '5dp', right : '150dp', style : Ti.UI.INPUT_BORDERSTYLE_ROUNDED, backgroundColor : '#fff', paddingLeft : '5dp'}"Button" : { font : { fontSize : '20dp', fontWeight : 'bold' }, top : '5dp', height : '40dp', width : '150dp', right : '5dp'}".addressField" : { backgroundColor : '#E0E0E0', height : '50dp', top : 0}

(13)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

Map attributes

-

mapType indicates what type of map should be displayed (possible values are: Ti.Map.STANDARD_TYPE,

Ti.Map.SATELLITE_TYPE and Ti.Map.HYBRID_TYPE)

-

animate is a boolean that indicates whether or not map actions, like opening and adding annotations, should be animated

-

regionFit is a boolean that indicates if the map should attempt to fit the region (MapView) in the visible view

-

userLocation is a boolean that indicates if the map should show the user's current device location as a pin on the map

-

region is an object that contains the 4 properties defining the visible area of the MapView

latitude and longitude represent the center of the map and are set based on the two variables defined in alloy.js file

The same latitude and longitude of a region can be represented with a different level of zoom via the latitudeDelta and

longitudeDelta properties (they respectively represent the latitude north and south, and the longitude east and west, from the center of the map that will be visible) - the smaller the delta values, the closer the zoom on the map

(14)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

To display a map, it is necessary to add the ti.map


module

-

Open tiapp.xml and click on + on the modules side

-

Choose ti.map module and add it

-

To use the module, you need to specify it by adding


to alloy.js file the following code


Alloy.Globals.LATITUDE_BASE = 44.765;

Alloy.Globals.LONGITUDE_BASE = 10.3;

if (OS_IOS || OS_ANDROID) {

Ti.Map = require('ti.map');

}

-

The first ad second line define two variables that will represent the center of the map

-

The if block specifies that Ti.Map calls refer to ti.map module (the if condition is due to the fact that the map module is just available for iOS and Android platforms) – the ns attribute of map.xml file refers to this
(15)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

To geocode the addresses provided by the user, you can use a script provided by Appcelerator in the

Alloy Samples page (

https://github.com/appcelerator/alloy/tree/master/samples/mapping/lib

), namely

geo.js

, and store it in

app/lib

-

Define the controllers for the new views

Create file

app/controllers/addressField.js

and add the following code

var

geo

=

require(

'geo'

)

;

$.searchButton.addEventListener(

'click'

,

function

(e) {

$.textField.blur()

;

geo.forwardGeocode($.textField.value,

function

(geodata) {

$.trigger(

'addAnnotation'

, {geodata: geodata})

;

})

;

})

;

Geocoding

searchButton click event listener executes a function called

forwardGeocode from geo.js which computes the latitude and longitude

corresponding to the address the user provided; its second parameter is a callback to be executed upon correct coordinates retrieval.

(16)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

Create file

app/controllers/map.js

and add the following code

var annotations = new Array();

exports.addAnnotation = function(geodata) {

var annotation = Alloy.createController('annotation', { title : geodata.title, latitude : geodata.coords.latitude, longitude : geodata.coords.longitude });
 
 annotations.push(annotation);
 
 $.map.addAnnotation(annotation.getView());
 
 $.map.setLocation({ latitude : geodata.coords.latitude, longitude : geodata.coords.longitude, latitudeDelta : 1, longitudeDelta : 1 });};

(17)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Add a function to tabOneViewChild controller so that when the user presses searchButton, and its callback executes, function addAnnotation defined in map controller is called ($.map.addAnnotation)


$.addressField.on('addAnnotation', function(e) {

$.map.addAnnotation(e.geodata);

});

-

searchButton callback calls forwardGeocode function defined in geo.js which takes, as second parameter, a

callback triggering function addAnnotation - since such a function is not defined in tabOneViewChild controller, the code we add uses the on method to call addAnnotation function defined in map.js

-

addAnnotation function adds a pin in the location searched by the user – to do so, you need to define the Annotation

controller


var args = arguments[0] || {};

$.annotation.title = args.title || '';

$.annotation.latitude = args.latitude || Alloy.Globals.LATITUDE_BASE;

$.annotation.longitude = args.longitude || Alloy.Globals.LONGITUDE_BASE;

-

The controller for a new annotation is created by addAnnotation method in map.js (see previous slide)

Editing controllers

The OR means that each variable can assume the value passed as a parameter when the controller is created, or a default value

(18)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Events can be used to provide interactions between different controllers

-

We can modify map.js so that, when a user clicks on a pin on the map, such a pin is removed upon confirm

Add an array for the annotations in map.js

Each time an annotation is added to the map from

function addAnnotation in map.js, also add it to the array

Add an event listener for the clicked pin and ask the user if he/she wants to remove the pin

We also have to modify annotation controller to fire an event when the user clicks the annotation itself


$.annotation.addEventListener('click', function(e) { Ti.App.fireEvent("app:clickedAnnotation", { title : e.source.title });});

Events management

Ti.App.addEventListener("app:clickedAnnotation", function(evt) { var indexOfClickedElement;

for(var i = 0; i < annotations.length; i++) {

var currentAnnotationTitle = annotations[i].getView().title;

if(currentAnnotationTitle == evt.title) {

// The index of the clicked annotation in the array indexOfClickedElement = i;

var removePinAlert = Titanium.UI.createAlertDialog({ message: 'Remove pin?',

buttonNames: ['Confirm', 'Cancel'] });

removePinAlert.addEventListener('click', function(e) {

// Clicked cancel, first check is for iphone, second for android

if (e.cancel === e.index || e.cancel === true) { return; } switch (e.index) { case 0: { annotations.splice(indexOfClickedElement, 1); $.map.removeAnnotation(evt.title); } break; case 1: { removePinAlert.hide(); } break; default: break; } }); removePinAlert.show(); break; // exit from for cycle

} }

(19)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

-

Alloy can access native APIs such as the phone contacts

-

We can display in tabTwoView a table listing all contacts

-

Also, we can add a listener to the table so that, when the user clicks a row, an alert displaying the information of the contacts is presented


$.contactsTable.addEventListener("click", function(e) { var contactDetails = e.source.title + "\n";

for(var temp in e.source.phone) {

var temp_numbers = e.source.phone[temp]; for(var k=0;k<temp_numbers.length; k++) { var temp_num = temp_numbers[k];

temp_num = temp_num.replace(/[^\d.]/g, ""); contactDetails += temp_num + "\n";

} }

alert("Clicked " + contactDetails);});

Contacts management

var addressBookDisallowed = function() { alert("Cannot access address book"); }; if (Ti.Contacts.contactsAuthorization == Ti.Contacts.AUTHORIZATION_AUTHORIZED) { renderContacts(); } else if (Ti.Contacts.contactsAuthorization == Ti.Contacts.AUTHORIZATION_UNKNOWN) { Ti.Contacts.requestAuthorization(function(e) { if (e.success) { renderContacts(); } else { addressBookDisallowed(); } }); } else { addressBookDisallowed(); } var data = []; function renderContacts() {

var contacts = Ti.Contacts.getAllPeople(); data = [];

for (var i = 0; i < contacts.length; i++) { var title = contacts[i].fullName; var phone = contacts[i].phone;

if (!title || title.length === 0) { title = "(no name)";

} data.push({ title : title, phone : phone }); } $.contactsTable.setData(data); }

(20)

Distributed Systems Group

Alessandro Grazioli 2015 - Parma

Università Degli Studi di Parma

http://dsg.ce.unipr.it/ Alloy Samples page (https://github.com/appcelerator/alloy/tree/master/samples/mapping/lib

References

Related documents

A decade of Digital Rights Management / Copy Protection / Conditional Access. Mobile

This answered both of the critical research questions (What are lecturers’ strategies to decolonise the English curriculum at a South African university?; How do lecturers

While studying diversity-related courses, Mayhew, Seifert, and Pascarella ( 2012 ) found that participants in the transition phase experienced an increased positive change in a

Functional description: If use motor temperature sensor and enable this function, Controller Stop Output Temperature and Controller Resume Output Temperature can be configured so as

Individuals that do not complete the online Introduction to the First Aid/CPR/AED Instructor Course, do not meet the requirements of the precourse skill session, or do not possess

The product’s life cycle - period usually consists of five major steps or phases: Product development, Product introduction, Product growth, Product maturity and finally

• Transcripts from each semester of your four (4) year Bachelor’s degree • Any transcripts or certificates received beyond your Bachelor’s degree program • Proof of

Utilities restructured to avoid further risk contamination of their healthy assets (renewables and grid infrastructure) by the conventional power generation business (fossil fuel