Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

Creating Alloy Widgets

Chargement dans…3
×

Consultez-les par la suite

1 sur 29
1 sur 29

Plus De Contenu Connexe

Creating Alloy Widgets

  1. 1. ALLOY WIDGETS Improving code re-use through a library of bespoke UI components. TiConf.eu Martin Hudson, Jonti Hudson February 2013
  2. 2. WHAT IS A WIDGET? A self-contained bespoke UI component that holds all the logic associated with its use.
  3. 3. WHAT IS A WIDGET? A self-contained bespoke UI component that holds all the logic associated with its use. • Create a re-usable library across multiple projects
  4. 4. WHAT IS A WIDGET? A self-contained bespoke UI component that holds all the logic associated with its use. • Create a re-usable library across multiple projects • Create components that manage cross- platform differences (e.g. a table edit / delete component)
  5. 5. WHAT IS A WIDGET? A self-contained bespoke UI component that holds all the logic associated with its use. • Create a re-usable library across multiple projects • Create components that manage cross- platform differences (e.g. a table edit / delete component) • Improve readability of code
  6. 6. WHAT IS A WIDGET? A self-contained bespoke UI component that holds all the logic associated with its use. • Create a re-usable library across multiple projects • Create components that manage cross- platform differences (e.g. a table edit / delete component) • Improve readability of code • Improve reliability due to re-use of tested components.
  7. 7. WHAT IS A WIDGET? App Widget Multiple widgets in the same window...
  8. 8. WHAT IS A WIDGET? App Widget ... or multiple instances of the same widget
  9. 9. A CUSTOM TABLE VIEW WIDGET An example of building a cross-platform widget that encapsulates common functionality but utilises platform specific behaviour.
  10. 10. CREATING A WIDGET To create a new widget, right click the project name in the Titanium Studio project view... and select New → Alloy Widget.
  11. 11. CREATING A WIDGET To create a new widget, right click the project name in the Titanium Studio project view... and select New → Alloy Widget. Use a “Reverse domain” naming convention to ensure widget names are not replicated when you share widgets with others.
  12. 12. WIDGET FILE STRUCTURE A widgets folder is created with Controllers, Styles and Views sub-directories. Note the absence of Models – this is because the main app should handle data storage
  13. 13. USING A WIDGET In the main app, we will call the newly created widget using the “Require” tag. App – index.xml Widget – widget.xml <Alloy> <Alloy> <Window class="container"> <TableView id="table"></TableView> <Require type="widget" </Alloy> src="co.mobiledatasystems.customEditableTable" id="table1"> </Require> <Button id="btnEdit" title="Allow Editing" onClick="btnEdit_click_Event"> </Button> </Window> </Alloy>
  14. 14. USING A WIDGET In the main app, we will call the newly created widget using the “Require” tag. App – index.xml Widget – widget.xml <Alloy> <Alloy> <Window class="container"> <TableView id="table"></TableView> <Require type="widget" </Alloy> src="co.mobiledatasystems.customEditableTable" id="table1"> </Require> <Button id="btnEdit" title="Allow Editing" onClick="btnEdit_click_Event"> </Button> </Window> </Alloy> Note we specify that we want a widget and reference the name of our new widget. Alloy knows where to find it.
  15. 15. USING A WIDGET In the main app, we will call the newly created widget using the “Require” tag. App – index.xml Widget – widget.xml <Alloy> <Alloy> <Window class="container"> <TableView id="table"></TableView> <Require type="widget" </Alloy> src="co.mobiledatasystems.customEditableTable" id="table1"> </Require> <Button id="btnEdit" title="Allow Editing" onClick="btnEdit_click_Event"> </Button> </Window> </Alloy> In the widget, we specify the UI components we want to expose. In our case it is only a TableView.
  16. 16. STYLING App – index.tss Widget – widget.tss ".container": { backgroundColor:"white" }, "#table1": { left: '10dp', right: '10dp', top: '20dp', bottom:'80dp' }, "#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp' } We have referenced our instance of the widget “table1” in index.xml
  17. 17. STYLING App – index.tss Widget – widget.tss ".container": { backgroundColor:"white" }, "#table1": { left: '10dp', right: '10dp', top: '40dp', bottom:'80dp' }, "#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp' } Note, where possible, do all the styling of widget components in the main app. This ensures maximum re-usability across projects.
  18. 18. CONTROLLERS - INITIALISE App – index.tss Widget – widget.js //copy the arguments passed in to the widget via. ".container": { the xml and tss parameters backgroundColor:"white" }, var _args = arguments[0] || {}; "#table1": { var editable = null; left: '10dp', right: '10dp', if(OS_ANDROID){ top: '40dp', editable = false; bottom:'80dp' }; }, //get each element set in the widget's xml or tss "#btnEdit": { parameters bottom:'10dp', left:'20dp', Ti.API.info(JSON.stringify(_args)); right:'20dp', height:'45dp' //iterate round all the parameters we have passed } in for (var key in _args) { if (_args.hasOwnProperty(key)) { In the widget's controller we can //checks key is a direct property of _args, not access all the parameters passed in somewhere down the object tree using the arguments[] array. if(OS_ANDROID){ switch (key){
  19. 19. CONTROLLERS - INITIALISE App – index.tss Widget – widget.js //copy the arguments passed in to the widget via. ".container": { the xml and tss parameters backgroundColor:"white" }, var _args = arguments[0] || {}; "#table1": { var editable = null; left: '10dp', right: '10dp', if(OS_ANDROID){ top: '40dp', editable = false; bottom:'80dp' }; }, //get each element set in the widget's xml or tss "#btnEdit": { parameters bottom:'10dp', left:'20dp', Ti.API.info(JSON.stringify(_args)); right:'20dp', height:'45dp' //iterate round all the parameters we have passed } in for (var key in _args) { if (_args.hasOwnProperty(key)) { In the widget's controller we can //checks key is a direct property of _args, not access all the parameters passed in somewhere down the object tree using the arguments[] array. if(OS_ANDROID){ switch (key){
  20. 20. CONTROLLERS - INITIALISE App – index.tss Widget – widget.js //iterate round all the parameters we have passed ".container": { in backgroundColor:"white" }, for (var key in _args) { "#table1": { if (_args.hasOwnProperty(key)) { left: '10dp', right: '10dp', //checks key is a direct property of _args, not top: '40dp', somewhere down the object tree bottom:'80dp' }, if(OS_ANDROID){ "#btnEdit": { switch (key){ bottom:'10dp', case 'editing': left:'20dp', editable = _args[key]; right:'20dp', break; height:'45dp' case 'moving': break; //android } doesn't recognise this property default: We can read each parameter passed in } $.table[key] = _args[key]; and process them appropriately. In our } else { $.table[key] = _args[key]; example “editable” and “moving” are }; }; iOS specific. We will set a local }; variable in the case of Android.
  21. 21. CONTROLLERS - INITIALISE App – index.tss Widget – widget.js //iterate round all the parameters we have passed ".container": { in backgroundColor:"white" }, for (var key in _args) { "#table1": { if (_args.hasOwnProperty(key)) { left: '10dp', right: '10dp', //checks key is a direct property of _args, not top: '40dp', somewhere down the object tree bottom:'80dp' }, if(OS_ANDROID){ "#btnEdit": { switch (key){ bottom:'10dp', case 'editing': left:'20dp', editable = _args[key]; right:'20dp', break; height:'45dp' case 'moving': break; //android } doesn't recognise this property default: We can read each parameter passed in } $.table[key] = _args[key]; and process them appropriately. In our } else { $.table[key] = _args[key]; example “editable” and “moving” are }; }; iOS specific. We will set a local }; variable in the case of Android.
  22. 22. CONTROLLERS – CALLING FUNCTIONS App – index.js Widget – widget.js $.index.open(); //custom method we expose to set the table's data var editMode = false; exports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows); //create some data to put in the table }; var rows = []; for(var i=0;i<10;i++){ //custom method we expose to allow the table to rows.push(Ti.UI.createTableViewRow({title:'Row be editable number ' + i})); exports.editing = function(edit /*bool*/){ }; if(OS_IOS){ $.table1.setData(rows); //call the "setData" $.table.editing = edit; //allow row method we created in the widget editing on iPhone & iPad } else { //toggle the "editable" mode of the table editable = edit; function btnEdit_click_Event(){ }; if(editMode){ }; $.btnEdit.title = "Allow Editing"; editMode = false; } else { Using the commonJS framework, we $.btnEdit.title = "Cancel Editing"; editMode = true; can expose functions in the widget. In }; $.table1.editing(editMode); //call the our example we expose “setData” so "editing" method we created in the widget }; we can populate the table after the widget has created it.
  23. 23. CONTROLLERS – CALLING FUNCTIONS App – index.js Widget – widget.js $.index.open(); //custom method we expose to set the table's data var editMode = false; exports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows); //create some data to put in the table }; var rows = []; for(var i=0;i<10;i++){ //custom method we expose to allow the table to rows.push(Ti.UI.createTableViewRow({title:'Row be editable number ' + i})); exports.editing = function(edit /*bool*/){ }; if(OS_IOS){ $.table1.setData(rows); //call the "setData" $.table.editing = edit; //allow row method we created in the widget editing on iPhone & iPad } else { //toggle the "editable" mode of the table editable = edit; function btnEdit_click_Event(){ }; if(editMode){ }; $.btnEdit.title = "Allow Editing"; editMode = false; } else { Using the commonJS framework, we $.btnEdit.title = "Cancel Editing"; editMode = true; can expose functions in the widget. In }; $.table1.editing(editMode); //call the our example we expose “setData” so "editing" method we created in the widget }; we can populate the table after the widget has created it.
  24. 24. CONTROLLERS – CALLING FUNCTIONS App – index.js Widget – widget.js $.index.open(); //custom method we expose to set the table's data var editMode = false; exports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows); //create some data to put in the table }; var rows = []; for(var i=0;i<10;i++){ //custom method we expose to allow the table to rows.push(Ti.UI.createTableViewRow({title:'Row be editable number ' + i})); exports.editing = function(edit /*bool*/){ }; if(OS_IOS){ $.table1.setData(rows); //call the "setData" $.table.editing = edit; //allow row method we created in the widget editing on iPhone & iPad } else { //toggle the "editable" mode of the table editable = edit; function btnEdit_click_Event(){ }; if(editMode){ }; $.btnEdit.title = "Allow Editing"; editMode = false; } else { $.btnEdit.title = "Cancel Editing"; editMode = true; Here, the main app is responding to a }; $.table1.editing(editMode); //call the button click event and changing the "editing" method we created in the widget }; editable state in the widget.
  25. 25. CONTROLLERS – CALLING FUNCTIONS App – index.js Widget – widget.js $.index.open(); //custom method we expose to set the table's data var editMode = false; exports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows); //create some data to put in the table }; var rows = []; for(var i=0;i<10;i++){ //custom method we expose to allow the table to rows.push(Ti.UI.createTableViewRow({title:'Row be editable number ' + i})); exports.editing = function(edit /*bool*/){ }; if(OS_IOS){ $.table1.setData(rows); //call the "setData" $.table.editing = edit; //allow row method we created in the widget editing on iPhone & iPad } else { //toggle the "editable" mode of the table editable = edit; function btnEdit_click_Event(){ }; if(editMode){ }; $.btnEdit.title = "Allow Editing"; editMode = false; } else { $.btnEdit.title = "Cancel Editing"; editMode = true; Here, the main app is responding to a }; $.table1.editing(editMode); //call the button click event and changing the "editing" method we created in the widget }; editable state in the widget.
  26. 26. CONTROLLERS – HANDLING EVENTS App – index.js Widget – widget.js $.table1.addEventListener('delete', function(r){ //create a handlers object that will contain Ti.API.info('row deleted: ' + JSON.stringify(r)); references to functions var dialog = Ti.UI.createAlertDialog({ var handlers = {}; message: r.row.title, title: 'Deleted' //assign some functions that do nothing. }); handlers.click = function(r){}; dialog.show(); handlers.deleteRow = function(r){}; }); //expose a function that can pass in a reference to an external function and assign the reference In the widget we create an to the appropriate handler. “addEventListener” function that has exports.addEventListener = function(listenerName, listenerFunction){ two parameters, the name of the event switch (listenerName){ case 'click' : and a reference to the function it will handlers.click = listenerFunction; break; call in the main app. case 'delete' : handlers.deleteRow = listenerFunction; break; We assign the reference to a }; }; “handlers” object we created in the if(OS_IOS){ widget. $.table.addEventListener('delete', function(r){
  27. 27. CONTROLLERS – HANDLING EVENTS App – index.js Widget – widget.js $.table1.addEventListener('delete', function(r){ //create a handlers object that will contain Ti.API.info('row deleted: ' + JSON.stringify(r)); references to functions var dialog = Ti.UI.createAlertDialog({ var handlers = {}; message: r.row.title, title: 'Deleted' //assign some functions that do nothing. }); handlers.click = function(r){}; dialog.show(); handlers.deleteRow = function(r){}; }); //expose a function that can pass in a reference to an external function and assign the reference to the appropriate handler. exports.addEventListener = function(listenerName, listenerFunction){ switch (listenerName){ In the main app we call the case 'click' : “addEventListener” function, passing in handlers.click = listenerFunction; break; the name of the listener and defining case 'delete' : handlers.deleteRow = listenerFunction; the function we want to execute when break; }; the even is fired. }; if(OS_IOS){ $.table.addEventListener('delete', function(r){
  28. 28. CONTROLLERS – HANDLING EVENTS App – index.js Widget – widget.js $.table1.addEventListener('delete', function(r){ //assign some functions that do nothing. Ti.API.info('row deleted: ' + JSON.stringify(r)); handlers.click = function(r){}; var dialog = Ti.UI.createAlertDialog({ handlers.deleteRow = function(r){}; message: r.row.title, title: 'Deleted' //expose a function that can pass in a reference }); to an external function and assign the reference dialog.show(); to the appropriate handler. }); exports.addEventListener = function(listenerName, listenerFunction){ switch (listenerName){ case 'click' : handlers.click = listenerFunction; break; case 'delete' : handlers.deleteRow = listenerFunction; In the widget we listen for the table's break; }; “delete” event and call the appropriate }; handler object. if(OS_IOS){ $.table.addEventListener('delete', function(r){ handlers.deleteRow(r); }); };
  29. 29. THANK YOU Source code: http://bit.ly/alloy-customTableView Slideshare: http://bit.ly/alloy-customTableViewSlides Mobile Data Systems Ltd. Turnkey mobile consultancy www.mobiledatasystems.co martin.hudson@mobiledatasystems.co

×