├── .gitignore
├── .project
├── .settings
└── com.aptana.editor.common.prefs
├── LICENSE
├── README.md
├── android
├── build.properties
├── build.xml
├── dist
│ └── androidcollectionview.jar
├── example
│ ├── alloy
│ │ ├── config.json
│ │ ├── controllers
│ │ │ └── index.js
│ │ ├── lib
│ │ │ └── CollectionView.js
│ │ ├── styles
│ │ │ └── index.tss
│ │ ├── views
│ │ │ └── index.xml
│ │ └── widgets
│ │ │ └── nl.fokkezb.pullToRefresh
│ │ │ ├── README.md
│ │ │ ├── controllers
│ │ │ └── widget.js
│ │ │ ├── views
│ │ │ └── widget.xml
│ │ │ └── widget.json
│ ├── app.js
│ ├── images
│ │ ├── TrashIcon.png
│ │ ├── TrashIcon@2x.png
│ │ ├── TrashIconSelected.png
│ │ ├── TrashIconSelected@2x.png
│ │ ├── UploadIcon.png
│ │ ├── UploadIcon@2x.png
│ │ ├── UploadIconSelected.png
│ │ └── UploadIconSelected@2x.png
│ └── lib
│ │ └── CollectionView.js
├── java-sources.txt
├── manifest
├── platform
│ ├── README
│ └── android
│ │ └── res
│ │ ├── layout
│ │ ├── swipe_refresh.xml
│ │ ├── ticollection_ui_list_header_or_footer.xml
│ │ └── titanium_ui_collection_item.xml
│ │ └── values
│ │ └── colors.xml
├── src
│ └── de
│ │ └── marcelpociot
│ │ └── collectionview
│ │ ├── AndroidcollectionviewModule.java
│ │ ├── BakedBezierInterpolator.java
│ │ ├── BaseCollectionViewItem.java
│ │ ├── CollectionItem.java
│ │ ├── CollectionItemProxy.java
│ │ ├── CollectionSectionProxy.java
│ │ ├── CollectionSwipeRefreshLayout.java
│ │ ├── CollectionView.java
│ │ ├── CollectionViewProxy.java
│ │ ├── CollectionViewTemplate.java
│ │ ├── DefaultCollectionViewTemplate.java
│ │ ├── ExampleProxy.java
│ │ ├── SwipeProgressBar.java
│ │ ├── SwipeRefreshLayout.java
│ │ ├── TiGridView.java
│ │ └── ViewItem.java
└── timodule.xml
├── dist
├── de.marcelpociot.collectionview-android-1.0.0.zip
├── de.marcelpociot.collectionview-android-1.1.1.zip
├── de.marcelpociot.collectionview-android-1.2.0.zip
├── de.marcelpociot.collectionview-android-1.3.0.zip
├── de.marcelpociot.collectionview-android-1.3.1.zip
├── de.marcelpociot.collectionview-iphone-1.0.0.zip
├── de.marcelpociot.collectionview-iphone-1.1.0.zip
├── de.marcelpociot.collectionview-iphone-1.1.1.zip
├── de.marcelpociot.collectionview-iphone-1.1.2.zip
├── de.marcelpociot.collectionview-iphone-1.2.0.zip
├── de.marcelpociot.collectionview-iphone-1.3.0.zip
├── de.marcelpociot.collectionview-iphone-1.3.1.zip
├── de.marcelpociot.collectionview-iphone-1.4.0.zip
├── de.marcelpociot.collectionview-iphone-1.4.1.zip
├── de.marcelpociot.collectionview-iphone-1.4.2.zip
├── ti.collectionview-android-2.0.0.zip
├── ti.collectionview-android-2.0.1.zip
├── ti.collectionview-android-3.0.0.zip
└── ti.collectionview-iphone-2.0.1.zip
├── documentation
├── contextmenu.gif
├── grid.png
└── waterfall.png
├── example
├── SampleProjectAlloy
│ └── app
│ │ ├── config.json
│ │ ├── controllers
│ │ └── index.js
│ │ ├── lib
│ │ └── CollectionView.js
│ │ ├── styles
│ │ └── index.tss
│ │ ├── views
│ │ └── index.xml
│ │ └── widgets
│ │ └── nl.fokkezb.pullToRefresh
│ │ ├── README.md
│ │ ├── controllers
│ │ └── widget.js
│ │ ├── views
│ │ └── widget.xml
│ │ └── widget.json
└── SampleProjectClassic
│ └── app.js
├── ios
├── Classes
│ ├── CHTCollectionViewWaterfallLayout
│ │ ├── CHTCollectionViewWaterfallLayout.h
│ │ └── CHTCollectionViewWaterfallLayout.m
│ ├── TiCollectionviewCollectionItem.h
│ ├── TiCollectionviewCollectionItem.m
│ ├── TiCollectionviewCollectionItemProxy.h
│ ├── TiCollectionviewCollectionItemProxy.m
│ ├── TiCollectionviewCollectionSectionProxy.h
│ ├── TiCollectionviewCollectionSectionProxy.m
│ ├── TiCollectionviewCollectionView.h
│ ├── TiCollectionviewCollectionView.m
│ ├── TiCollectionviewCollectionViewProxy.h
│ ├── TiCollectionviewCollectionViewProxy.m
│ ├── TiCollectionviewHeaderFooterReusableView.h
│ ├── TiCollectionviewHeaderFooterReusableView.m
│ ├── TiCollectionviewModule.h
│ ├── TiCollectionviewModule.m
│ ├── TiCollectionviewModuleAssets.h
│ ├── TiCollectionviewModuleAssets.m
│ ├── TiSearchDisplayController.h
│ └── TiSearchDisplayController.m
├── TiCollectionView.xcodeproj
│ └── project.pbxproj
├── TiCollectionViewWorkspace.xcworkspace
│ └── contents.xcworkspacedata
├── TiCollectionview_Prefix.pch
├── manifest
├── metadata.json
├── module.xcconfig
├── timodule.xml
└── titanium.xcconfig
└── lib
└── CollectionView.js
/.gitignore:
--------------------------------------------------------------------------------
1 | tmp
2 | bin
3 | build
4 | *.class
5 | *.pyo
6 | .DS_Store
7 | Index
8 | # Xcode
9 | *.pbxuser
10 | *.mode1v3
11 | *.mode2v3
12 | *.perspectivev3
13 | *.xcuserstate
14 | project.xcworkspace/
15 | xcuserdata/
16 | xcshareddata
17 |
18 | # Generated files
19 | *.o
20 | *.pyc
21 |
22 |
23 | #Python modules
24 | #MANIFEST
25 | build/
26 |
27 | # Backup files
28 | *~.nib
29 |
30 | #*/titanium.xcconfig
31 | */build.py
32 | android/dist/
33 | android/build.properties
34 | android/libs/
35 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | TiCollectionView
4 |
5 |
6 |
7 |
8 |
9 | com.appcelerator.titanium.core.builder
10 |
11 |
12 |
13 |
14 | com.aptana.ide.core.unifiedBuilder
15 |
16 |
17 |
18 |
19 |
20 | com.appcelerator.titanium.mobile.module.nature
21 | com.aptana.projects.webnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.settings/com.aptana.editor.common.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | selectUserAgents=com.appcelerator.titanium.mobile.module.nature\:iphone
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2014-2015 Marcel Pociot
2 | Copyright 2015-Present Nuno Costa
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ti.CollectionView
2 |
3 | [](http://gitt.io/component/ti.collectionview)
4 | [](http://mit-license.org)
5 | [](https://github.com/nuno/TiCollectionView/issues)
6 |
7 | ## ⚠️ IMPORTANT TO NOTE:
8 | Since version 2.0 the module id is ```ti.collectionview```
9 | and NOT ```de.marcelpociot.CollectionView```
10 |
11 | The original work was done by [Marcel Pociot](https://github.com/mpociot). He passed the module
12 | to me for me continue his GREAT work, he had not the time to continue! I will need all the help
13 | possible to keep that module great!
14 |
15 | ## Overview
16 |
17 | This module allows you to use a collection / grid view with the Appcelerator Titanium SDK.
18 |
19 | It uses the Titanium `ItemTemplate` objects for the best performance.
20 |
21 | ### Grid layout
22 | 
23 |
24 | ### Waterfall layout
25 | 
26 |
27 | ### Context menu
28 | 
29 |
30 | ## Installation
31 | ### Get it [](http://gitt.io/component/ti.collectionview)
32 | Download the latest distribution ZIP-file and consult the [Titanium Documentation](http://docs.appcelerator.com/titanium/latest/#!/guide/Using_a_Module) on how install it, or simply use the [gitTio CLI](http://gitt.io/cli):
33 |
34 | `$ gittio install ti.collectionview`
35 |
36 | ### Important notes for Android
37 | In order to make this module work for Android, you need to use the provided "[CollectionView.js](lib/CollectionView.js)" CommonJS library.
38 |
39 | ## API
40 |
41 | This module uses the [Ti.UI.ListView API](http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.UI.ListView).
42 |
43 | ## Additional parameters
44 |
45 | The ListView API gets extended by these custom parameters:
46 |
47 | * `layout` _(LAYOUT_WATERFALL | LAYOUT_GRID)_ - sets the layout to use for the collection view. You can select between the waterfall layout (like Pinterest) or the standard grid layout which is the default value.
48 |
49 | ### Waterfall layout specific configuration
50 |
51 | * `columnCount` _(Number)_ The number of columns to use. Default: 3
52 | * `minimumColumnSpacing` _(Number)_ The minimum spacing between each columns
53 | * `minimumInteritemSpacing` _(Number)_ The minimum spacing between each items (vertically)
54 | * `renderDirection` _(DIRECTION_LEFT_TO_RIGHT | DIRECTION_RIGHT_TO_LEFT | DIRECTION_SHORTEST_FIRST)_ The render direction to use. Default: DIRECTION_LEFT_TO_RIGHT
55 |
56 |
57 | ### iOS specific configuration (Creation Only!)
58 | * `scrollDirection` _(SCROLL_HORIZONTAL | SCROLL_VERTICAL )_ Default: SCROLL_VERTICAL
59 | * `showContextMenu` _Boolean_ - Should we show a contextual menu on longpress? Default: NO
60 | * `contextMenuStrokeColor` _Color_ - The stroke color of the context Menu indicator
61 | * `contextMenuItems` _Array_ - An array of context menu items each item is an object with the following attributes
62 | * `tintColor` _Color_ - The color used to tint the icon
63 | * `selected` _Image_ - The image for the selected state
64 | * `unselected` _Image_ - The image for the unselected state
65 |
66 | #### Events
67 | * `contextMenuClick` - Fired when a context menu item gets selected
68 | * `index` _Number_ - The selected menu item index
69 | * `itemIndex` _Number_ - The selected collection view item index
70 | * `sectionIndex` _Number_ - The selected collection view section index
71 |
72 | ---
73 |
74 | ### Android specific configuration
75 |
76 | * `columnWidth` _(Number)_ - Defines the width of each column. The Android module will fit as many columns in a row as possible
77 | * `verticalSpacing` _(Number)_ - Defines the vertical column spacing
78 | * `horizontalSpacing` _(Number)_ - Defines the horizontal column spacing
79 |
80 | ## Usage
81 |
82 | Alloy:
83 | ```xml
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | ```
105 |
106 | Vanilla JS:
107 |
108 | ```js
109 | var collectionView = require("ti.collectionview");
110 |
111 | var win = Ti.UI.createWindow({backgroundColor: 'white'});
112 |
113 | // Create a custom template that displays an image on the left,
114 | // then a title next to it with a subtitle below it.
115 | var myTemplate = {
116 | childTemplates: [
117 | { // Title
118 | type: 'Ti.UI.Label', // Use a label for the title
119 | bindId: 'info', // Maps to a custom info property of the item data
120 | properties: { // Sets the label properties
121 | color: 'black',
122 | font: { fontFamily:'Arial', fontSize: '20dp', fontWeight:'bold' },
123 | left: '60dp', top: 0,
124 | }
125 | },
126 | { // Subtitle
127 | type: 'Ti.UI.Label', // Use a label for the subtitle
128 | bindId: 'es_info', // Maps to a custom es_info property of the item data
129 | properties: { // Sets the label properties
130 | color: 'gray',
131 | font: { fontFamily:'Arial', fontSize: '14dp' },
132 | left: '60dp', top: '25dp',
133 | }
134 | }
135 | ]
136 | };
137 |
138 | var listView = require("ti.collectionview").createCollectionView({
139 | backgroundColor: "white",
140 | top: 0,
141 | left: 0,
142 | width: Ti.UI.FILL,
143 | height: Ti.UI.FILL,
144 | // Maps myTemplate dictionary to 'template' string
145 | templates: { 'template': myTemplate },
146 | // Use 'template', that is, the myTemplate dict created earlier
147 | // for all items as long as the template property is not defined for an item.
148 | defaultItemTemplate: 'template',
149 |
150 | // Context menu options
151 | showContextMenu : true,
152 | contextMenuStrokeColor : "red",
153 | contextMenuItems : [
154 | {
155 | tintColor: "red",
156 | selected: "/images/UploadIconSelected.png",
157 | unselected:"/images/UploadIcon.png",
158 | },
159 | {
160 | tintColor: "red",
161 | selected: "/images/TrashIconSelected.png",
162 | unselected:"/images/TrashIcon.png",
163 | }
164 | ],
165 |
166 | // ANDROID ONLY
167 | columnWidth: 150,
168 | verticalSpacing: 10,
169 | horizontalSpacing: 10
170 | });
171 | var sections = [];
172 |
173 | var fruitSection = collectionView.createCollectionSection({ headerTitle: 'Fruits / Frutas'});
174 | var fruitDataSet = [
175 | // the text property of info maps to the text property of the title label
176 | // the text property of es_info maps to text property of the subtitle label
177 | // the image property of pic maps to the image property of the image view
178 | { info: {text: 'Apple'}, es_info: {text: 'Manzana'}, properties: {height:150,width:150}},
179 | { info: {text: 'Apple'}, es_info: {text: 'Manzana'}, properties: {height:150,width:150}},
180 | ];
181 | fruitSection.setItems(fruitDataSet);
182 | sections.push(fruitSection);
183 |
184 | listView.setSections(sections);
185 | win.add(listView);
186 | win.open();
187 | ```
188 |
189 |
190 | ## Changelog
191 |
192 | All Releases are located in the **"/dist/*"** folder: https://github.com/nuno/TiCollectionView/tree/master/dist
193 |
194 | * v2.0.0
195 | * _iOS only_ Move project to ARC, fix all warnings and compile errors
196 | * _iOS only_ Fix main-thread execution
197 | * _iOS only_ Rebuild with 6.0.3.GA
198 | * _iOS only_ Rename project to "Ti.CollectionView"
199 | * _iOS only_ Support for the "allowsMultipleSelection" property
200 | * _iOS only_ Support for the "contentOffset" property
201 | * _iOS only_ Support for the "scrollstart" and "scrollend" event
202 | * v1.4.1
203 | * _iOS only_ Fix pull-to-refresh
204 | * v1.4.0
205 | * _iOS only_ Added support for horizontal scrolling
206 | * v1.3.0
207 | * _iOS only_ Added support for header and footer views
208 | * v1.2.0
209 | * _iOS only_ Added support for longtouch context menus
210 | * v1.1.1
211 | * Added support for Pull To Refresh
212 | * v1.1.0
213 | * Added waterfall layout for iOS
214 | * v1.0.0
215 | * Initial release with Android support added
216 |
217 |
218 | ## License
219 |
220 | Copyright 2014-2015 Marcel Pociot
221 |
222 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
223 |
224 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
225 |
226 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
227 |
228 | ## Contextmenu License
229 | Copyright 2014 Brandon McQuilkin
230 |
231 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
232 |
233 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
234 |
235 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
236 |
--------------------------------------------------------------------------------
/android/build.properties:
--------------------------------------------------------------------------------
1 | titanium.platform=/Users/marcelpociot/Library/Application Support/Titanium/mobilesdk/osx/3.4.1.GA/android
2 | android.platform=/Users/marcelpociot/Library/android-sdk-macosx/platforms/android-16
3 | google.apis=/Users/marcelpociot/Library/android-sdk-macosx/add-ons/addon-google_apis-google-16
4 | android.ndk=/Users/marcelpociot/Downloads/android-ndk-r9d
--------------------------------------------------------------------------------
/android/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ant build script for Titanium Android module androidcollectionview
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/android/dist/androidcollectionview.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/dist/androidcollectionview.jar
--------------------------------------------------------------------------------
/android/example/alloy/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "global": {},
3 | "env:development": {},
4 | "env:test": {},
5 | "env:production": {},
6 | "os:android": {},
7 | "os:blackberry": {},
8 | "os:ios": {},
9 | "os:mobileweb": {},
10 | "dependencies": {
11 | "nl.fokkezb.pullToRefresh": "2.0.1"
12 | }
13 | }
--------------------------------------------------------------------------------
/android/example/alloy/controllers/index.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $.listView.addEventListener("pull", function(e){
5 | Ti.API.info(e);
6 | });
7 |
8 | $.listView.addEventListener("pullend", function(e){
9 | Ti.API.info(e);
10 | });
11 |
12 | function myRefresher(e) {
13 |
14 | Ti.API.info("myRefresher");
15 |
16 | // fake a remote fetch
17 | setTimeout(function(){
18 | Ti.API.info("myRefresher callback");
19 | e.hide();
20 | }, 3000);
21 | }
22 |
23 | // init
24 | $.ptr.refresh();
25 |
26 | $.win.open();
--------------------------------------------------------------------------------
/android/example/alloy/lib/CollectionView.js:
--------------------------------------------------------------------------------
1 | function createCollectionView(options) {
2 | if( OS_IOS )
3 | {
4 | return require("ti.collectionview").createCollectionView(options);
5 | }
6 | var templates = options.templates;
7 | for (var binding in templates) {
8 | var currentTemplate = templates[binding];
9 | //process template
10 | processTemplate(currentTemplate);
11 | //process child templates
12 | processChildTemplates(currentTemplate);
13 | }
14 | Ti.API.info( JSON.stringify(options) );
15 | var listView = require("ti.collectionview").createCollectionView(options);
16 |
17 | return listView;
18 | }
19 |
20 | //Create ListItemProxy, add events, then store it in 'tiProxy' property
21 | function processTemplate(properties) {
22 | var cellProxy = require("ti.collectionview").createCollectionItem();
23 | properties.tiProxy = cellProxy;
24 | var events = properties.events;
25 | addEventListeners(events, cellProxy);
26 | }
27 |
28 | //Recursive function that process childTemplates and append corresponding proxies to
29 | //property 'tiProxy'. I.e: type: "Titanium.UI.Label" -> tiProxy: LabelProxy object
30 | function processChildTemplates(properties) {
31 | if (!properties.hasOwnProperty('childTemplates')) return;
32 |
33 | var childProperties = properties.childTemplates;
34 | if (childProperties === void 0 || childProperties === null) return;
35 |
36 | for (var i = 0; i < childProperties.length; i++) {
37 | var child = childProperties[i];
38 | var proxyType = child.type;
39 | if (proxyType !== void 0) {
40 | var creationProperties = child.properties;
41 | var creationFunction = lookup(proxyType);
42 | var childProxy;
43 | //create the proxy
44 | if (creationProperties !== void 0) {
45 | childProxy = creationFunction(creationProperties);
46 | } else {
47 | childProxy = creationFunction();
48 | }
49 | //add event listeners
50 | var events = child.events;
51 | addEventListeners(events, childProxy);
52 | //append proxy to tiProxy property
53 | child.tiProxy = childProxy;
54 | }
55 |
56 | processChildTemplates(child);
57 |
58 | }
59 |
60 |
61 | }
62 |
63 | //add event listeners
64 | function addEventListeners(events, proxy) {
65 | if (events !== void 0) {
66 | for (var eventName in events) {
67 | proxy.addEventListener(eventName, events[eventName]);
68 | }
69 | }
70 | }
71 |
72 | //convert name of UI elements into a constructor function.
73 | //I.e: lookup("Titanium.UI.Label") returns Titanium.UI.createLabel function
74 | function lookup(name) {
75 | var lastDotIndex = name.lastIndexOf('.');
76 | var proxy = eval(name.substring(0, lastDotIndex));
77 | if (typeof(proxy) == undefined) return;
78 |
79 | var proxyName = name.slice(lastDotIndex + 1);
80 | return proxy['create' + proxyName];
81 | }
82 |
83 | exports.createCollectionView = createCollectionView;
84 |
--------------------------------------------------------------------------------
/android/example/alloy/styles/index.tss:
--------------------------------------------------------------------------------
1 |
2 | "Label": {
3 | width: Ti.UI.SIZE,
4 | height: Ti.UI.SIZE,
5 | color: "#000",
6 | font: {
7 | fontSize: 12
8 | }
9 | }
10 |
11 | "ListSection":{
12 | itemSpacing: 40,
13 | lineSpacing: 14
14 | }
15 |
--------------------------------------------------------------------------------
/android/example/alloy/views/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/android/example/alloy/widgets/nl.fokkezb.pullToRefresh/README.md:
--------------------------------------------------------------------------------
1 | # Alloy *Pull to Refresh* Widget
2 |
3 | You can find the README at [https://github.com/fokkezb/nl.fokkezb.drawer](https://github.com/fokkezb/nl.fokkezb.pullToRefresh)
4 |
5 | ## License
6 |
7 |
8 | Copyright 2013-2014 Fokke Zandbergen
9 |
10 | Licensed under the Apache License, Version 2.0 (the "License");
11 | you may not use this file except in compliance with the License.
12 | You may obtain a copy of the License at
13 |
14 | http://www.apache.org/licenses/LICENSE-2.0
15 |
16 | Unless required by applicable law or agreed to in writing, software
17 | distributed under the License is distributed on an "AS IS" BASIS,
18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | See the License for the specific language governing permissions and
20 | limitations under the License.
21 |
--------------------------------------------------------------------------------
/android/example/alloy/widgets/nl.fokkezb.pullToRefresh/controllers/widget.js:
--------------------------------------------------------------------------------
1 | var moment = require('alloy/moment');
2 |
3 | var refreshControl;
4 |
5 | var isRefreshing = false;
6 | $.refresh = refresh;
7 |
8 | $.hide = hide;
9 | $.show = show;
10 |
11 | (function constructor(args) {
12 |
13 | if (!OS_IOS && !OS_ANDROID) {
14 | console.warn('[pullToRefresh] only supports iOS and Android.');
15 | return;
16 | }
17 |
18 | if (!_.isArray(args.children) || !_.contains(['Ti.UI.ListView', 'Ti.UI.TableView','de.marcelpociot.CollectionView', 'ti.collectionview'], args.children[0].apiName)) {
19 | console.error('[pullToRefresh] is missing required Ti.UI.ListView or Ti.UI.TableView or de.marcelpociot.CollectionView, or ti.collectionview as first child element.');
20 | return;
21 | }
22 |
23 |
24 | var list = args.children[0];
25 | delete args.children;
26 |
27 | _.extend($, args);
28 |
29 | if (OS_IOS) {
30 | refreshControl = Ti.UI.createRefreshControl();
31 | var attr = Titanium.UI.iOS.createAttributedString({
32 | text: "Pull to Refresh.",
33 | attributes: [
34 |
35 | ]
36 | });
37 | refreshControl.setTitle(attr);
38 | refreshControl.addEventListener('refreshstart', onRefreshstart);
39 |
40 | list.refreshControl = refreshControl;
41 |
42 | $.addTopLevelView(list);
43 |
44 | } else if (OS_ANDROID) {
45 | refreshControl = require('com.rkam.swiperefreshlayout').createSwipeRefresh({
46 | view: list
47 | });
48 |
49 | refreshControl.addEventListener('refreshing', onRefreshstart);
50 |
51 | $.addTopLevelView(refreshControl);
52 | }
53 |
54 | })(arguments[0] || {});
55 |
56 | function refresh() {
57 | if (!isRefreshing) {
58 | isRefreshing = true;
59 | show();
60 |
61 | onRefreshstart();
62 | }
63 | }
64 |
65 | function hide() {
66 | isRefreshing = false;
67 | if (OS_IOS) {
68 | var attr = Titanium.UI.iOS.createAttributedString({
69 | text: "Last Updated: " + new moment().format("MM-DD-YYYY hh:mm:ss a"),
70 | attributes: [
71 |
72 | ]
73 | });
74 | refreshControl.setTitle(attr);
75 | refreshControl.endRefreshing();
76 |
77 | } else if (OS_ANDROID) {
78 | refreshControl.setRefreshing(false);
79 | }
80 | }
81 |
82 | function show() {
83 |
84 | if (OS_IOS) {
85 | refreshControl.beginRefreshing();
86 | } else if (OS_ANDROID) {
87 | refreshControl.setRefreshing(true);
88 | }
89 | }
90 |
91 | function onRefreshstart() {
92 |
93 | $.trigger('release', {
94 | source: $,
95 | hide: hide
96 | });
97 | }
98 |
--------------------------------------------------------------------------------
/android/example/alloy/widgets/nl.fokkezb.pullToRefresh/views/widget.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/android/example/alloy/widgets/nl.fokkezb.pullToRefresh/widget.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "nl.fokkezb.pullToRefresh",
3 | "name": "Pull to Refresh Widget",
4 | "description" : "Uniform wrapper for iOS RefreshControl and Android SwipeRefreshLayout.",
5 | "author": "Fokke Zandbergen",
6 | "version": "2.0.1",
7 | "copyright":"Copyright (c) 2013-2015",
8 | "license":"Apache 2.0",
9 | "min-alloy-version": "1.5",
10 | "min-titanium-version":"3.4",
11 | "tags":"tableview,headerpullview,pulltorefresh,swiperefreshlayout,listview,pullview,refreshcontrol",
12 | "platforms":"ios,android"
13 | }
--------------------------------------------------------------------------------
/android/example/app.js:
--------------------------------------------------------------------------------
1 | var collectionView = require("ti.collectionview");
2 |
3 | var win = Ti.UI.createWindow({
4 | backgroundColor: 'white'
5 | });
6 |
7 | // Create a custom template that displays an image on the left,
8 | // then a title next to it with a subtitle below it.
9 | var myTemplate = {
10 | childTemplates: [
11 | { // Title
12 | type: 'Ti.UI.Label', // Use a label for the title
13 | bindId: 'info', // Maps to a custom info property of the item data
14 | properties: { // Sets the label properties
15 | color: 'black',
16 | font: { fontFamily:'Arial', fontSize: '20dp', fontWeight:'bold' },
17 | left: '60dp', top: 0,
18 | }
19 | },
20 | { // Subtitle
21 | type: 'Ti.UI.Label', // Use a label for the subtitle
22 | bindId: 'es_info', // Maps to a custom es_info property of the item data
23 | properties: { // Sets the label properties
24 | color: 'gray',
25 | font: { fontFamily:'Arial', fontSize: '14dp' },
26 | left: '60dp', top: '25dp',
27 | }
28 | }
29 | ]
30 | };
31 |
32 | var listView = require("CollectionView").createCollectionView({
33 | backgroundColor: "white",
34 | top: 0,
35 | left: 0,
36 | width: Ti.UI.FILL,
37 | height: Ti.UI.FILL,
38 | // Maps myTemplate dictionary to 'template' string
39 | templates: { 'template': myTemplate },
40 | // Use 'template', that is, the myTemplate dict created earlier
41 | // for all items as long as the template property is not defined for an item.
42 | defaultItemTemplate: 'template',
43 |
44 | // Context menu options
45 | showContextMenu : true,
46 | contextMenuStrokeColor : "red",
47 | contextMenuItems : [
48 | {
49 | tintColor: "red",
50 | selected: "/images/UploadIconSelected.png",
51 | unselected:"/images/UploadIcon.png",
52 | },
53 | {
54 | tintColor: "red",
55 | selected: "/images/TrashIconSelected.png",
56 | unselected:"/images/TrashIcon.png",
57 | }
58 | ],
59 |
60 | // ANDROID ONLY
61 | columnWidth: 150,
62 | verticalSpacing: 10,
63 | horizontalSpacing: 10
64 | });
65 |
66 | listView.addEventListener("contextMenuClick", function(e)
67 | {
68 | alert( "You clicked on menu item " + e.index + " - CollectionView item " + e.itemIndex );
69 | });
70 |
71 | var sections = [];
72 |
73 | var fruitSection = collectionView.createCollectionSection({ headerTitle: 'Fruits / Frutas'});
74 | var fruitDataSet = [
75 | // the text property of info maps to the text property of the title label
76 | // the text property of es_info maps to text property of the subtitle label
77 | // the image property of pic maps to the image property of the image view
78 | { info: {text: 'Apple'}, es_info: {text: 'Manzana'}, properties: {height:150,width:150}},
79 | { info: {text: 'Apple'}, es_info: {text: 'Manzana'}, properties: {height:150,width:150}},
80 | ];
81 | fruitSection.setItems(fruitDataSet);
82 | sections.push(fruitSection);
83 |
84 | listView.setSections(sections);
85 | win.add(listView);
86 |
87 |
88 | win.open();
89 |
--------------------------------------------------------------------------------
/android/example/images/TrashIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/TrashIcon.png
--------------------------------------------------------------------------------
/android/example/images/TrashIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/TrashIcon@2x.png
--------------------------------------------------------------------------------
/android/example/images/TrashIconSelected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/TrashIconSelected.png
--------------------------------------------------------------------------------
/android/example/images/TrashIconSelected@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/TrashIconSelected@2x.png
--------------------------------------------------------------------------------
/android/example/images/UploadIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/UploadIcon.png
--------------------------------------------------------------------------------
/android/example/images/UploadIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/UploadIcon@2x.png
--------------------------------------------------------------------------------
/android/example/images/UploadIconSelected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/UploadIconSelected.png
--------------------------------------------------------------------------------
/android/example/images/UploadIconSelected@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/android/example/images/UploadIconSelected@2x.png
--------------------------------------------------------------------------------
/android/example/lib/CollectionView.js:
--------------------------------------------------------------------------------
1 | function createCollectionView(options) {
2 | if( OS_IOS )
3 | {
4 | return require("ti.collectionview").createCollectionView(options);
5 | }
6 | var templates = options.templates;
7 | for (var binding in templates) {
8 | var currentTemplate = templates[binding];
9 | //process template
10 | processTemplate(currentTemplate);
11 | //process child templates
12 | processChildTemplates(currentTemplate);
13 | }
14 | Ti.API.info( JSON.stringify(options) );
15 | var listView = require("ti.collectionview").createCollectionView(options);
16 |
17 | return listView;
18 | }
19 |
20 | //Create ListItemProxy, add events, then store it in 'tiProxy' property
21 | function processTemplate(properties) {
22 | var cellProxy = require("ti.collectionview").createCollectionItem();
23 | properties.tiProxy = cellProxy;
24 | var events = properties.events;
25 | addEventListeners(events, cellProxy);
26 | }
27 |
28 | //Recursive function that process childTemplates and append corresponding proxies to
29 | //property 'tiProxy'. I.e: type: "Titanium.UI.Label" -> tiProxy: LabelProxy object
30 | function processChildTemplates(properties) {
31 | if (!properties.hasOwnProperty('childTemplates')) return;
32 |
33 | var childProperties = properties.childTemplates;
34 | if (childProperties === void 0 || childProperties === null) return;
35 |
36 | for (var i = 0; i < childProperties.length; i++) {
37 | var child = childProperties[i];
38 | var proxyType = child.type;
39 | if (proxyType !== void 0) {
40 | var creationProperties = child.properties;
41 | var creationFunction = lookup(proxyType);
42 | var childProxy;
43 | //create the proxy
44 | if (creationProperties !== void 0) {
45 | childProxy = creationFunction(creationProperties);
46 | } else {
47 | childProxy = creationFunction();
48 | }
49 | //add event listeners
50 | var events = child.events;
51 | addEventListeners(events, childProxy);
52 | //append proxy to tiProxy property
53 | child.tiProxy = childProxy;
54 | }
55 |
56 | processChildTemplates(child);
57 |
58 | }
59 |
60 |
61 | }
62 |
63 | //add event listeners
64 | function addEventListeners(events, proxy) {
65 | if (events !== void 0) {
66 | for (var eventName in events) {
67 | proxy.addEventListener(eventName, events[eventName]);
68 | }
69 | }
70 | }
71 |
72 | //convert name of UI elements into a constructor function.
73 | //I.e: lookup("Titanium.UI.Label") returns Titanium.UI.createLabel function
74 | function lookup(name) {
75 | var lastDotIndex = name.lastIndexOf('.');
76 | var proxy = eval(name.substring(0, lastDotIndex));
77 | if (typeof(proxy) == undefined) return;
78 |
79 | var proxyName = name.slice(lastDotIndex + 1);
80 | return proxy['create' + proxyName];
81 | }
82 |
83 | exports.createCollectionView = createCollectionView;
84 |
--------------------------------------------------------------------------------
/android/java-sources.txt:
--------------------------------------------------------------------------------
1 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/AndroidcollectionviewModule.java"
2 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/BakedBezierInterpolator.java"
3 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/BaseCollectionViewItem.java"
4 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/CollectionItem.java"
5 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/CollectionItemProxy.java"
6 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/CollectionSectionProxy.java"
7 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/CollectionSwipeRefreshLayout.java"
8 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/CollectionView.java"
9 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/CollectionViewProxy.java"
10 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/CollectionViewTemplate.java"
11 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/DefaultCollectionViewTemplate.java"
12 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/ExampleProxy.java"
13 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/SwipeProgressBar.java"
14 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/SwipeRefreshLayout.java"
15 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/TiGridView.java"
16 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/src/de/marcelpociot/collectionview/ViewItem.java"
17 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/build/generated/java/ti/collectionview/AndroidcollectionviewBootstrap.java"
18 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/build/generated/r/android/support/compat/R.java"
19 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/build/generated/r/android/support/design/R.java"
20 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/build/generated/r/android/support/v7/appcompat/R.java"
21 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/build/generated/r/android/support/v7/cardview/R.java"
22 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/build/generated/r/ti/collectionview/R.java"
23 | "/Users/hknoechel/Documents/appcelerator_modules/TiCollectionView/android/build/generated/r/ti/modules/titanium/ui/R.java"
--------------------------------------------------------------------------------
/android/manifest:
--------------------------------------------------------------------------------
1 | #
2 | # this is your module manifest and used by Titanium
3 | # during compilation, packaging, distribution, etc.
4 | #
5 | version: 3.0.0
6 | apiversion: 4
7 | architectures: arm64-v8a armeabi-v7a x86
8 | description: androidcollectionview
9 | author: Marcel Pociot
10 | license: MIT
11 | copyright: Copyright (c) 2014-2015 Marcel Pociot, 2015-Present Nuno Costa
12 |
13 | # these should not be edited
14 | name: androidcollectionview
15 | moduleid: ti.collectionview
16 | guid: 304bc7f8-ad6c-4348-9d32-658adf6f61f4
17 | platform: android
18 | minsdk: 7.0.0
19 |
--------------------------------------------------------------------------------
/android/platform/README:
--------------------------------------------------------------------------------
1 | You can place platform-specific files here in sub-folders named "android" and/or "iphone", just as you can with normal Titanium Mobile SDK projects. Any folders and files you place here will be merged with the platform-specific files in a Titanium Mobile project that uses this module.
2 |
3 | When a Titanium Mobile project that uses this module is built, the files from this platform/ folder will be treated the same as files (if any) from the Titanium Mobile project's platform/ folder.
4 |
--------------------------------------------------------------------------------
/android/platform/android/res/layout/swipe_refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/platform/android/res/layout/ticollection_ui_list_header_or_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/platform/android/res/layout/titanium_ui_collection_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
19 |
20 |
21 |
22 |
34 |
35 |
--------------------------------------------------------------------------------
/android/platform/android/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #1996BE
5 | #48B6D9
6 | #80D3ED
7 | #C2F0FF
8 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/AndroidcollectionviewModule.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This file was auto-generated by the Titanium Module SDK helper for Android
3 | * Appcelerator Titanium Mobile
4 | * Copyright (c) 2009-2010 by Appcelerator, Inc. All Rights Reserved.
5 | * Licensed under the terms of the Apache Public License
6 | * Please see the LICENSE included with this distribution for details.
7 | *
8 | */
9 | package de.marcelpociot.collectionview;
10 |
11 | import org.appcelerator.kroll.KrollModule;
12 | import org.appcelerator.kroll.annotations.Kroll;
13 |
14 | import org.appcelerator.titanium.TiApplication;
15 | import org.appcelerator.kroll.common.Log;
16 | import org.appcelerator.kroll.common.TiConfig;
17 |
18 |
19 | @Kroll.module(name="Androidcollectionview", id="ti.collectionview")
20 | public class AndroidcollectionviewModule extends KrollModule
21 | {
22 |
23 | // Standard Debugging variables
24 | private static final String LCAT = "AndroidcollectionviewModule";
25 | private static final boolean DBG = TiConfig.LOGD;
26 |
27 | // You can define constants with @Kroll.constant, for example:
28 | // @Kroll.constant public static final String EXTERNAL_NAME = value;
29 |
30 | public AndroidcollectionviewModule()
31 | {
32 | super();
33 | }
34 |
35 | @Kroll.onAppCreate
36 | public static void onAppCreate(TiApplication app)
37 | {
38 | Log.d(LCAT, "inside onAppCreate");
39 | // put module init code that needs to run when the application is created
40 | }
41 |
42 | // Methods
43 | @Kroll.method
44 | public String example()
45 | {
46 | Log.d(LCAT, "example called");
47 | return "hello world";
48 | }
49 |
50 | // Properties
51 | @Kroll.getProperty
52 | public String getExampleProp()
53 | {
54 | Log.d(LCAT, "get example property");
55 | return "hello world";
56 | }
57 |
58 |
59 | @Kroll.setProperty
60 | public void setExampleProp(String value) {
61 | Log.d(LCAT, "set example property: " + value);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/BakedBezierInterpolator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package de.marcelpociot.collectionview;
18 |
19 | import android.view.animation.Interpolator;
20 |
21 | /**
22 | * A pre-baked bezier-curved interpolator for indeterminate progress animations.
23 | */
24 | final class BakedBezierInterpolator implements Interpolator {
25 | private static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
26 |
27 | public final static BakedBezierInterpolator getInstance() {
28 | return INSTANCE;
29 | }
30 |
31 | /**
32 | * Use getInstance instead of instantiating.
33 | */
34 | private BakedBezierInterpolator() {
35 | super();
36 | }
37 |
38 | /**
39 | * Lookup table values.
40 | * Generated using a Bezier curve from (0,0) to (1,1) with control points:
41 | * P0 (0,0)
42 | * P1 (0.4, 0)
43 | * P2 (0.2, 1.0)
44 | * P3 (1.0, 1.0)
45 | *
46 | * Values sampled with x at regular intervals between 0 and 1.
47 | */
48 | private static final float[] VALUES = new float[] {
49 | 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
50 | 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
51 | 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
52 | 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
53 | 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
54 | 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
55 | 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
56 | 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
57 | 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
58 | 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
59 | };
60 |
61 | private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
62 |
63 | @Override
64 | public float getInterpolation(float input) {
65 | if (input >= 1.0f) {
66 | return 1.0f;
67 | }
68 |
69 | if (input <= 0f) {
70 | return 0f;
71 | }
72 |
73 | int position = Math.min(
74 | (int)(input * (VALUES.length - 1)),
75 | VALUES.length - 2);
76 |
77 | float quantized = position * STEP_SIZE;
78 | float difference = input - quantized;
79 | float weight = difference / STEP_SIZE;
80 |
81 | return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
82 | }
83 |
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/BaseCollectionViewItem.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 | import java.util.HashMap;
4 |
5 | import org.appcelerator.kroll.KrollDict;
6 | import org.appcelerator.titanium.TiDimension;
7 | import org.appcelerator.titanium.view.TiCompositeLayout;
8 | import org.appcelerator.titanium.view.TiUIView;
9 |
10 | import android.content.Context;
11 | import android.util.AttributeSet;
12 | import android.view.MotionEvent;
13 | import android.view.View.MeasureSpec;
14 |
15 | public class BaseCollectionViewItem extends TiCompositeLayout{
16 |
17 | private HashMap viewsMap;
18 | private ViewItem viewItem;
19 | private int minHeight;
20 | public BaseCollectionViewItem(Context context) {
21 | super(context);
22 | viewsMap = new HashMap();
23 | }
24 |
25 | public BaseCollectionViewItem(Context context, AttributeSet set) {
26 | super(context, set);
27 | setId(CollectionView.listContentId);
28 | TiDimension heightDimension = new TiDimension(CollectionView.MIN_ROW_HEIGHT, TiDimension.TYPE_UNDEFINED);
29 | minHeight = heightDimension.getAsPixels(this);
30 | setMinimumHeight(minHeight);
31 | viewsMap = new HashMap();
32 | viewItem = new ViewItem(null, new KrollDict());
33 | }
34 |
35 | public HashMap getViewsMap() {
36 | return viewsMap;
37 | }
38 |
39 | public ViewItem getViewItem() {
40 | return viewItem;
41 | }
42 |
43 | public void bindView(String binding, ViewItem view) {
44 | viewsMap.put(binding, view);
45 | }
46 |
47 | public TiUIView getViewFromBinding(String binding) {
48 | ViewItem viewItem = viewsMap.get(binding);
49 | if (viewItem != null) {
50 | return viewItem.getView();
51 | }
52 | return null;
53 | }
54 |
55 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
56 | int h = MeasureSpec.getSize(heightMeasureSpec);
57 | int hMode = MeasureSpec.getMode(heightMeasureSpec);
58 | if (h < minHeight && hMode == MeasureSpec.EXACTLY) {
59 | h = minHeight;
60 | }
61 | super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, hMode));
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/CollectionItem.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 | import java.util.HashMap;
4 |
5 | import org.appcelerator.kroll.KrollDict;
6 | import org.appcelerator.titanium.TiC;
7 | import org.appcelerator.titanium.proxy.TiViewProxy;
8 | import org.appcelerator.titanium.util.TiConvert;
9 | import org.appcelerator.titanium.view.TiCompositeLayout.LayoutParams;
10 | import org.appcelerator.titanium.view.TiUIView;
11 |
12 | import ti.modules.titanium.ui.UIModule;
13 | import android.view.View;
14 | import android.view.View.OnClickListener;
15 | import android.widget.ImageView;
16 |
17 | public class CollectionItem extends TiUIView {
18 |
19 | View listItemLayout;
20 | public CollectionItem(TiViewProxy proxy) {
21 | super(proxy);
22 | }
23 |
24 | public CollectionItem(TiViewProxy proxy, LayoutParams p, View v, View item_layout) {
25 | super(proxy);
26 | layoutParams = p;
27 | listItemLayout = item_layout;
28 | setNativeView(v);
29 | registerForTouch(v);
30 | v.setFocusable(false);
31 | }
32 |
33 | public void processProperties(KrollDict d) {
34 |
35 | if (d.containsKey(TiC.PROPERTY_ACCESSORY_TYPE)) {
36 | int accessory = TiConvert.toInt(d.get(TiC.PROPERTY_ACCESSORY_TYPE), -1);
37 | handleAccessory(accessory);
38 | }
39 | if (d.containsKey(TiC.PROPERTY_SELECTED_BACKGROUND_COLOR)) {
40 | d.put(TiC.PROPERTY_BACKGROUND_SELECTED_COLOR, d.get(TiC.PROPERTY_SELECTED_BACKGROUND_COLOR));
41 | }
42 | if (d.containsKey(TiC.PROPERTY_SELECTED_BACKGROUND_IMAGE)) {
43 | d.put(TiC.PROPERTY_BACKGROUND_SELECTED_IMAGE, d.get(TiC.PROPERTY_SELECTED_BACKGROUND_IMAGE));
44 | }
45 | super.processProperties(d);
46 | }
47 |
48 | private void handleAccessory(int accessory) {
49 |
50 | ImageView accessoryImage = (ImageView) listItemLayout.findViewById(CollectionView.accessory);
51 |
52 | switch(accessory) {
53 |
54 | case UIModule.LIST_ACCESSORY_TYPE_CHECKMARK:
55 | accessoryImage.setImageResource(CollectionView.isCheck);
56 | break;
57 | case UIModule.LIST_ACCESSORY_TYPE_DETAIL:
58 | accessoryImage.setImageResource(CollectionView.hasChild);
59 | break;
60 |
61 | case UIModule.LIST_ACCESSORY_TYPE_DISCLOSURE:
62 | accessoryImage.setImageResource(CollectionView.disclosure);
63 | break;
64 |
65 | default:
66 | accessoryImage.setImageResource(0);
67 | }
68 | }
69 |
70 | protected void setOnClickListener(View view)
71 | {
72 | view.setOnClickListener(new OnClickListener()
73 | {
74 | public void onClick(View view)
75 | {
76 | KrollDict data = dictFromEvent(lastUpEvent);
77 | handleFireItemClick(new KrollDict(data));
78 | fireEvent(TiC.EVENT_CLICK, data);
79 | }
80 | });
81 | }
82 |
83 | protected void handleFireItemClick (KrollDict data) {
84 | TiViewProxy listViewProxy = ((CollectionItemProxy)proxy).getListProxy();
85 | if (listViewProxy != null) {
86 | TiUIView listView = listViewProxy.peekView();
87 | if (listView != null) {
88 | KrollDict d = listView.getAdditionalEventData();
89 | if (d == null) {
90 | listView.setAdditionalEventData(new KrollDict((HashMap) additionalEventData));
91 | } else {
92 | d.clear();
93 | d.putAll(additionalEventData);
94 | }
95 | listView.fireEvent(TiC.EVENT_ITEM_CLICK, data);
96 | }
97 | }
98 | }
99 |
100 | public void release() {
101 | if (listItemLayout != null) {
102 | listItemLayout = null;
103 | }
104 | super.release();
105 | }
106 |
107 | }
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/CollectionItemProxy.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 | import java.lang.ref.WeakReference;
4 | import java.util.HashMap;
5 |
6 | import org.appcelerator.kroll.KrollDict;
7 | import org.appcelerator.kroll.annotations.Kroll;
8 | import org.appcelerator.titanium.TiC;
9 | import org.appcelerator.titanium.proxy.TiViewProxy;
10 | import org.appcelerator.titanium.util.TiConvert;
11 | import org.appcelerator.titanium.view.TiUIView;
12 | import android.app.Activity;
13 |
14 | @Kroll.proxy(creatableInModule = AndroidcollectionviewModule.class)
15 | public class CollectionItemProxy extends TiViewProxy
16 | {
17 | protected WeakReference listProxy;
18 |
19 | public TiUIView createView(Activity activity)
20 | {
21 | return new CollectionItem(this);
22 | }
23 |
24 | public void setListProxy(TiViewProxy list)
25 | {
26 | listProxy = new WeakReference(list);
27 | }
28 |
29 | public TiViewProxy getListProxy()
30 | {
31 | if (listProxy != null) {
32 | return listProxy.get();
33 | }
34 | return null;
35 | }
36 |
37 | public boolean fireEvent(final String event, final Object data, boolean bubbles)
38 | {
39 | fireItemClick(event, data);
40 | return super.fireEvent(event, data, bubbles);
41 | }
42 |
43 | private void fireItemClick(String event, Object data)
44 | {
45 | if (event.equals(TiC.EVENT_CLICK) && data instanceof HashMap) {
46 | KrollDict eventData = new KrollDict((HashMap) data);
47 | Object source = eventData.get(TiC.EVENT_PROPERTY_SOURCE);
48 | if (source != null && !source.equals(this) && listProxy != null) {
49 | TiViewProxy listViewProxy = listProxy.get();
50 | if (listViewProxy != null) {
51 | listViewProxy.fireEvent(TiC.EVENT_ITEM_CLICK, eventData);
52 | }
53 | }
54 | }
55 | }
56 |
57 | @Override
58 | public boolean hierarchyHasListener(String event)
59 | {
60 | // In order to fire the "itemclick" event when the children views are clicked,
61 | // the children views' "click" events must be fired and bubbled up. (TIMOB-14901)
62 | if (event.equals(TiC.EVENT_CLICK)) {
63 | return true;
64 | }
65 | return super.hierarchyHasListener(event);
66 | }
67 |
68 | public void release()
69 | {
70 | super.release();
71 | if (listProxy != null) {
72 | listProxy = null;
73 | }
74 | }
75 |
76 | @Override
77 | public String getApiName()
78 | {
79 | return "ti.CollectionItem";
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/CollectionSwipeRefreshLayout.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.ViewCompat;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 | import android.widget.AbsListView;
8 | import android.widget.FrameLayout;
9 | import android.widget.ScrollView;
10 |
11 | /**
12 | * MySwipeRefreshLayout is a modified SwipeRefreshLayout so that Titanium views
13 | * are supported. It overrides the canChildScrollUp method used by Android to
14 | * determine whether the gesture is for refresh or if the user is just scrolling up
15 | * the scrollable view.
16 | */
17 | public class CollectionSwipeRefreshLayout extends SwipeRefreshLayout {
18 |
19 | private View nativeView; // usually the layout wrapping the listview
20 | private View nativeChildView; // the native android listview
21 |
22 | public CollectionSwipeRefreshLayout(Context context) {
23 | super(context);
24 | }
25 |
26 | public CollectionSwipeRefreshLayout(Context context, AttributeSet attrs) {
27 | super(context, attrs);
28 | }
29 |
30 | public View getNativeView() {
31 | return nativeView;
32 | }
33 |
34 | public void setNativeView(View view) {
35 | this.nativeView = view;
36 | }
37 |
38 | @Override
39 | public boolean canChildScrollUp() {
40 | // ScrollViews are also an instance of FrameLayouts and we do not want to get
41 | // the ScrollView's child view as it will not work.
42 | if (nativeView instanceof FrameLayout && !(nativeView instanceof ScrollView)) {
43 | // Try to get the native Android ListView inside the FrameLayout
44 | nativeChildView = ((FrameLayout) nativeView).getChildAt(0);
45 | } else {
46 | nativeChildView = nativeView;
47 | }
48 | if (android.os.Build.VERSION.SDK_INT < 14) {
49 | if (nativeChildView instanceof AbsListView) {
50 | final AbsListView absListView = (AbsListView) nativeChildView;
51 | return absListView.getChildCount() > 0
52 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
53 | .getTop() < absListView.getPaddingTop());
54 | } else {
55 | return nativeChildView.getScrollY() > 0;
56 | }
57 | } else {
58 | return ViewCompat.canScrollVertically(nativeChildView, -1);
59 | }
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/CollectionViewProxy.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 |
4 | import java.util.ArrayList;
5 | import java.util.HashMap;
6 |
7 | import org.appcelerator.kroll.KrollDict;
8 | import org.appcelerator.kroll.KrollModule;
9 | import org.appcelerator.kroll.annotations.Kroll;
10 | import org.appcelerator.kroll.common.AsyncResult;
11 | import org.appcelerator.kroll.common.Log;
12 | import org.appcelerator.kroll.common.TiMessenger;
13 | import org.appcelerator.titanium.TiApplication;
14 | import org.appcelerator.titanium.TiC;
15 | import org.appcelerator.titanium.proxy.TiViewProxy;
16 | import org.appcelerator.titanium.util.TiConvert;
17 | import org.appcelerator.titanium.view.TiUIView;
18 |
19 | import android.app.Activity;
20 | import android.os.Handler;
21 | import android.os.Message;
22 |
23 | @Kroll.proxy(creatableInModule = AndroidcollectionviewModule.class, propertyAccessors = {
24 | TiC.PROPERTY_HEADER_TITLE,
25 | TiC.PROPERTY_FOOTER_TITLE,
26 | TiC.PROPERTY_DEFAULT_ITEM_TEMPLATE,
27 | TiC.PROPERTY_SHOW_VERTICAL_SCROLL_INDICATOR,
28 | TiC.PROPERTY_SEPARATOR_COLOR,
29 | TiC.PROPERTY_SEARCH_TEXT,
30 | TiC.PROPERTY_SEARCH_VIEW,
31 | TiC.PROPERTY_CASE_INSENSITIVE_SEARCH,
32 | TiC.PROPERTY_HEADER_DIVIDERS_ENABLED,
33 | TiC.PROPERTY_FOOTER_DIVIDERS_ENABLED
34 | })
35 | public class CollectionViewProxy extends TiViewProxy {
36 |
37 | private static final String TAG = "CollectionViewProxy";
38 |
39 | private static final int MSG_FIRST_ID = TiViewProxy.MSG_LAST_ID + 1;
40 |
41 | private static final int MSG_SECTION_COUNT = MSG_FIRST_ID + 399;
42 | private static final int MSG_SCROLL_TO_ITEM = MSG_FIRST_ID + 400;
43 | private static final int MSG_APPEND_SECTION = MSG_FIRST_ID + 401;
44 | private static final int MSG_INSERT_SECTION_AT = MSG_FIRST_ID + 402;
45 | private static final int MSG_DELETE_SECTION_AT = MSG_FIRST_ID + 403;
46 | private static final int MSG_REPLACE_SECTION_AT = MSG_FIRST_ID + 404;
47 | private static final int MSG_GET_SECTIONS = MSG_FIRST_ID + 405;
48 | private static final int MSG_SET_SECTIONS = MSG_FIRST_ID + 406;
49 |
50 |
51 |
52 | //indicate if user attempts to add/modify/delete sections before TiListView is created
53 | private boolean preload = false;
54 | private ArrayList preloadSections;
55 | private HashMap preloadMarker;
56 |
57 | public CollectionViewProxy() {
58 | super();
59 | defaultValues.put("columnWidth", 0);
60 | defaultValues.put("verticalSpacing", 0);
61 | defaultValues.put("horizontalSpacing", 0);
62 | }
63 |
64 | public TiUIView createView(Activity activity) {
65 | return new CollectionView(this, activity);
66 | }
67 |
68 | public void handleCreationArgs(KrollModule createdInModule, Object[] args) {
69 | preloadSections = new ArrayList();
70 | defaultValues.put(TiC.PROPERTY_DEFAULT_ITEM_TEMPLATE, "listDefaultTemplate");
71 | defaultValues.put(TiC.PROPERTY_CASE_INSENSITIVE_SEARCH, true);
72 | super.handleCreationArgs(createdInModule, args);
73 |
74 | }
75 | public void handleCreationDict(KrollDict options) {
76 | super.handleCreationDict(options);
77 | //Adding sections to preload sections, so we can handle appendSections/insertSection
78 | //accordingly if user call these before TiListView is instantiated.
79 | if (options.containsKey(TiC.PROPERTY_SECTIONS)) {
80 | Object obj = options.get(TiC.PROPERTY_SECTIONS);
81 | if (obj instanceof Object[]) {
82 | addPreloadSections((Object[]) obj, -1, true);
83 | }
84 | }
85 | }
86 |
87 | public void clearPreloadSections() {
88 | if (preloadSections != null) {
89 | preloadSections.clear();
90 | }
91 | }
92 |
93 | public ArrayList getPreloadSections() {
94 | return preloadSections;
95 | }
96 |
97 | public boolean getPreload() {
98 | return preload;
99 | }
100 |
101 | public void setPreload(boolean pload)
102 | {
103 | preload = pload;
104 | }
105 |
106 | public HashMap getPreloadMarker()
107 | {
108 | return preloadMarker;
109 | }
110 |
111 | private void addPreloadSections(Object secs, int index, boolean arrayOnly) {
112 | if (secs instanceof Object[]) {
113 | Object[] sections = (Object[]) secs;
114 | for (int i = 0; i < sections.length; i++) {
115 | Object section = sections[i];
116 | addPreloadSection(section, -1);
117 | }
118 | } else if (!arrayOnly) {
119 | addPreloadSection(secs, -1);
120 | }
121 | }
122 |
123 | private void addPreloadSection(Object section, int index) {
124 | if (section instanceof CollectionSectionProxy) {
125 | if (index == -1) {
126 | preloadSections.add((CollectionSectionProxy) section);
127 | } else {
128 | preloadSections.add(index, (CollectionSectionProxy) section);
129 | }
130 | }
131 | }
132 |
133 | @Kroll.method @Kroll.getProperty
134 | public int getSectionCount() {
135 | if (TiApplication.isUIThread()) {
136 | return handleSectionCount();
137 | } else {
138 | return (Integer) TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_SECTION_COUNT));
139 | }
140 | }
141 |
142 | public int handleSectionCount () {
143 | TiUIView listView = peekView();
144 | if (listView != null) {
145 | return ((CollectionView) listView).getSectionCount();
146 | }
147 | return 0;
148 | }
149 |
150 | @Kroll.method
151 | public void scrollToItem(int sectionIndex, int itemIndex, @SuppressWarnings("rawtypes") @Kroll.argument(optional=true)HashMap options) {
152 | boolean animated = true;
153 | if ( (options != null) && (options instanceof HashMap, ?>) ) {
154 | @SuppressWarnings("unchecked")
155 | KrollDict animationargs = new KrollDict(options);
156 | if (animationargs.containsKeyAndNotNull(TiC.PROPERTY_ANIMATED)) {
157 | animated = TiConvert.toBoolean(animationargs.get(TiC.PROPERTY_ANIMATED), true);
158 | }
159 | }
160 | if (TiApplication.isUIThread()) {
161 | handleScrollToItem(sectionIndex, itemIndex, animated);
162 | } else {
163 | KrollDict d = new KrollDict();
164 | d.put("itemIndex", itemIndex);
165 | d.put("sectionIndex", sectionIndex);
166 | d.put(TiC.PROPERTY_ANIMATED, Boolean.valueOf(animated));
167 | TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_SCROLL_TO_ITEM), d);
168 | }
169 | }
170 |
171 | @Kroll.method
172 | public void setMarker(Object marker) {
173 | if (marker instanceof HashMap) {
174 | HashMap m = (HashMap) marker;
175 | TiUIView listView = peekView();
176 | if (listView != null) {
177 | ((CollectionView)listView).setMarker(m);
178 | } else {
179 | preloadMarker = m;
180 | }
181 | }
182 | }
183 |
184 |
185 | @Override
186 | public boolean handleMessage(final Message msg) {
187 |
188 | switch (msg.what) {
189 |
190 | case MSG_SECTION_COUNT: {
191 | AsyncResult result = (AsyncResult)msg.obj;
192 | result.setResult(handleSectionCount());
193 | return true;
194 | }
195 |
196 | case MSG_SCROLL_TO_ITEM: {
197 | AsyncResult result = (AsyncResult)msg.obj;
198 | KrollDict data = (KrollDict) result.getArg();
199 | int sectionIndex = data.getInt("sectionIndex");
200 | int itemIndex = data.getInt("itemIndex");
201 | boolean animated = data.getBoolean(TiC.PROPERTY_ANIMATED);
202 | handleScrollToItem(sectionIndex, itemIndex, animated);
203 | result.setResult(null);
204 | return true;
205 | }
206 | case MSG_APPEND_SECTION: {
207 | AsyncResult result = (AsyncResult)msg.obj;
208 | handleAppendSection(result.getArg());
209 | result.setResult(null);
210 | return true;
211 | }
212 | case MSG_DELETE_SECTION_AT: {
213 | AsyncResult result = (AsyncResult)msg.obj;
214 | handleDeleteSectionAt(TiConvert.toInt(result.getArg()));
215 | result.setResult(null);
216 | return true;
217 | }
218 | case MSG_INSERT_SECTION_AT: {
219 | AsyncResult result = (AsyncResult)msg.obj;
220 | KrollDict data = (KrollDict) result.getArg();
221 | int index = data.getInt("index");
222 | Object section = data.get("section");
223 | handleInsertSectionAt(index, section);
224 | result.setResult(null);
225 | return true;
226 | }
227 | case MSG_REPLACE_SECTION_AT: {
228 | AsyncResult result = (AsyncResult)msg.obj;
229 | KrollDict data = (KrollDict) result.getArg();
230 | int index = data.getInt("index");
231 | Object section = data.get("section");
232 | handleReplaceSectionAt(index, section);
233 | result.setResult(null);
234 | return true;
235 | }
236 |
237 | case MSG_GET_SECTIONS: {
238 | AsyncResult result = (AsyncResult)msg.obj;
239 | result.setResult(handleSections());
240 | return true;
241 | }
242 |
243 | case MSG_SET_SECTIONS: {
244 | AsyncResult result = (AsyncResult)msg.obj;
245 | TiUIView listView = peekView();
246 | if (listView != null) {
247 | ((CollectionView)listView).processSectionsAndNotify((Object[])result.getArg());
248 | } else {
249 | Log.e(TAG, "Unable to set sections, listView is null", Log.DEBUG_MODE);
250 | }
251 | result.setResult(null);
252 | return true;
253 | }
254 |
255 | default:
256 | return super.handleMessage(msg);
257 | }
258 | }
259 | private void handleScrollToItem(int sectionIndex, int itemIndex, boolean animated) {
260 | TiUIView listView = peekView();
261 | if (listView != null) {
262 | ((CollectionView) listView).scrollToItem(sectionIndex, itemIndex, animated);
263 | }
264 | }
265 |
266 | @Kroll.method
267 | public void appendSection(Object section) {
268 | if (TiApplication.isUIThread()) {
269 | handleAppendSection(section);
270 | } else {
271 | TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_APPEND_SECTION), section);
272 | }
273 | }
274 |
275 | private void handleAppendSection(Object section) {
276 | TiUIView listView = peekView();
277 | if (listView != null) {
278 | ((CollectionView) listView).appendSection(section);
279 | } else {
280 | preload = true;
281 | addPreloadSections(section, -1, false);
282 | }
283 | }
284 |
285 | @Kroll.method
286 | public void deleteSectionAt(int index) {
287 | if (TiApplication.isUIThread()) {
288 | handleDeleteSectionAt(index);
289 | } else {
290 | TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_DELETE_SECTION_AT), index);
291 | }
292 | }
293 |
294 | private void handleDeleteSectionAt(int index) {
295 | TiUIView listView = peekView();
296 | if (listView != null) {
297 | ((CollectionView) listView).deleteSectionAt(index);
298 | } else {
299 | if (index < 0 || index >= preloadSections.size()) {
300 | Log.e(TAG, "Invalid index to delete section");
301 | return;
302 | }
303 | preload = true;
304 | preloadSections.remove(index);
305 | }
306 | }
307 |
308 | @Kroll.method
309 | public void insertSectionAt(int index, Object section) {
310 | if (TiApplication.isUIThread()) {
311 | handleInsertSectionAt(index, section);
312 | } else {
313 | sendInsertSectionMessage(index, section);
314 | }
315 | }
316 |
317 | private void sendInsertSectionMessage(int index, Object section) {
318 | KrollDict data = new KrollDict();
319 | data.put("index", index);
320 | data.put("section", section);
321 | TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_INSERT_SECTION_AT), data);
322 | }
323 |
324 | private void handleInsertSectionAt(int index, Object section) {
325 | TiUIView listView = peekView();
326 | if (listView != null) {
327 | ((CollectionView) listView).insertSectionAt(index, section);
328 | } else {
329 | if (index < 0 || index > preloadSections.size()) {
330 | Log.e(TAG, "Invalid index to insertSection");
331 | return;
332 | }
333 | preload = true;
334 | addPreloadSections(section, index, false);
335 | }
336 | }
337 |
338 | @Kroll.method
339 | public void replaceSectionAt(int index, Object section) {
340 | if (TiApplication.isUIThread()) {
341 | handleReplaceSectionAt(index, section);
342 | } else {
343 | sendReplaceSectionMessage(index, section);
344 | }
345 | }
346 |
347 | private void sendReplaceSectionMessage(int index, Object section) {
348 | KrollDict data = new KrollDict();
349 | data.put("index", index);
350 | data.put("section", section);
351 | TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_REPLACE_SECTION_AT), data);
352 | }
353 |
354 | private void handleReplaceSectionAt(int index, Object section) {
355 | TiUIView listView = peekView();
356 | if (listView != null) {
357 | ((CollectionView) listView).replaceSectionAt(index, section);
358 | } else {
359 | handleDeleteSectionAt(index);
360 | handleInsertSectionAt(index, section);
361 |
362 | }
363 | }
364 |
365 | @Kroll.method @Kroll.getProperty
366 | public CollectionSectionProxy[] getSections()
367 | {
368 | if (TiApplication.isUIThread()) {
369 | return handleSections();
370 | } else {
371 | return (CollectionSectionProxy[]) TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_GET_SECTIONS));
372 | }
373 | }
374 |
375 | @Kroll.setProperty @Kroll.method
376 | public void setSections(Object sections)
377 | {
378 | if (!(sections instanceof Object[])) {
379 | Log.e(TAG, "Invalid argument type to setSection(), needs to be an array", Log.DEBUG_MODE);
380 | return;
381 | }
382 | //Update java and javascript property
383 | setProperty(TiC.PROPERTY_SECTIONS, sections);
384 |
385 | Object[] sectionsArray = (Object[]) sections;
386 | TiUIView listView = peekView();
387 | //Preload sections if listView is not opened.
388 | if (listView == null) {
389 | preload = true;
390 | clearPreloadSections();
391 | addPreloadSections(sectionsArray, -1, true);
392 | } else {
393 | if (TiApplication.isUIThread()) {
394 | ((CollectionView)listView).processSectionsAndNotify(sectionsArray);
395 | } else {
396 | TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_SET_SECTIONS), sectionsArray);
397 | }
398 |
399 | }
400 | }
401 |
402 | private CollectionSectionProxy[] handleSections()
403 | {
404 | TiUIView listView = peekView();
405 |
406 | if (listView != null) {
407 | return ((CollectionView) listView).getSections();
408 | }
409 | ArrayList preloadedSections = getPreloadSections();
410 | return preloadedSections.toArray(new CollectionSectionProxy[preloadedSections.size()]);
411 | }
412 |
413 | @Kroll.method
414 | public void setRefreshing(boolean refreshing) {
415 | TiUIView listView = peekView();
416 |
417 | if (listView != null) {
418 | ((CollectionView) listView).setRefreshing(refreshing);
419 | }
420 | }
421 |
422 | @Kroll.method @Kroll.getProperty
423 | public boolean isRefreshing() {
424 | TiUIView listView = peekView();
425 |
426 | if (listView != null) {
427 | return ((CollectionView) listView).isRefreshing();
428 | }
429 |
430 | return false;
431 | }
432 |
433 |
434 | @Override
435 | public String getApiName()
436 | {
437 | return "ti.collectionview";
438 | }
439 | }
440 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/CollectionViewTemplate.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Set;
6 |
7 | import org.appcelerator.kroll.KrollDict;
8 | import org.appcelerator.kroll.common.Log;
9 | import org.appcelerator.titanium.TiC;
10 | import org.appcelerator.titanium.proxy.TiViewProxy;
11 | import org.appcelerator.titanium.util.TiConvert;
12 |
13 | public class CollectionViewTemplate {
14 |
15 | protected static final String TAG = "TiTemplate";
16 |
17 | protected HashMap dataItems;
18 |
19 | public static final String DEFAULT_TEMPLATE = "defaultTemplate";
20 |
21 | public static final String GENERATED_BINDING = "generatedBinding:";
22 |
23 | //Identifier for template, specified in ListView creation dict
24 | private String templateID;
25 | //Internal identifier for template, each template has a unique type
26 | private int templateType;
27 |
28 | protected DataItem rootItem;
29 |
30 | protected String itemID;
31 | //Properties of the template.
32 | private KrollDict properties;
33 |
34 | public class DataItem {
35 | //proxy for the item
36 | TiViewProxy vProxy;
37 | //binding id
38 | String bindId;
39 | DataItem parent;
40 | ArrayList children;
41 | KrollDict defaultProperties;
42 |
43 | public DataItem(TiViewProxy proxy, String id, DataItem parent) {
44 | vProxy = proxy;
45 | bindId = id;
46 | this.parent = parent;
47 | setProxyParent();
48 | children = new ArrayList();
49 | defaultProperties = new KrollDict();
50 | }
51 |
52 | private void setProxyParent() {
53 |
54 | if (vProxy != null && parent != null) {
55 | TiViewProxy parentProxy = parent.getViewProxy();
56 | if (parentProxy != null) {
57 | vProxy.setParent(parentProxy);
58 | }
59 | }
60 | }
61 |
62 | public TiViewProxy getViewProxy() {
63 | return vProxy;
64 | }
65 |
66 | public String getBindingId() {
67 | return bindId;
68 | }
69 | public void setDefaultProperties(KrollDict d) {
70 | defaultProperties = d;
71 | }
72 |
73 | public KrollDict getDefaultProperties() {
74 | return defaultProperties;
75 | }
76 |
77 | public DataItem getParent() {
78 | return parent;
79 | }
80 |
81 | public ArrayList getChildren() {
82 | return children;
83 | }
84 |
85 | public void addChild(DataItem child) {
86 | children.add(child);
87 | }
88 |
89 | public void release() {
90 | if (vProxy != null) {
91 | vProxy.release();
92 | vProxy = null;
93 | }
94 | children.clear();
95 | parent = null;
96 | }
97 | }
98 |
99 | public CollectionViewTemplate(String id, KrollDict properties) {
100 | //Init our binding hashmaps
101 | dataItems = new HashMap();
102 |
103 | //Set item id. Item binding is always "properties"
104 | itemID = TiC.PROPERTY_PROPERTIES;
105 | //Init vars.
106 | templateID = id;
107 | templateType = -1;
108 | if (properties != null) {
109 | this.properties = properties;
110 | processProperties(this.properties);
111 | } else {
112 | this.properties = new KrollDict();
113 | }
114 |
115 | }
116 |
117 | private DataItem bindProxiesAndProperties(KrollDict properties, boolean isRootTemplate, DataItem parent) {
118 | Object proxy = null;
119 | String id = null;
120 | Object props = null;
121 | DataItem item = null;
122 | if (properties.containsKey(TiC.PROPERTY_TI_PROXY)) {
123 | proxy = properties.get(TiC.PROPERTY_TI_PROXY);
124 | }
125 |
126 | //Get/generate random bind id
127 | if (isRootTemplate) {
128 | id = itemID;
129 | } else if (properties.containsKey(TiC.PROPERTY_BIND_ID)) {
130 | id = TiConvert.toString(properties, TiC.PROPERTY_BIND_ID);
131 | } else {
132 | id = GENERATED_BINDING + Math.random();
133 | }
134 |
135 |
136 | if (proxy instanceof TiViewProxy) {
137 | TiViewProxy viewProxy = (TiViewProxy) proxy;
138 | if (isRootTemplate) {
139 | rootItem = item = new DataItem(viewProxy, TiC.PROPERTY_PROPERTIES, null);
140 | } else {
141 | item = new DataItem(viewProxy, id, parent);
142 | parent.addChild(item);
143 | }
144 | dataItems.put(id, item);
145 | }
146 |
147 | if (properties.containsKey(TiC.PROPERTY_PROPERTIES)) {
148 | props = properties.get(TiC.PROPERTY_PROPERTIES);
149 | }
150 |
151 | if (props instanceof HashMap) {
152 | item.setDefaultProperties(new KrollDict((HashMap)props));
153 | }
154 |
155 | return item;
156 | }
157 |
158 | private void processProperties(KrollDict properties) {
159 | bindProxiesAndProperties(properties, true, null);
160 | if (properties.containsKey(TiC.PROPERTY_CHILD_TEMPLATES)) {
161 | processChildProperties(properties.get(TiC.PROPERTY_CHILD_TEMPLATES), rootItem);
162 | }
163 |
164 | }
165 |
166 | private void processChildProperties(Object childProperties, DataItem parent) {
167 | if (childProperties instanceof Object[]) {
168 | Object[] propertiesArray = (Object[])childProperties;
169 | for (int i = 0; i < propertiesArray.length; i++) {
170 | HashMap properties = (HashMap) propertiesArray[i];
171 | //bind proxies and default properties
172 | DataItem item = bindProxiesAndProperties(new KrollDict(properties), false, parent);
173 | //Recursively calls for all childTemplates
174 | if (properties.containsKey(TiC.PROPERTY_CHILD_TEMPLATES)) {
175 | if(item == null) {
176 | Log.e(TAG, "Unable to generate valid data from child view", Log.DEBUG_MODE);
177 | }
178 | processChildProperties(properties.get(TiC.PROPERTY_CHILD_TEMPLATES), item);
179 | }
180 | }
181 | }
182 | }
183 |
184 | public String getTemplateID() {
185 | return templateID;
186 | }
187 |
188 | public void setType(int type) {
189 | templateType = type;
190 | }
191 |
192 | public int getType() {
193 | return templateType;
194 | }
195 |
196 | public String getItemID() {
197 | return itemID;
198 | }
199 |
200 | public void setRootParent(TiViewProxy listView) {
201 | CollectionItemProxy rootProxy = (CollectionItemProxy) rootItem.getViewProxy();
202 | if (rootProxy != null && rootProxy.getListProxy() == null) {
203 | rootProxy.setListProxy(listView);
204 | }
205 | }
206 |
207 | /**
208 | * Returns the bound view proxy if exists.
209 | */
210 | public DataItem getDataItem(String binding) {
211 | return dataItems.get(binding);
212 | }
213 |
214 | public DataItem getRootItem() {
215 | return rootItem;
216 | }
217 |
218 | public void updateOrMergeWithDefaultProperties(KrollDict data, boolean update) {
219 | for (String binding: data.keySet()) {
220 | DataItem dataItem = dataItems.get(binding);
221 | if (dataItem == null) continue;
222 |
223 | KrollDict defaultProps = dataItem.getDefaultProperties();
224 | KrollDict props = new KrollDict((HashMap)data.get(binding));
225 | if (defaultProps != null) {
226 | if (update) {
227 | //update default properties
228 | Set existingKeys = defaultProps.keySet();
229 | for (String key: props.keySet()) {
230 | if (!existingKeys.contains(key)) {
231 | defaultProps.put(key, null);
232 | }
233 | }
234 | } else {
235 | //merge default properties with new properties and update data
236 | HashMap newData = ((HashMap)defaultProps.clone());
237 | newData.putAll(props);
238 | data.put(binding, newData);
239 | }
240 | }
241 | }
242 |
243 | }
244 |
245 | public void release () {
246 | for (int i = 0; i < dataItems.size(); i++) {
247 | DataItem item = dataItems.get(i);
248 | if (item != null) {
249 | item.release();
250 | }
251 | }
252 | dataItems.clear();
253 | if (rootItem != null) {
254 | rootItem.release();
255 | rootItem = null;
256 | }
257 | }
258 | }
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/DefaultCollectionViewTemplate.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 |
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 |
7 | import org.appcelerator.kroll.KrollDict;
8 | import org.appcelerator.kroll.common.Log;
9 | import org.appcelerator.titanium.TiC;
10 | import org.appcelerator.titanium.util.TiConvert;
11 |
12 | import ti.modules.titanium.ui.ImageViewProxy;
13 | import ti.modules.titanium.ui.LabelProxy;
14 | import ti.modules.titanium.ui.widget.listview.TiListViewTemplate;
15 | import android.app.Activity;
16 |
17 | public class DefaultCollectionViewTemplate extends CollectionViewTemplate {
18 |
19 | public DefaultCollectionViewTemplate(String id, KrollDict properties, Activity activity) {
20 | super(id, properties);
21 | generateDefaultProps(activity);
22 | }
23 |
24 | public void generateDefaultProps(Activity activity) {
25 |
26 | //Generate root item data proxy
27 | CollectionItemProxy proxy = new CollectionItemProxy();
28 | proxy.setActivity(activity);
29 | rootItem = new DataItem(proxy, TiC.PROPERTY_PROPERTIES, null);
30 | dataItems.put(itemID, rootItem);
31 |
32 | //Init default properties for our proxies
33 | KrollDict defaultLabelProperties = new KrollDict();
34 | KrollDict defaultImageProperties = new KrollDict();
35 |
36 | //Generate label proxy
37 | LabelProxy labelProxy = new LabelProxy();
38 | labelProxy.getProperties().put(TiC.PROPERTY_TOUCH_ENABLED, false);
39 | labelProxy.setActivity(activity);
40 | //Generate properties
41 | defaultLabelProperties.put(TiC.PROPERTY_LEFT, "2dp");
42 | defaultLabelProperties.put(TiC.PROPERTY_WIDTH, "55%");
43 | defaultLabelProperties.put(TiC.PROPERTY_TEXT, "label");
44 | //bind the proxy and default propertiess
45 | DataItem labelItem = new DataItem(labelProxy, TiC.PROPERTY_TITLE, rootItem);
46 | dataItems.put(TiC.PROPERTY_TITLE, labelItem);
47 | //set default properties
48 | labelItem.setDefaultProperties(defaultLabelProperties);
49 | //add child
50 | rootItem.addChild(labelItem);
51 |
52 | //Generate image proxy
53 | ImageViewProxy imageProxy = new ImageViewProxy();
54 | imageProxy.getProperties().put(TiC.PROPERTY_TOUCH_ENABLED, false);
55 | imageProxy.setActivity(activity);
56 | //Generate properties
57 | defaultImageProperties.put(TiC.PROPERTY_RIGHT, "25dp");
58 | defaultImageProperties.put(TiC.PROPERTY_WIDTH, "15%");
59 | //bind the proxy and default properties
60 | DataItem imageItem = new DataItem (imageProxy, TiC.PROPERTY_IMAGE, rootItem);
61 | dataItems.put(TiC.PROPERTY_IMAGE, imageItem);
62 | //set default properties
63 | imageItem.setDefaultProperties(defaultImageProperties);
64 | //add child
65 | rootItem.addChild(imageItem);
66 |
67 |
68 | }
69 | private void parseDefaultData(KrollDict data) {
70 | //for built-in template, we only process 'properties' key
71 | Iterator bindings = data.keySet().iterator();
72 | while (bindings.hasNext()) {
73 | String binding = bindings.next();
74 | if (!binding.equals(TiC.PROPERTY_PROPERTIES)) {
75 | Log.e(TAG, "Please only use 'properties' key for built-in template", Log.DEBUG_MODE);
76 | bindings.remove();
77 | }
78 | }
79 |
80 | KrollDict properties = data.getKrollDict(TiC.PROPERTY_PROPERTIES);
81 | KrollDict clone_properties = new KrollDict((HashMap)properties);
82 | if (clone_properties.containsKey(TiC.PROPERTY_TITLE)) {
83 | KrollDict text = new KrollDict();
84 | text.put(TiC.PROPERTY_TEXT, TiConvert.toString(clone_properties, TiC.PROPERTY_TITLE));
85 | data.put(TiC.PROPERTY_TITLE, text);
86 | if (clone_properties.containsKey(TiC.PROPERTY_FONT)) {
87 | text.put(TiC.PROPERTY_FONT, clone_properties.getKrollDict(TiC.PROPERTY_FONT).clone());
88 | clone_properties.remove(TiC.PROPERTY_FONT);
89 | }
90 | if (clone_properties.containsKey(TiC.PROPERTY_COLOR)) {
91 | text.put(TiC.PROPERTY_COLOR, clone_properties.get(TiC.PROPERTY_COLOR));
92 | clone_properties.remove(TiC.PROPERTY_COLOR);
93 | }
94 | clone_properties.remove(TiC.PROPERTY_TITLE);
95 | }
96 |
97 | if (clone_properties.containsKey(TiC.PROPERTY_IMAGE)) {
98 | KrollDict image = new KrollDict();
99 | image.put(TiC.PROPERTY_IMAGE, TiConvert.toString(clone_properties, TiC.PROPERTY_IMAGE));
100 | data.put(TiC.PROPERTY_IMAGE, image);
101 | clone_properties.remove(TiC.PROPERTY_IMAGE);
102 | }
103 |
104 | data.put(TiC.PROPERTY_PROPERTIES, clone_properties);
105 | }
106 |
107 | public void updateOrMergeWithDefaultProperties(KrollDict data, boolean update) {
108 |
109 | if (!data.containsKey(TiC.PROPERTY_PROPERTIES)) {
110 | Log.e(TAG, "Please use 'properties' binding for builtInTemplate");
111 | if (!update) {
112 | //apply default behavior
113 | data.clear();
114 | }
115 | return;
116 | }
117 | parseDefaultData(data);
118 | super.updateOrMergeWithDefaultProperties(data, update);
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/ExampleProxy.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This file was auto-generated by the Titanium Module SDK helper for Android
3 | * Appcelerator Titanium Mobile
4 | * Copyright (c) 2009-2010 by Appcelerator, Inc. All Rights Reserved.
5 | * Licensed under the terms of the Apache Public License
6 | * Please see the LICENSE included with this distribution for details.
7 | *
8 | */
9 | package de.marcelpociot.collectionview;
10 |
11 | import org.appcelerator.kroll.KrollDict;
12 | import org.appcelerator.kroll.KrollProxy;
13 | import org.appcelerator.kroll.annotations.Kroll;
14 | import org.appcelerator.titanium.TiC;
15 | import org.appcelerator.kroll.common.Log;
16 | import org.appcelerator.kroll.common.TiConfig;
17 | import org.appcelerator.titanium.util.TiConvert;
18 | import org.appcelerator.titanium.proxy.TiViewProxy;
19 | import org.appcelerator.titanium.view.TiCompositeLayout;
20 | import org.appcelerator.titanium.view.TiCompositeLayout.LayoutArrangement;
21 | import org.appcelerator.titanium.view.TiUIView;
22 |
23 | import android.app.Activity;
24 |
25 |
26 | // This proxy can be created by calling Androidcollectionview.createExample({message: "hello world"})
27 | @Kroll.proxy(creatableInModule=AndroidcollectionviewModule.class)
28 | public class ExampleProxy extends TiViewProxy
29 | {
30 | // Standard Debugging variables
31 | private static final String LCAT = "ExampleProxy";
32 | private static final boolean DBG = TiConfig.LOGD;
33 |
34 | private class ExampleView extends TiUIView
35 | {
36 | public ExampleView(TiViewProxy proxy) {
37 | super(proxy);
38 | LayoutArrangement arrangement = LayoutArrangement.DEFAULT;
39 |
40 | if (proxy.hasProperty(TiC.PROPERTY_LAYOUT)) {
41 | String layoutProperty = TiConvert.toString(proxy.getProperty(TiC.PROPERTY_LAYOUT));
42 | if (layoutProperty.equals(TiC.LAYOUT_HORIZONTAL)) {
43 | arrangement = LayoutArrangement.HORIZONTAL;
44 | } else if (layoutProperty.equals(TiC.LAYOUT_VERTICAL)) {
45 | arrangement = LayoutArrangement.VERTICAL;
46 | }
47 | }
48 | setNativeView(new TiCompositeLayout(proxy.getActivity(), arrangement));
49 | }
50 |
51 | @Override
52 | public void processProperties(KrollDict d)
53 | {
54 | super.processProperties(d);
55 | }
56 | }
57 |
58 |
59 | // Constructor
60 | public ExampleProxy()
61 | {
62 | super();
63 | }
64 |
65 | @Override
66 | public TiUIView createView(Activity activity)
67 | {
68 | TiUIView view = new ExampleView(this);
69 | view.getLayoutParams().autoFillsHeight = true;
70 | view.getLayoutParams().autoFillsWidth = true;
71 | return view;
72 | }
73 |
74 | // Handle creation options
75 | @Override
76 | public void handleCreationDict(KrollDict options)
77 | {
78 | super.handleCreationDict(options);
79 |
80 | if (options.containsKey("message")) {
81 | Log.d(LCAT, "example created with message: " + options.get("message"));
82 | }
83 | }
84 |
85 | // Methods
86 | @Kroll.method
87 | public void printMessage(String message)
88 | {
89 | Log.d(LCAT, "printing message: " + message);
90 | }
91 |
92 |
93 | @Kroll.getProperty @Kroll.method
94 | public String getMessage()
95 | {
96 | return "Hello World from my module";
97 | }
98 |
99 | @Kroll.setProperty @Kroll.method
100 | public void setMessage(String message)
101 | {
102 | Log.d(LCAT, "Tried setting module message to: " + message);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/SwipeProgressBar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package de.marcelpociot.collectionview;
18 |
19 | import android.graphics.Canvas;
20 | import android.graphics.Paint;
21 | import android.graphics.Rect;
22 | import android.graphics.RectF;
23 | import android.support.v4.view.ViewCompat;
24 | import android.view.View;
25 | import android.view.animation.AnimationUtils;
26 | import android.view.animation.Interpolator;
27 |
28 |
29 | /**
30 | * Custom progress bar that shows a cycle of colors as widening circles that
31 | * overdraw each other. When finished, the bar is cleared from the inside out as
32 | * the main cycle continues. Before running, this can also indicate how close
33 | * the user is to triggering something (e.g. how far they need to pull down to
34 | * trigger a refresh).
35 | */
36 | final class SwipeProgressBar {
37 |
38 | // Default progress animation colors are grays.
39 | private final static int COLOR1 = 0xB3000000;
40 | private final static int COLOR2 = 0x80000000;
41 | private final static int COLOR3 = 0x4d000000;
42 | private final static int COLOR4 = 0x1a000000;
43 |
44 | // The duration of the animation cycle.
45 | private static final int ANIMATION_DURATION_MS = 2000;
46 |
47 | // The duration of the animation to clear the bar.
48 | private static final int FINISH_ANIMATION_DURATION_MS = 1000;
49 |
50 | // Interpolator for varying the speed of the animation.
51 | private static final Interpolator INTERPOLATOR = BakedBezierInterpolator.getInstance();
52 |
53 | private final Paint mPaint = new Paint();
54 | private final RectF mClipRect = new RectF();
55 | private float mTriggerPercentage;
56 | private long mStartTime;
57 | private long mFinishTime;
58 | private boolean mRunning;
59 |
60 | // Colors used when rendering the animation,
61 | private int mColor1;
62 | private int mColor2;
63 | private int mColor3;
64 | private int mColor4;
65 | private View mParent;
66 |
67 | private Rect mBounds = new Rect();
68 |
69 | public SwipeProgressBar(View parent) {
70 | mParent = parent;
71 | mColor1 = COLOR1;
72 | mColor2 = COLOR2;
73 | mColor3 = COLOR3;
74 | mColor4 = COLOR4;
75 | }
76 |
77 | /**
78 | * Set the four colors used in the progress animation. The first color will
79 | * also be the color of the bar that grows in response to a user swipe
80 | * gesture.
81 | *
82 | * @param color1 Integer representation of a color.
83 | * @param color2 Integer representation of a color.
84 | * @param color3 Integer representation of a color.
85 | * @param color4 Integer representation of a color.
86 | */
87 | void setColorScheme(int color1, int color2, int color3, int color4) {
88 | mColor1 = color1;
89 | mColor2 = color2;
90 | mColor3 = color3;
91 | mColor4 = color4;
92 | }
93 |
94 | /**
95 | * Update the progress the user has made toward triggering the swipe
96 | * gesture. and use this value to update the percentage of the trigger that
97 | * is shown.
98 | */
99 | void setTriggerPercentage(float triggerPercentage) {
100 | mTriggerPercentage = triggerPercentage;
101 | mStartTime = 0;
102 | ViewCompat.postInvalidateOnAnimation(mParent);
103 | }
104 |
105 | /**
106 | * Start showing the progress animation.
107 | */
108 | void start() {
109 | if (!mRunning) {
110 | mTriggerPercentage = 0;
111 | mStartTime = AnimationUtils.currentAnimationTimeMillis();
112 | mRunning = true;
113 | mParent.postInvalidate();
114 | }
115 | }
116 |
117 | /**
118 | * Stop showing the progress animation.
119 | */
120 | void stop() {
121 | if (mRunning) {
122 | mTriggerPercentage = 0;
123 | mFinishTime = AnimationUtils.currentAnimationTimeMillis();
124 | mRunning = false;
125 | mParent.postInvalidate();
126 | }
127 | }
128 |
129 | /**
130 | * @return Return whether the progress animation is currently running.
131 | */
132 | boolean isRunning() {
133 | return mRunning || mFinishTime > 0;
134 | }
135 |
136 | void draw(Canvas canvas) {
137 | final int width = mBounds.width();
138 | final int height = mBounds.height();
139 | final int cx = width / 2;
140 | final int cy = height / 2;
141 | boolean drawTriggerWhileFinishing = false;
142 | int restoreCount = canvas.save();
143 | canvas.clipRect(mBounds);
144 |
145 | if (mRunning || (mFinishTime > 0)) {
146 | long now = AnimationUtils.currentAnimationTimeMillis();
147 | long elapsed = (now - mStartTime) % ANIMATION_DURATION_MS;
148 | long iterations = (now - mStartTime) / ANIMATION_DURATION_MS;
149 | float rawProgress = (elapsed / (ANIMATION_DURATION_MS / 100f));
150 |
151 | // If we're not running anymore, that means we're running through
152 | // the finish animation.
153 | if (!mRunning) {
154 | // If the finish animation is done, don't draw anything, and
155 | // don't repost.
156 | if ((now - mFinishTime) >= FINISH_ANIMATION_DURATION_MS) {
157 | mFinishTime = 0;
158 | return;
159 | }
160 |
161 | // Otherwise, use a 0 opacity alpha layer to clear the animation
162 | // from the inside out. This layer will prevent the circles from
163 | // drawing within its bounds.
164 | long finishElapsed = (now - mFinishTime) % FINISH_ANIMATION_DURATION_MS;
165 | float finishProgress = (finishElapsed / (FINISH_ANIMATION_DURATION_MS / 100f));
166 | float pct = (finishProgress / 100f);
167 | // Radius of the circle is half of the screen.
168 | float clearRadius = width / 2 * INTERPOLATOR.getInterpolation(pct);
169 | mClipRect.set(cx - clearRadius, 0, cx + clearRadius, height);
170 | canvas.saveLayerAlpha(mClipRect, 0, 0);
171 | // Only draw the trigger if there is a space in the center of
172 | // this refreshing view that needs to be filled in by the
173 | // trigger. If the progress view is just still animating, let it
174 | // continue animating.
175 | drawTriggerWhileFinishing = true;
176 | }
177 |
178 | // First fill in with the last color that would have finished drawing.
179 | if (iterations == 0) {
180 | canvas.drawColor(mColor1);
181 | } else {
182 | if (rawProgress >= 0 && rawProgress < 25) {
183 | canvas.drawColor(mColor4);
184 | } else if (rawProgress >= 25 && rawProgress < 50) {
185 | canvas.drawColor(mColor1);
186 | } else if (rawProgress >= 50 && rawProgress < 75) {
187 | canvas.drawColor(mColor2);
188 | } else {
189 | canvas.drawColor(mColor3);
190 | }
191 | }
192 |
193 | // Then draw up to 4 overlapping concentric circles of varying radii, based on how far
194 | // along we are in the cycle.
195 | // progress 0-50 draw mColor2
196 | // progress 25-75 draw mColor3
197 | // progress 50-100 draw mColor4
198 | // progress 75 (wrap to 25) draw mColor1
199 | if ((rawProgress >= 0 && rawProgress <= 25)) {
200 | float pct = (((rawProgress + 25) * 2) / 100f);
201 | drawCircle(canvas, cx, cy, mColor1, pct);
202 | }
203 | if (rawProgress >= 0 && rawProgress <= 50) {
204 | float pct = ((rawProgress * 2) / 100f);
205 | drawCircle(canvas, cx, cy, mColor2, pct);
206 | }
207 | if (rawProgress >= 25 && rawProgress <= 75) {
208 | float pct = (((rawProgress - 25) * 2) / 100f);
209 | drawCircle(canvas, cx, cy, mColor3, pct);
210 | }
211 | if (rawProgress >= 50 && rawProgress <= 100) {
212 | float pct = (((rawProgress - 50) * 2) / 100f);
213 | drawCircle(canvas, cx, cy, mColor4, pct);
214 | }
215 | if ((rawProgress >= 75 && rawProgress <= 100)) {
216 | float pct = (((rawProgress - 75) * 2) / 100f);
217 | drawCircle(canvas, cx, cy, mColor1, pct);
218 | }
219 | if (mTriggerPercentage > 0 && drawTriggerWhileFinishing) {
220 | // There is some portion of trigger to draw. Restore the canvas,
221 | // then draw the trigger. Otherwise, the trigger does not appear
222 | // until after the bar has finished animating and appears to
223 | // just jump in at a larger width than expected.
224 | canvas.restoreToCount(restoreCount);
225 | restoreCount = canvas.save();
226 | canvas.clipRect(mBounds);
227 | drawTrigger(canvas, cx, cy);
228 | }
229 | // Keep running until we finish out the last cycle.
230 | ViewCompat.postInvalidateOnAnimation(mParent);
231 | } else {
232 | // Otherwise if we're in the middle of a trigger, draw that.
233 | if (mTriggerPercentage > 0 && mTriggerPercentage <= 1.0) {
234 | drawTrigger(canvas, cx, cy);
235 | }
236 | }
237 | canvas.restoreToCount(restoreCount);
238 | }
239 |
240 | private void drawTrigger(Canvas canvas, int cx, int cy) {
241 | mPaint.setColor(mColor1);
242 | canvas.drawCircle(cx, cy, cx * mTriggerPercentage, mPaint);
243 | }
244 |
245 | /**
246 | * Draws a circle centered in the view.
247 | *
248 | * @param canvas the canvas to draw on
249 | * @param cx the center x coordinate
250 | * @param cy the center y coordinate
251 | * @param color the color to draw
252 | * @param pct the percentage of the view that the circle should cover
253 | */
254 | private void drawCircle(Canvas canvas, float cx, float cy, int color, float pct) {
255 | mPaint.setColor(color);
256 | canvas.save();
257 | canvas.translate(cx, cy);
258 | float radiusScale = INTERPOLATOR.getInterpolation(pct);
259 | canvas.scale(radiusScale, radiusScale);
260 | canvas.drawCircle(0, 0, cx, mPaint);
261 | canvas.restore();
262 | }
263 |
264 | /**
265 | * Set the drawing bounds of this SwipeProgressBar.
266 | */
267 | void setBounds(int left, int top, int right, int bottom) {
268 | mBounds.left = left;
269 | mBounds.top = top;
270 | mBounds.right = right;
271 | mBounds.bottom = bottom;
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/android/src/de/marcelpociot/collectionview/ViewItem.java:
--------------------------------------------------------------------------------
1 | package de.marcelpociot.collectionview;
2 |
3 | import java.util.HashMap;
4 |
5 | import org.appcelerator.kroll.KrollDict;
6 | import org.appcelerator.titanium.view.TiUIView;
7 |
8 | public class ViewItem {
9 | TiUIView view;
10 | KrollDict properties;
11 | KrollDict diffProperties;
12 |
13 | public ViewItem(TiUIView view, KrollDict props) {
14 | properties = new KrollDict((HashMap)props.clone());
15 | this.view = view;
16 | diffProperties = new KrollDict();
17 | }
18 |
19 | public TiUIView getView() {
20 | return view;
21 | }
22 |
23 | /**
24 | * This method compares applied properties of a view and our data model to
25 | * generate a new set of properties we need to set. It is crucial for scrolling performance.
26 | * @param properties The properties from our data model
27 | * @return The difference set of properties to set
28 | */
29 | public KrollDict generateDiffProperties(KrollDict properties) {
30 | diffProperties.clear();
31 |
32 | for (String appliedProp : this.properties.keySet()) {
33 | if (!properties.containsKey(appliedProp)) {
34 | applyProperty(appliedProp, null);
35 | }
36 | }
37 |
38 | for (String property : properties.keySet()) {
39 | Object value = properties.get(property);
40 | if (CollectionView.MUST_SET_PROPERTIES.contains(property)) {
41 | applyProperty(property, value);
42 | continue;
43 | }
44 |
45 | Object existingVal = this.properties.get(property);
46 | if (existingVal == null || value == null || !existingVal.equals(value)) {
47 | applyProperty(property, value);
48 | }
49 | }
50 | return diffProperties;
51 |
52 | }
53 |
54 | private void applyProperty(String key, Object value) {
55 | diffProperties.put(key, value);
56 | properties.put(key, value);
57 | }
58 |
59 |
60 | }
--------------------------------------------------------------------------------
/android/timodule.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-android-1.0.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-android-1.0.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-android-1.1.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-android-1.1.1.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-android-1.2.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-android-1.2.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-android-1.3.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-android-1.3.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-android-1.3.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-android-1.3.1.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.0.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.0.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.1.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.1.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.1.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.1.1.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.1.2.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.1.2.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.2.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.2.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.3.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.3.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.3.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.3.1.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.4.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.4.0.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.4.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.4.1.zip
--------------------------------------------------------------------------------
/dist/de.marcelpociot.collectionview-iphone-1.4.2.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/de.marcelpociot.collectionview-iphone-1.4.2.zip
--------------------------------------------------------------------------------
/dist/ti.collectionview-android-2.0.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/ti.collectionview-android-2.0.0.zip
--------------------------------------------------------------------------------
/dist/ti.collectionview-android-2.0.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/ti.collectionview-android-2.0.1.zip
--------------------------------------------------------------------------------
/dist/ti.collectionview-android-3.0.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/ti.collectionview-android-3.0.0.zip
--------------------------------------------------------------------------------
/dist/ti.collectionview-iphone-2.0.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/dist/ti.collectionview-iphone-2.0.1.zip
--------------------------------------------------------------------------------
/documentation/contextmenu.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/documentation/contextmenu.gif
--------------------------------------------------------------------------------
/documentation/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/documentation/grid.png
--------------------------------------------------------------------------------
/documentation/waterfall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuno/TiCollectionView/8ab6d5f8855aba4f21fc3f4c9e6d1c424c08cffc/documentation/waterfall.png
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "global": {},
3 | "env:development": {},
4 | "env:test": {},
5 | "env:production": {},
6 | "os:android": {},
7 | "os:blackberry": {},
8 | "os:ios": {},
9 | "os:mobileweb": {},
10 | "dependencies": {
11 | "nl.fokkezb.pullToRefresh": "2.0.1"
12 | }
13 | }
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/controllers/index.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $.listView.addEventListener("pull", function(e){
5 | Ti.API.info(e);
6 | });
7 |
8 | $.listView.addEventListener("pullend", function(e){
9 | Ti.API.info(e);
10 | });
11 |
12 | function myRefresher(e) {
13 |
14 | Ti.API.info("myRefresher");
15 |
16 | // fake a remote fetch
17 | setTimeout(function(){
18 | Ti.API.info("myRefresher callback");
19 | e.hide();
20 | }, 3000);
21 | }
22 |
23 | // init
24 | $.ptr.refresh();
25 |
26 | $.win.open();
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/lib/CollectionView.js:
--------------------------------------------------------------------------------
1 | // The two following statements are required so Titanium can detect the
2 | // use of ListView and RefreshControl at compile time.
3 | Ti.UI.createListView();
4 | Ti.UI.createRefreshControl();
5 |
6 | function createCollectionView(options) {
7 | if( OS_IOS )
8 | {
9 | return require("ti.collectionview").createCollectionView(options);
10 | }
11 | var templates = options.templates;
12 | for (var binding in templates) {
13 | var currentTemplate = templates[binding];
14 | //process template
15 | processTemplate(currentTemplate);
16 | //process child templates
17 | processChildTemplates(currentTemplate);
18 | }
19 | Ti.API.info( JSON.stringify(options) );
20 | var listView = require("ti.collectionview").createCollectionView(options);
21 |
22 | return listView;
23 | }
24 |
25 | //Create ListItemProxy, add events, then store it in 'tiProxy' property
26 | function processTemplate(properties) {
27 | var cellProxy = require("ti.collectionview").createCollectionItem();
28 | properties.tiProxy = cellProxy;
29 | var events = properties.events;
30 | addEventListeners(events, cellProxy);
31 | }
32 |
33 | //Recursive function that process childTemplates and append corresponding proxies to
34 | //property 'tiProxy'. I.e: type: "Titanium.UI.Label" -> tiProxy: LabelProxy object
35 | function processChildTemplates(properties) {
36 | if (!properties.hasOwnProperty('childTemplates')) return;
37 |
38 | var childProperties = properties.childTemplates;
39 | if (childProperties === void 0 || childProperties === null) return;
40 |
41 | for (var i = 0; i < childProperties.length; i++) {
42 | var child = childProperties[i];
43 | var proxyType = child.type;
44 | if (proxyType !== void 0) {
45 | var creationProperties = child.properties;
46 | var creationFunction = lookup(proxyType);
47 | var childProxy;
48 | //create the proxy
49 | if (creationProperties !== void 0) {
50 | childProxy = creationFunction(creationProperties);
51 | } else {
52 | childProxy = creationFunction();
53 | }
54 | //add event listeners
55 | var events = child.events;
56 | addEventListeners(events, childProxy);
57 | //append proxy to tiProxy property
58 | child.tiProxy = childProxy;
59 | }
60 |
61 | processChildTemplates(child);
62 |
63 | }
64 |
65 |
66 | }
67 |
68 | //add event listeners
69 | function addEventListeners(events, proxy) {
70 | if (events !== void 0) {
71 | for (var eventName in events) {
72 | proxy.addEventListener(eventName, events[eventName]);
73 | }
74 | }
75 | }
76 |
77 | //convert name of UI elements into a constructor function.
78 | //I.e: lookup("Titanium.UI.Label") returns Titanium.UI.createLabel function
79 | function lookup(name) {
80 | var lastDotIndex = name.lastIndexOf('.');
81 | var proxy = eval(name.substring(0, lastDotIndex));
82 | if (typeof(proxy) == undefined) return;
83 |
84 | var proxyName = name.slice(lastDotIndex + 1);
85 | return proxy['create' + proxyName];
86 | }
87 |
88 | exports.createCollectionView = createCollectionView;
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/styles/index.tss:
--------------------------------------------------------------------------------
1 |
2 | "Label": {
3 | width: Ti.UI.SIZE,
4 | height: Ti.UI.SIZE,
5 | color: "#000",
6 | font: {
7 | fontSize: 12
8 | }
9 | }
10 |
11 | "ListSection":{
12 | itemSpacing: 40,
13 | lineSpacing: 14
14 | }
15 |
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/views/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/widgets/nl.fokkezb.pullToRefresh/README.md:
--------------------------------------------------------------------------------
1 | # Alloy *Pull to Refresh* Widget
2 |
3 | You can find the README at [https://github.com/fokkezb/nl.fokkezb.drawer](https://github.com/fokkezb/nl.fokkezb.pullToRefresh)
4 |
5 | ## License
6 |
7 |
8 | Copyright 2013-2014 Fokke Zandbergen
9 |
10 | Licensed under the Apache License, Version 2.0 (the "License");
11 | you may not use this file except in compliance with the License.
12 | You may obtain a copy of the License at
13 |
14 | http://www.apache.org/licenses/LICENSE-2.0
15 |
16 | Unless required by applicable law or agreed to in writing, software
17 | distributed under the License is distributed on an "AS IS" BASIS,
18 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | See the License for the specific language governing permissions and
20 | limitations under the License.
21 |
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/widgets/nl.fokkezb.pullToRefresh/controllers/widget.js:
--------------------------------------------------------------------------------
1 | var moment = require('alloy/moment');
2 |
3 | var refreshControl;
4 |
5 | var isRefreshing = false;
6 | $.refresh = refresh;
7 |
8 | $.hide = hide;
9 | $.show = show;
10 |
11 | (function constructor(args) {
12 |
13 | if (!OS_IOS && !OS_ANDROID) {
14 | console.warn('[pullToRefresh] only supports iOS and Android.');
15 | return;
16 | }
17 |
18 | if (!_.isArray(args.children) || !_.contains(['Ti.UI.ListView', 'Ti.UI.TableView','de.marcelpociot.CollectionView'], args.children[0].apiName)) {
19 | console.error('[pullToRefresh] is missing required Ti.UI.ListView or Ti.UI.TableView or de.marcelpociot.CollectionView as first child element.');
20 | return;
21 | }
22 |
23 |
24 | var list = args.children[0];
25 | delete args.children;
26 |
27 | _.extend($, args);
28 |
29 | if (OS_IOS) {
30 | refreshControl = Ti.UI.createRefreshControl();
31 | var attr = Titanium.UI.iOS.createAttributedString({
32 | text: "Pull to Refresh.",
33 | attributes: [
34 |
35 | ]
36 | });
37 | refreshControl.setTitle(attr);
38 | refreshControl.addEventListener('refreshstart', onRefreshstart);
39 |
40 | list.refreshControl = refreshControl;
41 |
42 | $.addTopLevelView(list);
43 |
44 | } else if (OS_ANDROID) {
45 | refreshControl = require('com.rkam.swiperefreshlayout').createSwipeRefresh({
46 | view: list
47 | });
48 |
49 | refreshControl.addEventListener('refreshing', onRefreshstart);
50 |
51 | $.addTopLevelView(refreshControl);
52 | }
53 |
54 | })(arguments[0] || {});
55 |
56 | function refresh() {
57 | if (!isRefreshing) {
58 | isRefreshing = true;
59 | show();
60 |
61 | onRefreshstart();
62 | }
63 | }
64 |
65 | function hide() {
66 | isRefreshing = false;
67 | if (OS_IOS) {
68 | var attr = Titanium.UI.iOS.createAttributedString({
69 | text: "Last Updated: " + new moment().format("MM-DD-YYYY hh:mm:ss a"),
70 | attributes: [
71 |
72 | ]
73 | });
74 | refreshControl.setTitle(attr);
75 | refreshControl.endRefreshing();
76 |
77 | } else if (OS_ANDROID) {
78 | refreshControl.setRefreshing(false);
79 | }
80 | }
81 |
82 | function show() {
83 |
84 | if (OS_IOS) {
85 | refreshControl.beginRefreshing();
86 | } else if (OS_ANDROID) {
87 | refreshControl.setRefreshing(true);
88 | }
89 | }
90 |
91 | function onRefreshstart() {
92 |
93 | $.trigger('release', {
94 | source: $,
95 | hide: hide
96 | });
97 | }
98 |
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/widgets/nl.fokkezb.pullToRefresh/views/widget.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/SampleProjectAlloy/app/widgets/nl.fokkezb.pullToRefresh/widget.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "nl.fokkezb.pullToRefresh",
3 | "name": "Pull to Refresh Widget",
4 | "description" : "Uniform wrapper for iOS RefreshControl and Android SwipeRefreshLayout.",
5 | "author": "Fokke Zandbergen",
6 | "version": "2.0.1",
7 | "copyright":"Copyright (c) 2013-2015",
8 | "license":"Apache 2.0",
9 | "min-alloy-version": "1.5",
10 | "min-titanium-version":"3.4",
11 | "tags":"tableview,headerpullview,pulltorefresh,swiperefreshlayout,listview,pullview,refreshcontrol",
12 | "platforms":"ios,android"
13 | }
--------------------------------------------------------------------------------
/example/SampleProjectClassic/app.js:
--------------------------------------------------------------------------------
1 | var TiCollectionView = require('ti.collectionview');
2 |
3 | var win = Ti.UI.createWindow({
4 | backgroundColor: 'white'
5 | });
6 |
7 | // Create a custom template that displays an image on the left,
8 | // then a title next to it with a subtitle below it.
9 | var myTemplate = {
10 | childTemplates: [
11 | { // Title
12 | type: 'Ti.UI.Label', // Use a label for the title
13 | bindId: 'info', // Maps to a custom info property of the item data
14 | properties: { // Sets the label properties
15 | font: { fontSize: 20, fontWeight:'bold' },
16 | top: 10,
17 | }
18 | }, { // Subtitle
19 | type: 'Ti.UI.Label', // Use a label for the subtitle
20 | bindId: 'es_info', // Maps to a custom es_info property of the item data
21 | properties: { // Sets the label properties
22 | color: 'gray',
23 | font: { fontSize: 14 },
24 | top: 40,
25 | }
26 | }
27 | ]
28 | };
29 |
30 | var collectionView = TiCollectionView.createCollectionView({
31 | backgroundColor: 'white',
32 | // Maps myTemplate dictionary to 'template' string
33 | templates: { 'template': myTemplate },
34 | // Use 'template', that is, the myTemplate dict created earlier
35 | // for all items as long as the template property is not defined for an item.
36 | defaultItemTemplate: 'template',
37 |
38 | // ANDROID ONLY
39 | columnWidth: 150,
40 | verticalSpacing: 10,
41 | horizontalSpacing: 10
42 | });
43 |
44 | collectionView.addEventListener('itemclick', function(e) {
45 | alert('Tapped cell at section = ' + e.sectionIndex + ', item = ' + e.itemIndex);
46 | });
47 |
48 | var sections = [];
49 |
50 | var fruitSection = TiCollectionView.createCollectionSection({ headerTitle: 'Fruits / Frutas' });
51 | var fruitDataSet = [
52 | // the text property of info maps to the text property of the title label
53 | // the text property of es_info maps to text property of the subtitle label
54 | // the image property of pic maps to the image property of the image view
55 | { info: { text: 'Apple 1' }, es_info: { text: 'Manzana 1' }, properties: { height: 150, width: 150 } },
56 | { info: { text: 'Apple 2' }, es_info: { text: 'Manzana 2' }, properties: { height: 150, width: 150 } },
57 | { info: { text: 'Apple 3' }, es_info: { text: 'Manzana 3' }, properties: { height: 150, width: 150 } },
58 | { info: { text: 'Apple 4' }, es_info: { text: 'Manzana 4' }, properties: { height: 150, width: 150 } },
59 | { info: { text: 'Apple 5' }, es_info: { text: 'Manzana 5' }, properties: { height: 150, width: 150 } },
60 | { info: { text: 'Apple 6' }, es_info: { text: 'Manzana 6' }, properties: { height: 150, width: 150 } },
61 | ];
62 |
63 | fruitSection.setItems(fruitDataSet);
64 | sections.push(fruitSection);
65 |
66 | collectionView.setSections(sections);
67 |
68 | win.add(collectionView);
69 | win.open();
70 |
--------------------------------------------------------------------------------
/ios/Classes/CHTCollectionViewWaterfallLayout/CHTCollectionViewWaterfallLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionViewWaterfallLayout.h
3 | //
4 | // Created by Nelson on 12/11/19.
5 | // Copyright (c) 2012 Nelson Tai. All rights reserved.
6 | //
7 |
8 | #import
9 |
10 | /**
11 | * Enumerated structure to define direction in which items can be rendered.
12 | */
13 | typedef NS_ENUM (NSUInteger, CHTCollectionViewWaterfallLayoutItemRenderDirection) {
14 | CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst,
15 | CHTCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight,
16 | CHTCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft
17 | };
18 |
19 | /**
20 | * Constants that specify the types of supplementary views that can be presented using a waterfall layout.
21 | */
22 |
23 | /// A supplementary view that identifies the header for a given section.
24 | extern NSString *const CHTCollectionElementKindSectionHeader;
25 | /// A supplementary view that identifies the footer for a given section.
26 | extern NSString *const CHTCollectionElementKindSectionFooter;
27 |
28 | #pragma mark - CHTCollectionViewDelegateWaterfallLayout
29 |
30 | @class CHTCollectionViewWaterfallLayout;
31 |
32 | /**
33 | * The CHTCollectionViewDelegateWaterfallLayout protocol defines methods that let you coordinate with a
34 | * CHTCollectionViewWaterfallLayout object to implement a waterfall-based layout.
35 | * The methods of this protocol define the size of items.
36 | *
37 | * The waterfall layout object expects the collection view’s delegate object to adopt this protocol.
38 | * Therefore, implement this protocol on object assigned to your collection view’s delegate property.
39 | */
40 | @protocol CHTCollectionViewDelegateWaterfallLayout
41 | @required
42 | /**
43 | * Asks the delegate for the size of the specified item’s cell.
44 | *
45 | * @param collectionView
46 | * The collection view object displaying the waterfall layout.
47 | * @param collectionViewLayout
48 | * The layout object requesting the information.
49 | * @param indexPath
50 | * The index path of the item.
51 | *
52 | * @return
53 | * The original size of the specified item. Both width and height must be greater than 0.
54 | */
55 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
56 |
57 | @optional
58 | /**
59 | * Asks the delegate for the column count in a section
60 | *
61 | * @param collectionView
62 | * The collection view object displaying the waterfall layout.
63 | * @param collectionViewLayout
64 | * The layout object requesting the information.
65 | * @param section
66 | * The section.
67 | *
68 | * @return
69 | * The original column count for that section. Must be greater than 0.
70 | */
71 | - (NSInteger)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout columnCountForSection:(NSInteger)section;
72 |
73 | /**
74 | * Asks the delegate for the height of the header view in the specified section.
75 | *
76 | * @param collectionView
77 | * The collection view object displaying the waterfall layout.
78 | * @param collectionViewLayout
79 | * The layout object requesting the information.
80 | * @param section
81 | * The index of the section whose header size is being requested.
82 | *
83 | * @return
84 | * The height of the header. If you return 0, no header is added.
85 | *
86 | * @discussion
87 | * If you do not implement this method, the waterfall layout uses the value in its headerHeight property to set the size of the header.
88 | *
89 | * @see
90 | * headerHeight
91 | */
92 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section;
93 |
94 | /**
95 | * Asks the delegate for the height of the footer view in the specified section.
96 | *
97 | * @param collectionView
98 | * The collection view object displaying the waterfall layout.
99 | * @param collectionViewLayout
100 | * The layout object requesting the information.
101 | * @param section
102 | * The index of the section whose header size is being requested.
103 | *
104 | * @return
105 | * The height of the footer. If you return 0, no footer is added.
106 | *
107 | * @discussion
108 | * If you do not implement this method, the waterfall layout uses the value in its footerHeight property to set the size of the footer.
109 | *
110 | * @see
111 | * footerHeight
112 | */
113 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForFooterInSection:(NSInteger)section;
114 |
115 | /**
116 | * Asks the delegate for the insets in the specified section.
117 | *
118 | * @param collectionView
119 | * The collection view object displaying the waterfall layout.
120 | * @param collectionViewLayout
121 | * The layout object requesting the information.
122 | * @param section
123 | * The index of the section whose insets are being requested.
124 | *
125 | * @discussion
126 | * If you do not implement this method, the waterfall layout uses the value in its sectionInset property.
127 | *
128 | * @return
129 | * The insets for the section.
130 | */
131 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
132 |
133 | /**
134 | * Asks the delegate for the header insets in the specified section.
135 | *
136 | * @param collectionView
137 | * The collection view object displaying the waterfall layout.
138 | * @param collectionViewLayout
139 | * The layout object requesting the information.
140 | * @param section
141 | * The index of the section whose header insets are being requested.
142 | *
143 | * @discussion
144 | * If you do not implement this method, the waterfall layout uses the value in its headerInset property.
145 | *
146 | * @return
147 | * The headerInsets for the section.
148 | */
149 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForHeaderInSection:(NSInteger)section;
150 |
151 | /**
152 | * Asks the delegate for the footer insets in the specified section.
153 | *
154 | * @param collectionView
155 | * The collection view object displaying the waterfall layout.
156 | * @param collectionViewLayout
157 | * The layout object requesting the information.
158 | * @param section
159 | * The index of the section whose footer insets are being requested.
160 | *
161 | * @discussion
162 | * If you do not implement this method, the waterfall layout uses the value in its footerInset property.
163 | *
164 | * @return
165 | * The footerInsets for the section.
166 | */
167 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForFooterInSection:(NSInteger)section;
168 |
169 | /**
170 | * Asks the delegate for the minimum spacing between two items in the same column
171 | * in the specified section. If this method is not implemented, the
172 | * minimumInteritemSpacing property is used for all sections.
173 | *
174 | * @param collectionView
175 | * The collection view object displaying the waterfall layout.
176 | * @param collectionViewLayout
177 | * The layout object requesting the information.
178 | * @param section
179 | * The index of the section whose minimum interitem spacing is being requested.
180 | *
181 | * @discussion
182 | * If you do not implement this method, the waterfall layout uses the value in its minimumInteritemSpacing property to determine the amount of space between items in the same column.
183 | *
184 | * @return
185 | * The minimum interitem spacing.
186 | */
187 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
188 |
189 | /**
190 | * Asks the delegate for the minimum spacing between colums in a secified section. If this method is not implemented, the
191 | * minimumColumnSpacing property is used for all sections.
192 | *
193 | * @param collectionView
194 | * The collection view object displaying the waterfall layout.
195 | * @param collectionViewLayout
196 | * The layout object requesting the information.
197 | * @param section
198 | * The index of the section whose minimum interitem spacing is being requested.
199 | *
200 | * @discussion
201 | * If you do not implement this method, the waterfall layout uses the value in its minimumColumnSpacing property to determine the amount of space between columns in each section.
202 | *
203 | * @return
204 | * The minimum spacing between each column.
205 | */
206 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumColumnSpacingForSectionAtIndex:(NSInteger)section;
207 |
208 | @end
209 |
210 | #pragma mark - CHTCollectionViewWaterfallLayout
211 |
212 | /**
213 | * The CHTCollectionViewWaterfallLayout class is a concrete layout object that organizes items into waterfall-based grids
214 | * with optional header and footer views for each section.
215 | *
216 | * A waterfall layout works with the collection view’s delegate object to determine the size of items, headers, and footers
217 | * in each section. That delegate object must conform to the `CHTCollectionViewDelegateWaterfallLayout` protocol.
218 | *
219 | * Each section in a waterfall layout can have its own custom header and footer. To configure the header or footer for a view,
220 | * you must configure the height of the header or footer to be non zero. You can do this by implementing the appropriate delegate
221 | * methods or by assigning appropriate values to the `headerHeight` and `footerHeight` properties.
222 | * If the header or footer height is 0, the corresponding view is not added to the collection view.
223 | *
224 | * @note CHTCollectionViewWaterfallLayout doesn't support decoration view, and it supports vertical scrolling direction only.
225 | */
226 | @interface CHTCollectionViewWaterfallLayout : UICollectionViewLayout
227 |
228 | /**
229 | * @brief How many columns for this layout.
230 | * @discussion Default: 2
231 | */
232 | @property (nonatomic, assign) NSInteger columnCount;
233 |
234 | /**
235 | * @brief The minimum spacing to use between successive columns.
236 | * @discussion Default: 10.0
237 | */
238 | @property (nonatomic, assign) CGFloat minimumColumnSpacing;
239 |
240 | /**
241 | * @brief The minimum spacing to use between items in the same column.
242 | * @discussion Default: 10.0
243 | * @note This spacing is not applied to the space between header and columns or between columns and footer.
244 | */
245 | @property (nonatomic, assign) CGFloat minimumInteritemSpacing;
246 |
247 | /**
248 | * @brief Height for section header
249 | * @discussion
250 | * If your collectionView's delegate doesn't implement `collectionView:layout:heightForHeaderInSection:`,
251 | * then this value will be used.
252 | *
253 | * Default: 0
254 | */
255 | @property (nonatomic, assign) CGFloat headerHeight;
256 |
257 | /**
258 | * @brief Height for section footer
259 | * @discussion
260 | * If your collectionView's delegate doesn't implement `collectionView:layout:heightForFooterInSection:`,
261 | * then this value will be used.
262 | *
263 | * Default: 0
264 | */
265 | @property (nonatomic, assign) CGFloat footerHeight;
266 |
267 | /**
268 | * @brief The margins that are used to lay out the header for each section.
269 | * @discussion
270 | * These insets are applied to the headers in each section.
271 | * They represent the distance between the top of the collection view and the top of the content items
272 | * They also indicate the spacing on either side of the header. They do not affect the size of the headers or footers themselves.
273 | *
274 | * Default: UIEdgeInsetsZero
275 | */
276 | @property (nonatomic, assign) UIEdgeInsets headerInset;
277 |
278 | /**
279 | * @brief The margins that are used to lay out the footer for each section.
280 | * @discussion
281 | * These insets are applied to the footers in each section.
282 | * They represent the distance between the top of the collection view and the top of the content items
283 | * They also indicate the spacing on either side of the footer. They do not affect the size of the headers or footers themselves.
284 | *
285 | * Default: UIEdgeInsetsZero
286 | */
287 | @property (nonatomic, assign) UIEdgeInsets footerInset;
288 |
289 | /**
290 | * @brief The margins that are used to lay out content in each section.
291 | * @discussion
292 | * Section insets are margins applied only to the items in the section.
293 | * They represent the distance between the header view and the columns and between the columns and the footer view.
294 | * They also indicate the spacing on either side of columns. They do not affect the size of the headers or footers themselves.
295 | *
296 | * Default: UIEdgeInsetsZero
297 | */
298 | @property (nonatomic, assign) UIEdgeInsets sectionInset;
299 |
300 | /**
301 | * @brief The direction in which items will be rendered in subsequent rows.
302 | * @discussion
303 | * The direction in which each item is rendered. This could be left to right (CHTCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight), right to left (CHTCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft), or shortest column fills first (CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst).
304 | *
305 | * Default: CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst
306 | */
307 | @property (nonatomic, assign) CHTCollectionViewWaterfallLayoutItemRenderDirection itemRenderDirection;
308 |
309 | /**
310 | * @brief The minimum height of the collection view's content.
311 | * @discussion
312 | * The minimum height of the collection view's content. This could be used to allow hidden headers with no content.
313 | *
314 | * Default: 0.f
315 | */
316 | @property (nonatomic, assign) CGFloat minimumContentHeight;
317 |
318 | /**
319 | * @brief The calculated width of an item in the specified section.
320 | * @discussion
321 | * The width of an item is calculated based on number of columns, the collection view width, and the horizontal insets for that section.
322 | */
323 | - (CGFloat)itemWidthInSectionAtIndex:(NSInteger)section;
324 |
325 | @end
326 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionItem.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 | #import
8 | #import "TiGradient.h"
9 | #import "TiCollectionviewCollectionView.h"
10 | #import "TiCollectionviewCollectionItemProxy.h"
11 | #import "TiSelectedCellbackgroundView.h"
12 |
13 | enum {
14 | TiUIListItemTemplateStyleCustom = -1
15 | };
16 |
17 | @interface TiCollectionviewCollectionItem : UICollectionViewCell
18 | {
19 | TiGradientLayer * gradientLayer;
20 | TiGradient * backgroundGradient;
21 | TiGradient * selectedBackgroundGradient;
22 | }
23 |
24 | @property (nonatomic, readonly) NSInteger templateStyle;
25 | @property (nonatomic, readonly) TiCollectionviewCollectionItemProxy *proxy;
26 | @property (nonatomic, readwrite, retain) NSDictionary *dataItem;
27 |
28 | - (void)initWithProxy:(TiCollectionviewCollectionItemProxy *)proxy;
29 |
30 | - (BOOL)canApplyDataItem:(NSDictionary *)otherItem;
31 | - (void)setPosition:(int)position isGrouped:(BOOL)grouped;
32 | - (void)configureCellBackground;
33 | @end
34 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionItem.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 | #import "TiCollectionviewCollectionItem.h"
8 | #import "TiUtils.h"
9 | #import "TiViewProxy.h"
10 | #import "ImageLoader.h"
11 | #import "Webcolor.h"
12 |
13 | @implementation TiCollectionviewCollectionItem {
14 | TiCollectionviewCollectionItemProxy *_proxy;
15 | NSInteger _templateStyle;
16 | NSMutableDictionary *_initialValues;
17 | NSMutableDictionary *_currentValues;
18 | NSMutableSet *_resetKeys;
19 | NSDictionary *_dataItem;
20 | NSDictionary *_bindings;
21 | int _positionMask;
22 | BOOL _grouped;
23 | UIView* _bgView;
24 | }
25 |
26 | @synthesize templateStyle = _templateStyle;
27 | @synthesize proxy = _proxy;
28 | @synthesize dataItem = _dataItem;
29 |
30 | - (void)initWithProxy:(TiCollectionviewCollectionItemProxy *)proxy
31 | {
32 | _templateStyle = TiUIListItemTemplateStyleCustom;
33 | _initialValues = [[NSMutableDictionary alloc] initWithCapacity:10];
34 | _currentValues = [[NSMutableDictionary alloc] initWithCapacity:10];
35 | _resetKeys = [[NSMutableSet alloc] initWithCapacity:10];
36 | _proxy = proxy;
37 | _proxy.listItem = self;
38 | }
39 |
40 | - (void)dealloc
41 | {
42 | _proxy.listItem = nil;
43 | [_bgView removeFromSuperview];
44 | }
45 |
46 | - (NSDictionary *)bindings
47 | {
48 | if (_bindings == nil) {
49 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:10];
50 | [[self class] buildBindingsForViewProxy:_proxy intoDictionary:dict];
51 | _bindings = [dict copy];
52 | }
53 | return _bindings;
54 | }
55 |
56 | - (void)prepareForReuse
57 | {
58 | [super prepareForReuse];
59 | }
60 |
61 | - (void)layoutSubviews
62 | {
63 | if (_bgView != nil) {
64 | if ([_bgView superview] == nil) {
65 | [self.backgroundView addSubview:_bgView];
66 | }
67 | CGRect bounds = [self.backgroundView bounds];
68 | if ((_positionMask == TiCellBackgroundViewPositionTop) || (_positionMask == TiCellBackgroundViewPositionSingleLine) ) {
69 | [_bgView setFrame:CGRectMake(0, 1, bounds.size.width, bounds.size.height -2)];
70 | } else {
71 | [_bgView setFrame:bounds];
72 | }
73 | [_bgView setNeedsDisplay];
74 | } else if ([self.backgroundView isKindOfClass:[TiSelectedCellBackgroundView class]]) {
75 | [self.backgroundView setNeedsDisplay];
76 | }
77 | [super layoutSubviews];
78 | if (_templateStyle == TiUIListItemTemplateStyleCustom) {
79 | // prevent any crashes that could be caused by unsupported layouts
80 | _proxy.layoutProperties->layoutStyle = TiLayoutRuleAbsolute;
81 | [_proxy layoutChildren:NO];
82 | }
83 | }
84 |
85 | #pragma mark - Background Support
86 | -(BOOL) selectedOrHighlighted
87 | {
88 | return [self isSelected] || [self isHighlighted];
89 | }
90 |
91 | -(void) updateGradientLayer:(BOOL)useSelected withAnimation:(BOOL)animated
92 | {
93 | TiGradient * currentGradient = useSelected?selectedBackgroundGradient:backgroundGradient;
94 |
95 | if(currentGradient == nil)
96 | {
97 | [gradientLayer removeFromSuperlayer];
98 | //Because there's the chance that the other state still has the gradient, let's keep it around.
99 | return;
100 | }
101 |
102 |
103 | if(gradientLayer == nil)
104 | {
105 | gradientLayer = [[TiGradientLayer alloc] init];
106 | [gradientLayer setNeedsDisplayOnBoundsChange:YES];
107 | [gradientLayer setFrame:[self bounds]];
108 | }
109 |
110 | [gradientLayer setGradient:currentGradient];
111 |
112 | CALayer * ourLayer = [[[self contentView] layer] superlayer];
113 |
114 | if([gradientLayer superlayer] != ourLayer)
115 | {
116 | CALayer* contentLayer = [[self contentView] layer];
117 | [ourLayer insertSublayer:gradientLayer below:contentLayer];
118 | }
119 | if (animated) {
120 | CABasicAnimation *flash = [CABasicAnimation animationWithKeyPath:@"opacity"];
121 | flash.fromValue = [NSNumber numberWithFloat:0.0];
122 | flash.toValue = [NSNumber numberWithFloat:1.0];
123 | flash.duration = 1.0;
124 | [gradientLayer addAnimation:flash forKey:@"flashAnimation"];
125 | }
126 | [gradientLayer setNeedsDisplay];
127 | }
128 |
129 | -(void) setBackgroundGradient_:(id)value
130 | {
131 | TiGradient * newGradient = [TiGradient gradientFromObject:value proxy:_proxy];
132 | if(newGradient == backgroundGradient)
133 | {
134 | return;
135 | }
136 | backgroundGradient = newGradient;
137 |
138 | if(![self selectedOrHighlighted])
139 | {
140 | [self updateGradientLayer:NO withAnimation:NO];
141 | }
142 | }
143 |
144 | -(void) setSelectedBackgroundGradient_:(id)value
145 | {
146 | TiGradient * newGradient = [TiGradient gradientFromObject:value proxy:_proxy];
147 | if(newGradient == selectedBackgroundGradient)
148 | {
149 | return;
150 | }
151 | selectedBackgroundGradient = newGradient;
152 |
153 | if([self selectedOrHighlighted])
154 | {
155 | [self updateGradientLayer:YES withAnimation:NO];
156 | }
157 | }
158 |
159 | -(void)setPosition:(int)position isGrouped:(BOOL)grouped
160 | {
161 | _positionMask = position;
162 | _grouped = grouped;
163 | }
164 |
165 |
166 | -(BOOL)compareDataItemValue:(NSString*)theKey withItem:(NSDictionary *)otherItem
167 | {
168 | id propertiesValue = [_dataItem objectForKey:@"properties"];
169 | NSDictionary *properties = ([propertiesValue isKindOfClass:[NSDictionary class]]) ? propertiesValue : nil;
170 | id curValue = [properties objectForKey:theKey];
171 |
172 | propertiesValue = [otherItem objectForKey:@"properties"];
173 | properties = ([propertiesValue isKindOfClass:[NSDictionary class]]) ? propertiesValue : nil;
174 | id otherValue = [properties objectForKey:theKey];
175 | return ( (curValue == otherValue) || [curValue isEqual:otherValue]);
176 |
177 | }
178 |
179 | - (BOOL)canApplyDataItem:(NSDictionary *)otherItem;
180 | {
181 | id template = [_dataItem objectForKey:@"template"];
182 | id otherTemplate = [otherItem objectForKey:@"template"];
183 | BOOL same = (template == otherTemplate) || [template isEqual:otherTemplate];
184 | if (same) {
185 | same = [self compareDataItemValue:@"height" withItem:otherItem];
186 | }
187 | //These properties are applied in willDisplayCell. So force reload.
188 | if (same) {
189 | same = [self compareDataItemValue:@"backgroundColor" withItem:otherItem];
190 | }
191 | if (same) {
192 | same = [self compareDataItemValue:@"backgroundImage" withItem:otherItem];
193 | }
194 | if (same) {
195 | same = [self compareDataItemValue:@"tintColor" withItem:otherItem];
196 | }
197 | return same;
198 | }
199 |
200 | - (void)configureCellBackground
201 | {
202 | //Ensure that we store the default backgroundColor
203 | if ([_initialValues objectForKey:@"backgroundColor"] == nil) {
204 | id initialValue = nil;
205 | if (_templateStyle == TiUIListItemTemplateStyleCustom) {
206 | initialValue = [[TiUtils colorValue:[_proxy valueForKey:@"backgroundColor"]] color];
207 | }
208 | if (IS_NULL_OR_NIL(initialValue)) {
209 | initialValue = [self backgroundColor];
210 | }
211 | [_initialValues setObject:(initialValue != nil ? initialValue : [NSNull null]) forKey:@"backgroundColor"];
212 | }
213 | id propertiesValue = [_dataItem objectForKey:@"properties"];
214 | NSDictionary *properties = ([propertiesValue isKindOfClass:[NSDictionary class]]) ? propertiesValue : nil;
215 | id colorValue = [properties objectForKey:@"backgroundColor"];
216 | UIColor *color = colorValue != nil ? [[TiUtils colorValue:colorValue] _color] : nil;
217 | if (color == nil) {
218 | id initVal = [_initialValues objectForKey:@"backgroundColor"];
219 | if ([initVal isKindOfClass:[UIColor class]]) {
220 | color = initVal;
221 | } else {
222 | color = [[TiUtils colorValue:initVal] color];
223 | }
224 | }
225 | self.backgroundColor = color;
226 |
227 | //Ensure that we store the backgroundImage
228 | if ([_initialValues objectForKey:@"backgroundImage"] == nil) {
229 | id initialValue = nil;
230 | if (_templateStyle == TiUIListItemTemplateStyleCustom) {
231 | initialValue = [_proxy valueForKey:@"backgroundImage"];
232 | }
233 | [_initialValues setObject:(initialValue != nil ? initialValue : [NSNull null]) forKey:@"backgroundImage"];
234 | }
235 | id backgroundImage = [properties objectForKey:@"backgroundImage"];
236 | if (IS_NULL_OR_NIL(backgroundImage)) {
237 | backgroundImage = [_initialValues objectForKey:@"backgroundImage"];
238 | }
239 | UIImage* bgImage = [[ImageLoader sharedLoader] loadImmediateStretchableImage:[TiUtils toURL:backgroundImage proxy:_proxy] withLeftCap:TiDimensionAuto topCap:TiDimensionAuto];
240 | if (_grouped && ![TiUtils isIOS7OrGreater]) {
241 | UIView* superView = [self backgroundView];
242 | if (bgImage != nil) {
243 | if (![_bgView isKindOfClass:[UIImageView class]]) {
244 | [_bgView removeFromSuperview];
245 | _bgView = [[UIImageView alloc] initWithFrame:CGRectZero];
246 | _bgView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
247 | [superView addSubview:_bgView];
248 | }
249 | [(UIImageView*)_bgView setImage:bgImage];
250 | [_bgView setBackgroundColor:[UIColor clearColor]];
251 | } else {
252 | [_bgView removeFromSuperview];
253 | }
254 | } else {
255 | if (bgImage != nil) {
256 | //Set the backgroundView to ImageView and set its backgroundColor to bgColor
257 | if ([self.backgroundView isKindOfClass:[UIImageView class]]) {
258 | [(UIImageView*)self.backgroundView setImage:bgImage];
259 | [(UIImageView*)self.backgroundView setBackgroundColor:[UIColor clearColor]];
260 | } else {
261 | UIImageView *view_ = [[UIImageView alloc] initWithFrame:CGRectZero];
262 | [view_ setImage:bgImage];
263 | [view_ setBackgroundColor:[UIColor clearColor]];
264 | self.backgroundView = view_;
265 | }
266 | } else {
267 | self.backgroundView = nil;
268 | }
269 | }
270 |
271 | }
272 |
273 | - (void)setDataItem:(NSDictionary *)dataItem
274 | {
275 | _dataItem = dataItem;
276 | [_resetKeys addObjectsFromArray:[_currentValues allKeys]];
277 | id propertiesValue = [dataItem objectForKey:@"properties"];
278 | NSDictionary *properties = ([propertiesValue isKindOfClass:[NSDictionary class]]) ? propertiesValue : nil;
279 | switch (_templateStyle) {
280 | default:
281 | [dataItem enumerateKeysAndObjectsUsingBlock:^(NSString *bindId, id dict, BOOL *stop) {
282 | if (![dict isKindOfClass:[NSDictionary class]] || [bindId isEqualToString:@"properties"]) {
283 | return;
284 | }
285 | id bindObject = [self valueForUndefinedKey:bindId];
286 | if (bindObject != nil) {
287 | BOOL reproxying = NO;
288 | if ([bindObject isKindOfClass:[TiProxy class]]) {
289 | [bindObject setReproxying:YES];
290 | reproxying = YES;
291 | }
292 | [(NSDictionary *)dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
293 | NSString *keyPath = [NSString stringWithFormat:@"%@.%@", bindId, key];
294 | if ([self shouldUpdateValue:value forKeyPath:keyPath]) {
295 | [self recordChangeValue:value forKeyPath:keyPath withBlock:^{
296 | [bindObject setValue:value forKey:key];
297 | }];
298 | }
299 | }];
300 | if (reproxying) {
301 | [bindObject setReproxying:NO];
302 | }
303 | }
304 | }];
305 | break;
306 | }
307 |
308 | id backgroundGradientValue = [properties objectForKey:@"backgroundGradient"];
309 | if (IS_NULL_OR_NIL(backgroundGradientValue)) {
310 | backgroundGradientValue = [_proxy valueForKey:@"backgroundGradient"];
311 | }
312 | [self setBackgroundGradient_:backgroundGradientValue];
313 |
314 |
315 | id selectedBackgroundGradientValue = [properties objectForKey:@"selectedBackgroundGradient"];
316 | if (IS_NULL_OR_NIL(selectedBackgroundGradientValue)) {
317 | selectedBackgroundGradientValue = [_proxy valueForKey:@"selectedBackgroundGradient"];
318 | }
319 | [self setSelectedBackgroundGradient_:selectedBackgroundGradientValue];
320 |
321 | [_resetKeys enumerateObjectsUsingBlock:^(NSString *keyPath, BOOL *stop) {
322 | id value = [_initialValues objectForKey:keyPath];
323 | [self setValue:(value != [NSNull null] ? value : nil) forKeyPath:keyPath];
324 | [_currentValues removeObjectForKey:keyPath];
325 | }];
326 | [_resetKeys removeAllObjects];
327 | }
328 |
329 | - (id)valueForUndefinedKey:(NSString *)key
330 | {
331 | return [self.bindings objectForKey:key];
332 | }
333 |
334 | - (void)recordChangeValue:(id)value forKeyPath:(NSString *)keyPath withBlock:(void(^)(void))block
335 | {
336 | if ([_initialValues objectForKey:keyPath] == nil) {
337 | id initialValue = [self valueForKeyPath:keyPath];
338 | [_initialValues setObject:(initialValue != nil ? initialValue : [NSNull null]) forKey:keyPath];
339 | }
340 | block();
341 | if (value != nil) {
342 | [_currentValues setObject:value forKey:keyPath];
343 | } else {
344 | [_currentValues removeObjectForKey:keyPath];
345 | }
346 | [_resetKeys removeObject:keyPath];
347 | }
348 |
349 | - (BOOL)shouldUpdateValue:(id)value forKeyPath:(NSString *)keyPath
350 | {
351 | id current = [_currentValues objectForKey:keyPath];
352 | BOOL sameValue = ((current == value) || [current isEqual:value]);
353 | if (sameValue) {
354 | [_resetKeys removeObject:keyPath];
355 | }
356 | return !sameValue;
357 | }
358 |
359 | #pragma mark - Static
360 |
361 | + (void)buildBindingsForViewProxy:(TiViewProxy *)viewProxy intoDictionary:(NSMutableDictionary *)dict
362 | {
363 | [viewProxy.children enumerateObjectsUsingBlock:^(TiViewProxy *childViewProxy, NSUInteger idx, BOOL *stop) {
364 | [[self class] buildBindingsForViewProxy:childViewProxy intoDictionary:dict];
365 | }];
366 | id bindId = [viewProxy valueForKey:@"bindId"];
367 | if (bindId != nil) {
368 | [dict setObject:viewProxy forKey:bindId];
369 | }
370 | }
371 |
372 | @end
373 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionItemProxy.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 |
8 | #import "TiViewProxy.h"
9 |
10 | @class TiCollectionviewCollectionItem;
11 | @class TiCollectionviewCollectionViewProxy;
12 |
13 | @interface TiCollectionviewCollectionItemProxy : TiViewProxy
14 |
15 | @property (nonatomic, readwrite, assign) TiCollectionviewCollectionItem *listItem;
16 | @property (nonatomic, readwrite, retain) NSIndexPath *indexPath;
17 |
18 | - (id)initWithListViewProxy:(TiCollectionviewCollectionViewProxy *)listViewProxy inContext:(id)context;
19 | -(void)deregisterProxy:(id)context;
20 | @end
21 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionItemProxy.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 | #import "TiCollectionviewCollectionItemProxy.h"
8 | #import "TiUtils.h"
9 | #import "TiCollectionviewCollectionItem.h"
10 | #import "TiCollectionviewCollectionViewProxy.h"
11 |
12 | static void SetEventOverrideDelegateRecursive(NSArray *children, id eventOverrideDelegate);
13 |
14 | @implementation TiCollectionviewCollectionItemProxy {
15 | TiCollectionviewCollectionViewProxy *_listViewProxy; // weak
16 | }
17 |
18 | @synthesize listItem = _listItem;
19 | @synthesize indexPath = _indexPath;
20 |
21 | -(NSString*)apiName
22 | {
23 | return @"Ti.CollectionItem";
24 | }
25 |
26 | - (id)initWithListViewProxy:(TiCollectionviewCollectionViewProxy *)listViewProxy inContext:(id)context
27 | {
28 | self = [self _initWithPageContext:context];
29 | if (self) {
30 | _listViewProxy = listViewProxy;
31 | [context.krollContext invokeBlockOnThread:^{
32 | [context registerProxy:self];
33 | //Reusable cell will keep native proxy alive.
34 | //This proxy will keep its JS object alive.
35 | [self rememberSelf];
36 | }];
37 | self.modelDelegate = self;
38 | }
39 | return self;
40 | }
41 |
42 | +(BOOL)shouldRegisterOnInit
43 | {
44 | //Since this is initialized on main thread,
45 | //there is no need to register on init. Registration
46 | //done later on JS thread (See above)
47 | return NO;
48 | }
49 |
50 | -(void)deregisterProxy:(id)context
51 | {
52 | //Aggressive removal of children on deallocation of cell
53 | [self removeAllChildren:nil];
54 | [self windowDidClose];
55 | //Go ahead and unprotect JS object and mark context closed
56 | //(Since cell no longer exists, the proxy is inaccessible)
57 | [context.krollContext invokeBlockOnThread:^{
58 | [self forgetSelf];
59 | [self contextShutdown:context];
60 | }];
61 | }
62 |
63 | - (id)init
64 | {
65 | self = [super init];
66 | if (self) {
67 | viewInitialized = YES;
68 | [self windowWillOpen];
69 | [self windowDidOpen];
70 | [self willShow];
71 | }
72 | return self;
73 | }
74 |
75 | - (TiUIView *)view
76 | {
77 | return nil;
78 | }
79 |
80 | -(BOOL)viewAttached
81 | {
82 | return _listItem != nil;
83 | }
84 |
85 | -(UIView *)parentViewForChild:(TiViewProxy *)child
86 | {
87 | return _listItem.contentView;
88 | }
89 |
90 | -(void)propertyChanged:(NSString*)key oldValue:(id)oldValue newValue:(id)newValue proxy:(TiProxy*)proxy_
91 | {
92 |
93 | }
94 |
95 | - (void)unarchiveFromTemplate:(id)viewTemplate
96 | {
97 | [super unarchiveFromTemplate:viewTemplate];
98 | SetEventOverrideDelegateRecursive(self.children, self);
99 | }
100 |
101 | -(BOOL)canHaveControllerParent
102 | {
103 | return NO;
104 | }
105 |
106 | #pragma mark - TiViewEventOverrideDelegate
107 |
108 | - (NSDictionary *)overrideEventObject:(NSDictionary *)eventObject forEvent:(NSString *)eventType fromViewProxy:(TiViewProxy *)viewProxy
109 | {
110 | NSMutableDictionary *updatedEventObject = [eventObject mutableCopy];
111 | [updatedEventObject setObject:NUMINTEGER(_indexPath.section) forKey:@"sectionIndex"];
112 | [updatedEventObject setObject:NUMINTEGER(_indexPath.row) forKey:@"itemIndex"];
113 | [updatedEventObject setObject:[_listViewProxy sectionForIndex:_indexPath.section] forKey:@"section"];
114 | id propertiesValue = [_listItem.dataItem objectForKey:@"properties"];
115 | NSDictionary *properties = ([propertiesValue isKindOfClass:[NSDictionary class]]) ? propertiesValue : nil;
116 | id itemId = [properties objectForKey:@"itemId"];
117 | if (itemId != nil) {
118 | [updatedEventObject setObject:itemId forKey:@"itemId"];
119 | }
120 | id bindId = [viewProxy valueForKey:@"bindId"];
121 | if (bindId != nil) {
122 | [updatedEventObject setObject:bindId forKey:@"bindId"];
123 | }
124 | return updatedEventObject;
125 | }
126 |
127 | @end
128 |
129 | static void SetEventOverrideDelegateRecursive(NSArray *children, id eventOverrideDelegate)
130 | {
131 | [children enumerateObjectsUsingBlock:^(TiViewProxy *child, NSUInteger idx, BOOL *stop) {
132 | child.eventOverrideDelegate = eventOverrideDelegate;
133 | SetEventOverrideDelegateRecursive(child.children, eventOverrideDelegate);
134 | }];
135 | }
136 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionSectionProxy.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 |
8 | #import "TiBase.h"
9 | #import "TiProxy.h"
10 |
11 | @protocol TiCollectionviewCollectionViewDelegate
12 | @required
13 |
14 | - (void)dispatchUpdateAction:(void(^)(UICollectionView *tableView))block;
15 | - (id)dispatchBlockWithResult:(id(^)(void))block;
16 |
17 | @end
18 |
19 | @interface TiCollectionviewCollectionSectionProxy : TiProxy < TiCollectionviewCollectionViewDelegate >
20 |
21 | @property (nonatomic, readwrite, assign) id delegate;
22 | @property (nonatomic, readwrite, assign) NSUInteger sectionIndex;
23 |
24 | // Private API. Used by ListView directly. Not for public comsumption
25 | - (NSDictionary *)itemAtIndex:(NSUInteger)index;
26 | - (void) deleteItemAtIndex:(NSUInteger)index;
27 | - (void) addItem:(NSDictionary*)item atIndex:(NSUInteger)index;
28 |
29 | // Public API
30 | @property (nonatomic, readonly) NSUInteger itemCount;
31 | @property (nonatomic, readonly) NSArray *items;
32 | @property (nonatomic, readwrite, copy) NSString *headerTitle;
33 | @property (nonatomic, readwrite, copy) NSString *footerTitle;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionSectionProxy.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 |
8 | #import "TiCollectionviewCollectionSectionProxy.h"
9 | #import "TiCollectionviewCollectionViewProxy.h"
10 | #import "TiCollectionviewCollectionView.h"
11 | #import "TiCollectionviewCollectionItem.h"
12 |
13 | @interface TiCollectionviewCollectionSectionProxy ()
14 | @property (nonatomic, readonly) id dispatcher;
15 | @end
16 |
17 | @implementation TiCollectionviewCollectionSectionProxy {
18 | NSMutableArray *_items;
19 | }
20 |
21 | @synthesize delegate = _delegate;
22 | @synthesize sectionIndex = _sectionIndex;
23 | @synthesize headerTitle = _headerTitle;
24 | @synthesize footerTitle = _footerTitle;
25 |
26 | -(NSString*)apiName
27 | {
28 | return @"Ti.CollectionSection";
29 | }
30 |
31 | - (id)init
32 | {
33 | self = [super init];
34 | if (self) {
35 | _items = [[NSMutableArray alloc] initWithCapacity:20];
36 | }
37 | return self;
38 | }
39 |
40 | - (id)dispatcher
41 | {
42 | return _delegate != nil ? _delegate : self;
43 | }
44 |
45 | // These API's are used by the ListView directly. Not for public consumption
46 | - (NSDictionary *)itemAtIndex:(NSUInteger)index
47 | {
48 | if (index < [_items count]) {
49 | id item = [_items objectAtIndex:index];
50 | if ([item isKindOfClass:[NSDictionary class]]) {
51 | return item;
52 | }
53 | }
54 | return nil;
55 | }
56 |
57 | - (void) deleteItemAtIndex:(NSUInteger)index
58 | {
59 | if ([_items count] <= index) {
60 | DLog(@"[WARN] ListSectionProxy: deleteItemAtIndex index is out of range");
61 | } else {
62 | [_items removeObjectAtIndex:index];
63 | }
64 | }
65 |
66 | - (void) addItem:(NSDictionary*)item atIndex:(NSUInteger)index
67 | {
68 | if (index > [_items count]) {
69 | DLog(@"[WARN] ListSectionProxy: addItem:atIndex: index is out of range");
70 | } else {
71 | if (index == [_items count]) {
72 | [_items addObject:item];
73 | } else {
74 | [_items insertObject:item atIndex:index];
75 | }
76 | }
77 | }
78 |
79 |
80 |
81 | #pragma mark - Public API
82 |
83 | - (NSArray *)items
84 | {
85 | return [self.dispatcher dispatchBlockWithResult:^() {
86 | return [_items copy];
87 | }];
88 | }
89 |
90 | - (NSUInteger)itemCount
91 | {
92 | return [[self.dispatcher dispatchBlockWithResult:^() {
93 | return [NSNumber numberWithUnsignedInteger:[_items count]];
94 | }] unsignedIntegerValue];
95 | }
96 |
97 | - (id)getItemAt:(id)args
98 | {
99 | ENSURE_ARG_COUNT(args, 1);
100 | NSUInteger itemIndex = [TiUtils intValue:[args objectAtIndex:0]];
101 | return [self.dispatcher dispatchBlockWithResult:^() {
102 | return (itemIndex < [_items count]) ? [_items objectAtIndex:itemIndex] : nil;
103 | }];
104 | }
105 |
106 | - (void)setItems:(id)args
107 | {
108 | [self setItems:args withObject:[NSDictionary dictionaryWithObject:NUMINT(UITableViewRowAnimationNone) forKey:@"animationStyle"]];
109 | }
110 |
111 | - (void)setItems:(id)args withObject:(id)properties
112 | {
113 | ENSURE_TYPE_OR_NIL(args,NSArray);
114 | NSArray *items = args;
115 | NSUInteger oldCount = [_items count];
116 | NSUInteger newCount = [items count];
117 | if ( (oldCount != newCount)) {
118 | NSUInteger minCount = MIN(oldCount, newCount);
119 | NSUInteger maxCount = MAX(oldCount, newCount);
120 | NSUInteger diffCount = maxCount - minCount;
121 |
122 | //Dispath block for difference
123 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
124 | [_items setArray:items];
125 | NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:diffCount];
126 | for (NSUInteger i = 0; i < diffCount; ++i) {
127 | [indexPaths addObject:[NSIndexPath indexPathForRow:(minCount + i) inSection:_sectionIndex]];
128 | }
129 | if (newCount > oldCount) {
130 | [tableView insertItemsAtIndexPaths:indexPaths];
131 | } else {
132 | [tableView deleteItemsAtIndexPaths:indexPaths];
133 | }
134 | }];
135 |
136 | //Dispatch block for common items
137 | if (minCount > 0) {
138 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
139 | NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:minCount];
140 | for (NSUInteger i = 0; i < minCount; ++i) {
141 | [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:_sectionIndex]];
142 | }
143 | [tableView reloadItemsAtIndexPaths:indexPaths];
144 | }];
145 | }
146 |
147 | } else {
148 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
149 | [_items setArray:items];
150 | [tableView reloadSections:[NSIndexSet indexSetWithIndex:_sectionIndex]];
151 | }];
152 | }
153 | }
154 |
155 | - (void)appendItems:(id)args
156 | {
157 | ENSURE_ARG_COUNT(args, 1);
158 | NSArray *items = [args objectAtIndex:0];
159 | if ([items count] == 0) {
160 | return;
161 | }
162 | ENSURE_TYPE_OR_NIL(items,NSArray);
163 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
164 | NSUInteger insertIndex = [_items count];
165 | [_items addObjectsFromArray:items];
166 | NSUInteger count = [items count];
167 | NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:count];
168 | for (NSUInteger i = 0; i < count; ++i) {
169 | [indexPaths addObject:[NSIndexPath indexPathForRow:insertIndex+i inSection:_sectionIndex]];
170 | }
171 | [tableView insertItemsAtIndexPaths:indexPaths];
172 | }];
173 | }
174 |
175 | - (void)insertItemsAt:(id)args
176 | {
177 | ENSURE_ARG_COUNT(args, 2);
178 | NSUInteger insertIndex = [TiUtils intValue:[args objectAtIndex:0]];
179 | NSArray *items = [args objectAtIndex:1];
180 | if ([items count] == 0) {
181 | return;
182 | }
183 | ENSURE_TYPE_OR_NIL(items,NSArray);
184 |
185 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
186 | if ([_items count] < insertIndex) {
187 | DLog(@"[WARN] ListView: Insert item index is out of range");
188 | return;
189 | }
190 | [_items replaceObjectsInRange:NSMakeRange(insertIndex, 0) withObjectsFromArray:items];
191 | NSUInteger count = [items count];
192 | NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:count];
193 | for (NSUInteger i = 0; i < count; ++i) {
194 | [indexPaths addObject:[NSIndexPath indexPathForRow:insertIndex+i inSection:_sectionIndex]];
195 | }
196 | [tableView insertItemsAtIndexPaths:indexPaths];
197 | }];
198 | }
199 |
200 | - (void)replaceItemsAt:(id)args
201 | {
202 | ENSURE_ARG_COUNT(args, 3);
203 | NSUInteger insertIndex = [TiUtils intValue:[args objectAtIndex:0]];
204 | NSUInteger replaceCount = [TiUtils intValue:[args objectAtIndex:1]];
205 | NSArray *items = [args objectAtIndex:2];
206 | ENSURE_TYPE_OR_NIL(items,NSArray);
207 |
208 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
209 | if ([_items count] < insertIndex) {
210 | DLog(@"[WARN] ListView: Replace item index is out of range");
211 | return;
212 | }
213 | NSUInteger actualReplaceCount = MIN(replaceCount, [_items count]-insertIndex);
214 | [_items replaceObjectsInRange:NSMakeRange(insertIndex, actualReplaceCount) withObjectsFromArray:items];
215 | NSUInteger count = [items count];
216 | NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:MAX(count, actualReplaceCount)];
217 | for (NSUInteger i = 0; i < actualReplaceCount; ++i) {
218 | [indexPaths addObject:[NSIndexPath indexPathForRow:insertIndex+i inSection:_sectionIndex]];
219 | }
220 | if (actualReplaceCount > 0) {
221 | [tableView deleteItemsAtIndexPaths:indexPaths];
222 | }
223 | [indexPaths removeAllObjects];
224 | for (NSUInteger i = 0; i < count; ++i) {
225 | [indexPaths addObject:[NSIndexPath indexPathForRow:insertIndex+i inSection:_sectionIndex]];
226 | }
227 | if (count > 0) {
228 | [tableView insertItemsAtIndexPaths:indexPaths];
229 | }
230 | }];
231 | }
232 |
233 | - (void)deleteItemsAt:(id)args
234 | {
235 | ENSURE_ARG_COUNT(args, 2);
236 | NSUInteger deleteIndex = [TiUtils intValue:[args objectAtIndex:0]];
237 | NSUInteger deleteCount = [TiUtils intValue:[args objectAtIndex:1]];
238 | if (deleteCount == 0) {
239 | return;
240 | }
241 |
242 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
243 | if ([_items count] <= deleteIndex) {
244 | DLog(@"[WARN] ListView: Delete item index is out of range");
245 | return;
246 | }
247 | NSUInteger actualDeleteCount = MIN(deleteCount, [_items count]-deleteIndex);
248 | if (actualDeleteCount == 0) {
249 | return;
250 | }
251 | [_items removeObjectsInRange:NSMakeRange(deleteIndex, actualDeleteCount)];
252 | NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:actualDeleteCount];
253 | for (NSUInteger i = 0; i < actualDeleteCount; ++i) {
254 | [indexPaths addObject:[NSIndexPath indexPathForRow:deleteIndex+i inSection:_sectionIndex]];
255 | }
256 | [tableView deleteItemsAtIndexPaths:indexPaths];
257 | }];
258 | }
259 |
260 | - (void)updateItemAt:(id)args
261 | {
262 | ENSURE_ARG_COUNT(args, 2);
263 | NSUInteger itemIndex = [TiUtils intValue:[args objectAtIndex:0]];
264 | NSDictionary *item = [args objectAtIndex:1];
265 | ENSURE_TYPE_OR_NIL(item,NSDictionary);
266 |
267 | [self.dispatcher dispatchUpdateAction:^(UICollectionView *tableView) {
268 | if ([_items count] <= itemIndex) {
269 | DLog(@"[WARN] ListView: Update item index is out of range");
270 | return;
271 | }
272 |
273 | if (item != nil) {
274 | [_items replaceObjectAtIndex:itemIndex withObject:item];
275 | }
276 |
277 | NSArray *indexPaths = [[NSArray alloc] initWithObjects:[NSIndexPath indexPathForRow:itemIndex inSection:_sectionIndex], nil];
278 | BOOL forceReload = NO;
279 | if (!forceReload) {
280 | TiCollectionviewCollectionItem *cell = (TiCollectionviewCollectionItem *)[tableView cellForItemAtIndexPath:[indexPaths objectAtIndex:0]];
281 | if ((cell != nil) && ([cell canApplyDataItem:item])) {
282 | cell.dataItem = item;
283 | } else {
284 | forceReload = YES;
285 | }
286 | }
287 | if (forceReload) {
288 | [tableView reloadItemsAtIndexPaths:indexPaths];
289 | }
290 | }];
291 | }
292 |
293 | #pragma mark - TiUIListViewDelegate
294 |
295 | - (void)dispatchUpdateAction:(void (^)(UICollectionView *))block
296 | {
297 | block(nil);
298 | }
299 |
300 | - (id)dispatchBlockWithResult:(id (^)(void))block
301 | {
302 | return block();
303 | }
304 |
305 | @end
306 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionView.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 |
8 | #import "TiUIView.h"
9 | #import "TiCollectionviewCollectionViewProxy.h"
10 | #import "CHTCollectionViewWaterfallLayout.h"
11 | #import "TiSearchDisplayController.h"
12 |
13 | typedef enum {
14 | kLayoutTypeGrid,
15 | kLayoutTypeWaterfall
16 | } LayoutType;
17 |
18 | typedef enum {
19 | kScrollHorizontal,
20 | kScrollVertical
21 | } ScrollDirection;
22 |
23 | @interface TiCollectionviewCollectionView : TiUIView
24 |
25 | #pragma mark - Private APIs
26 |
27 | @property (nonatomic, readonly) UICollectionView *collectionView;
28 | @property (nonatomic, readonly) BOOL isSearchActive;
29 |
30 | - (void)setDictTemplates_:(id)args;
31 | - (void)setContentInsets_:(id)value withObject:(id)props;
32 | - (void)updateIndicesForVisibleRows;
33 | - (void)updateSearchResults:(id)unused;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionViewProxy.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 |
8 | #import "TiViewProxy.h"
9 | #import "TiCollectionviewCollectionSectionProxy.h"
10 |
11 | @interface TiCollectionviewCollectionViewProxy : TiViewProxy < TiCollectionviewCollectionViewDelegate >
12 |
13 | @property (nonatomic, readonly) NSArray *sections;
14 | @property (nonatomic, readonly) NSNumber *sectionCount;
15 |
16 | - (TiCollectionviewCollectionSectionProxy *)sectionForIndex:(NSUInteger)index;
17 | - (void) deleteSectionAtIndex:(NSUInteger)index;
18 | - (void) setMarker:(id)args;
19 | @end
20 |
21 | @interface TiCollectionviewCollectionViewProxy (internal)
22 | -(void)willDisplayCell:(NSIndexPath*)indexPath;
23 | @end
24 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewCollectionViewProxy.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Appcelerator Titanium Mobile
3 | * Copyright (c) 2013 by Appcelerator, Inc. All Rights Reserved.
4 | * Licensed under the terms of the Apache Public License
5 | * Please see the LICENSE included with this distribution for details.
6 | */
7 |
8 | #import "TiCollectionviewCollectionViewProxy.h"
9 | #import "TiCollectionviewCollectionView.h"
10 | #import "TiUtils.h"
11 | #import "TiViewTemplate.h"
12 |
13 | @interface TiCollectionviewCollectionViewProxy ()
14 | @property (nonatomic, readwrite) TiCollectionviewCollectionView *listView;
15 | @end
16 |
17 | @implementation TiCollectionviewCollectionViewProxy {
18 | NSMutableArray *_sections;
19 | NSMutableArray *_operationQueue;
20 | pthread_mutex_t _operationQueueMutex;
21 | pthread_rwlock_t _markerLock;
22 | NSIndexPath *marker;
23 | }
24 |
25 | -(NSString*)apiName
26 | {
27 | return @"Ti.CollectionView";
28 | }
29 |
30 | - (id)init
31 | {
32 | self = [super init];
33 | if (self) {
34 | _sections = [[NSMutableArray alloc] initWithCapacity:4];
35 | _operationQueue = [[NSMutableArray alloc] initWithCapacity:10];
36 | pthread_mutex_init(&_operationQueueMutex,NULL);
37 | pthread_rwlock_init(&_markerLock,NULL);
38 | }
39 | return self;
40 | }
41 |
42 | -(void)_initWithProperties:(NSDictionary *)properties
43 | {
44 | [self initializeProperty:@"canScroll" defaultValue:NUMBOOL(YES)];
45 | [super _initWithProperties:properties];
46 | }
47 |
48 | - (void)dealloc
49 | {
50 | pthread_mutex_destroy(&_operationQueueMutex);
51 | pthread_rwlock_destroy(&_markerLock);
52 | }
53 |
54 | - (TiCollectionviewCollectionView *)listView
55 | {
56 | return (TiCollectionviewCollectionView *)self.view;
57 | }
58 |
59 | - (void)dispatchUpdateAction:(void(^)(UICollectionView *tableView))block
60 | {
61 | if (view == nil) {
62 | block(nil);
63 | return;
64 | }
65 |
66 | if ([self.listView isSearchActive]) {
67 | block(nil);
68 | TiThreadPerformOnMainThread(^{
69 | [self.listView updateSearchResults:nil];
70 | }, [NSThread isMainThread]);
71 | return;
72 | }
73 |
74 | BOOL triggerMainThread;
75 | pthread_mutex_lock(&_operationQueueMutex);
76 | triggerMainThread = [_operationQueue count] == 0;
77 | [_operationQueue addObject:(id)block];
78 | pthread_mutex_unlock(&_operationQueueMutex);
79 | if (triggerMainThread) {
80 | TiThreadPerformOnMainThread(^{
81 | [self processUpdateActions];
82 | }, [NSThread isMainThread]);
83 | }
84 | }
85 |
86 | - (void)dispatchBlock:(void(^)(UICollectionView *tableView))block
87 | {
88 | if (view == nil) {
89 | block(nil);
90 | return;
91 | }
92 | if ([NSThread isMainThread]) {
93 | return block(self.listView.collectionView);
94 | }
95 | TiThreadPerformOnMainThread(^{
96 | block(self.listView.collectionView);
97 | }, YES);
98 | }
99 |
100 | - (id)dispatchBlockWithResult:(id(^)(void))block
101 | {
102 | if ([NSThread isMainThread]) {
103 | return block();
104 | }
105 |
106 | __block id result = nil;
107 | TiThreadPerformOnMainThread(^{
108 | result = block();
109 | }, YES);
110 | return result;
111 | }
112 |
113 | - (void)processUpdateActions
114 | {
115 | UICollectionView *tableView = self.listView.collectionView;
116 | BOOL removeHead = NO;
117 | while (YES) {
118 | void (^block)(UICollectionView *) = nil;
119 | pthread_mutex_lock(&_operationQueueMutex);
120 | if (removeHead) {
121 | [_operationQueue removeObjectAtIndex:0];
122 | }
123 | if ([_operationQueue count] > 0) {
124 | block = [_operationQueue objectAtIndex:0];
125 | removeHead = YES;
126 | }
127 | pthread_mutex_unlock(&_operationQueueMutex);
128 | if (block != nil) {
129 | block(tableView);
130 | } else {
131 | [self.listView updateIndicesForVisibleRows];
132 | [self contentsWillChange];
133 | return;
134 | }
135 | }
136 | }
137 |
138 | - (TiCollectionviewCollectionSectionProxy *)sectionForIndex:(NSUInteger)index
139 | {
140 | if (index < [_sections count]) {
141 | return [_sections objectAtIndex:index];
142 | }
143 | return nil;
144 | }
145 |
146 | - (void) deleteSectionAtIndex:(NSUInteger)index
147 | {
148 | if ([_sections count] <= index) {
149 | DLog(@"[WARN] ListViewProxy: Delete section index is out of range");
150 | return;
151 | }
152 | TiCollectionviewCollectionSectionProxy *section = [_sections objectAtIndex:index];
153 | [_sections removeObjectAtIndex:index];
154 | section.delegate = nil;
155 | [_sections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
156 | section.sectionIndex = idx;
157 | }];
158 | [self forgetProxy:section];
159 | }
160 |
161 | - (NSArray *)keySequence
162 | {
163 | static dispatch_once_t onceToken;
164 | static NSArray *keySequence = nil;
165 | dispatch_once(&onceToken, ^{
166 | keySequence = [[NSArray alloc] initWithObjects:@"style", @"templates", @"defaultItemTemplate", @"sections", @"backgroundColor",nil];
167 | });
168 | return keySequence;
169 | }
170 |
171 | - (void)viewDidAttach
172 | {
173 | [self.listView collectionView];
174 | }
175 |
176 | - (void)willShow
177 | {
178 | [super willShow];
179 | }
180 |
181 | #pragma mark - Public API
182 |
183 | - (void)setTemplates:(id)args
184 | {
185 | ENSURE_TYPE_OR_NIL(args,NSDictionary);
186 | NSMutableDictionary *templates = [[NSMutableDictionary alloc] initWithCapacity:[args count]];
187 | [(NSDictionary *)args enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
188 | TiViewTemplate *template = [TiViewTemplate templateFromViewTemplate:obj];
189 | if (template != nil) {
190 | [templates setObject:template forKey:key];
191 | }
192 | }];
193 | TiThreadPerformOnMainThread(^{
194 | [self.listView setDictTemplates_:templates];
195 | }, [NSThread isMainThread]);
196 | }
197 |
198 | - (NSArray *)sections
199 | {
200 | return [self dispatchBlockWithResult:^() {
201 | return [_sections copy];
202 | }];
203 | }
204 |
205 | - (NSNumber *)sectionCount
206 | {
207 | return [self dispatchBlockWithResult:^() {
208 | return [NSNumber numberWithUnsignedInteger:[_sections count]];
209 | }];
210 | }
211 |
212 | - (void)setSections:(id)args
213 | {
214 | ENSURE_TYPE_OR_NIL(args,NSArray);
215 | NSMutableArray *insertedSections = [args mutableCopy];
216 | [insertedSections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
217 | ENSURE_TYPE(section, TiCollectionviewCollectionSectionProxy);
218 | [self rememberProxy:section];
219 | }];
220 | [self dispatchBlock:^(UICollectionView *tableView) {
221 | [_sections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
222 | section.delegate = nil;
223 | if (![insertedSections containsObject:section]) {
224 | [self forgetProxy:section];
225 | }
226 | }];
227 | _sections = insertedSections;
228 | [_sections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
229 | section.delegate = self;
230 | section.sectionIndex = idx;
231 | }];
232 | [tableView reloadData];
233 | [self contentsWillChange];
234 | }];
235 | }
236 |
237 | - (void)appendSection:(id)args
238 | {
239 | ENSURE_ARG_COUNT(args, 1);
240 | id arg = [args objectAtIndex:0];
241 | NSArray *appendedSections = [arg isKindOfClass:[NSArray class]] ? arg : [NSArray arrayWithObject:arg];
242 | if ([appendedSections count] == 0) {
243 | return;
244 | }
245 |
246 | [appendedSections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
247 | ENSURE_TYPE(section, TiCollectionviewCollectionSectionProxy);
248 | [self rememberProxy:section];
249 | }];
250 | [self dispatchUpdateAction:^(UICollectionView *tableView) {
251 | NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
252 | [appendedSections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
253 | if (![_sections containsObject:section]) {
254 | NSUInteger insertIndex = [_sections count];
255 | [_sections addObject:section];
256 | section.delegate = self;
257 | section.sectionIndex = insertIndex;
258 | [indexSet addIndex:insertIndex];
259 | } else {
260 | DLog(@"[WARN] ListView: Attempt to append exising section");
261 | }
262 | }];
263 | if ([indexSet count] > 0) {
264 | [tableView insertSections:indexSet];
265 | }
266 | }];
267 | }
268 |
269 | - (void)deleteSectionAt:(id)args
270 | {
271 | ENSURE_ARG_COUNT(args, 1);
272 | NSUInteger deleteIndex = [TiUtils intValue:[args objectAtIndex:0]];
273 | [self dispatchUpdateAction:^(UICollectionView *tableView) {
274 | if ([_sections count] <= deleteIndex) {
275 | DLog(@"[WARN] ListView: Delete section index is out of range");
276 | return;
277 | }
278 | TiCollectionviewCollectionSectionProxy *section = [_sections objectAtIndex:deleteIndex];
279 | [_sections removeObjectAtIndex:deleteIndex];
280 | section.delegate = nil;
281 | [_sections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
282 | section.sectionIndex = idx;
283 | }];
284 | [tableView deleteSections:[NSIndexSet indexSetWithIndex:deleteIndex]];
285 | [self forgetProxy:section];
286 | }];
287 | }
288 |
289 | - (void)insertSectionAt:(id)args
290 | {
291 | ENSURE_ARG_COUNT(args, 2);
292 | NSUInteger insertIndex = [TiUtils intValue:[args objectAtIndex:0]];
293 | id arg = [args objectAtIndex:1];
294 | NSArray *insertSections = [arg isKindOfClass:[NSArray class]] ? arg : [NSArray arrayWithObject:arg];
295 | if ([insertSections count] == 0) {
296 | return;
297 | }
298 | [insertSections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
299 | ENSURE_TYPE(section, TiCollectionviewCollectionSectionProxy);
300 | [self rememberProxy:section];
301 | }];
302 | [self dispatchUpdateAction:^(UICollectionView *tableView) {
303 | if ([_sections count] < insertIndex) {
304 | DLog(@"[WARN] ListView: Insert section index is out of range");
305 | [insertSections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
306 | [self forgetProxy:section];
307 | }];
308 | return;
309 | }
310 | NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
311 | __block NSUInteger index = insertIndex;
312 | [insertSections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
313 | if (![_sections containsObject:section]) {
314 | [_sections insertObject:section atIndex:index];
315 | section.delegate = self;
316 | [indexSet addIndex:index];
317 | ++index;
318 | } else {
319 | DLog(@"[WARN] ListView: Attempt to insert exising section");
320 | }
321 | }];
322 | [_sections enumerateObjectsUsingBlock:^(TiCollectionviewCollectionSectionProxy *section, NSUInteger idx, BOOL *stop) {
323 | section.sectionIndex = idx;
324 | }];
325 | [tableView insertSections:indexSet];
326 | }];
327 | }
328 |
329 | - (void)replaceSectionAt:(id)args
330 | {
331 | ENSURE_ARG_COUNT(args, 2);
332 | NSUInteger replaceIndex = [TiUtils intValue:[args objectAtIndex:0]];
333 | TiCollectionviewCollectionSectionProxy *section = [args objectAtIndex:1];
334 | ENSURE_TYPE_OR_NIL(section, TiCollectionviewCollectionSectionProxy);
335 |
336 | [self rememberProxy:section];
337 | [self dispatchUpdateAction:^(UICollectionView *tableView) {
338 | if ([_sections containsObject:section]) {
339 | DebugLog(@"[WARN] ListView: Attempt to insert exising section");
340 | return;
341 | }
342 | if ([_sections count] <= replaceIndex) {
343 | DebugLog(@"[WARN] ListView: Replace section index is out of range");
344 | [self forgetProxy:section];
345 | return;
346 | }
347 | TiCollectionviewCollectionSectionProxy *prevSection = [_sections objectAtIndex:replaceIndex];
348 | prevSection.delegate = nil;
349 | if (section != nil) {
350 | [_sections replaceObjectAtIndex:replaceIndex withObject:section];
351 | section.delegate = self;
352 | section.sectionIndex = replaceIndex;
353 | }
354 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:replaceIndex];
355 | [tableView deleteSections:indexSet];
356 | [tableView insertSections:indexSet];
357 | [self forgetProxy:prevSection];
358 | }];
359 | }
360 |
361 | - (void)scrollToItem:(id)args
362 | {
363 | if (view != nil) {
364 | ENSURE_ARG_COUNT(args, 2);
365 | NSUInteger sectionIndex = [TiUtils intValue:[args objectAtIndex:0]];
366 | NSUInteger itemIndex = [TiUtils intValue:[args objectAtIndex:1]];
367 | NSDictionary *properties = [args count] > 2 ? [args objectAtIndex:2] : nil;
368 | UICollectionViewScrollPosition scrollPosition = [TiUtils intValue:@"position" properties:properties def:UICollectionViewScrollPositionNone];
369 | BOOL animated = [TiUtils boolValue:@"animated" properties:properties def:YES];
370 | TiThreadPerformOnMainThread(^{
371 | if ([_sections count] <= sectionIndex) {
372 | DLog(@"[WARN] ListView: Scroll to section index is out of range");
373 | return;
374 | }
375 | TiCollectionviewCollectionSectionProxy *section = [_sections objectAtIndex:sectionIndex];
376 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:MIN(itemIndex, section.itemCount) inSection:sectionIndex];
377 | [self.listView.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
378 | }, [NSThread isMainThread]);
379 | }
380 | }
381 |
382 | - (void)selectItem:(id)args
383 | {
384 | if (view != nil) {
385 | ENSURE_ARG_COUNT(args, 2);
386 | NSUInteger sectionIndex = [TiUtils intValue:[args objectAtIndex:0]];
387 | NSUInteger itemIndex = [TiUtils intValue:[args objectAtIndex:1]];
388 | TiThreadPerformOnMainThread(^{
389 | if ([_sections count] <= sectionIndex) {
390 | DLog(@"[WARN] ListView: Select section index is out of range");
391 | return;
392 | }
393 | TiCollectionviewCollectionSectionProxy *section = [_sections objectAtIndex:sectionIndex];
394 | if (section.itemCount <= itemIndex) {
395 | DLog(@"[WARN] ListView: Select item index is out of range");
396 | return;
397 | }
398 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:itemIndex inSection:sectionIndex];
399 | [self.listView.collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone];
400 | [self.listView.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
401 | }, NO);
402 | }
403 | }
404 |
405 | - (void)deselectItem:(id)args
406 | {
407 | if (view != nil) {
408 | ENSURE_ARG_COUNT(args, 2);
409 | NSUInteger sectionIndex = [TiUtils intValue:[args objectAtIndex:0]];
410 | NSUInteger itemIndex = [TiUtils intValue:[args objectAtIndex:1]];
411 | TiThreadPerformOnMainThread(^{
412 | if ([_sections count] <= sectionIndex) {
413 | DLog(@"[WARN] ListView: Select section index is out of range");
414 | return;
415 | }
416 | TiCollectionviewCollectionSectionProxy *section = [_sections objectAtIndex:sectionIndex];
417 | if (section.itemCount <= itemIndex) {
418 | DLog(@"[WARN] ListView: Select item index is out of range");
419 | return;
420 | }
421 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:itemIndex inSection:sectionIndex];
422 | [self.listView.collectionView deselectItemAtIndexPath:indexPath animated:YES];
423 | }, [NSThread isMainThread]);
424 | }
425 | }
426 |
427 | -(void)setContentInsets:(id)args
428 | {
429 | id arg1;
430 | id arg2;
431 | if ([args isKindOfClass:[NSDictionary class]]) {
432 | arg1 = args;
433 | arg2 = nil;
434 | }
435 | else {
436 | arg1 = [args objectAtIndex:0];
437 | arg2 = [args count] > 1 ? [args objectAtIndex:1] : nil;
438 | }
439 | TiThreadPerformOnMainThread(^{
440 | [self.listView setContentInsets_:arg1 withObject:arg2];
441 | }, NO);
442 | }
443 |
444 | #pragma mark - Marker Support
445 | - (void)setMarker:(id)args;
446 | {
447 | ENSURE_SINGLE_ARG(args, NSDictionary);
448 | pthread_rwlock_wrlock(&_markerLock);
449 | int section = [TiUtils intValue:[args objectForKey:@"sectionIndex"] def:-1];
450 | int row = [TiUtils intValue:[args objectForKey:@"itemIndex"] def:-1];
451 | marker = [NSIndexPath indexPathForRow:row inSection:section];
452 | pthread_rwlock_unlock(&_markerLock);
453 | }
454 |
455 | -(void)willDisplayCell:(NSIndexPath*)indexPath
456 | {
457 | if ((marker != nil) && [self _hasListeners:@"marker"]) {
458 | //Never block the UI thread
459 | int result = pthread_rwlock_tryrdlock(&_markerLock);
460 | if (result != 0) {
461 | return;
462 | }
463 | if ( (indexPath.section > marker.section) || ( (marker.section == indexPath.section) && (indexPath.row >= marker.row) ) ){
464 | [self fireEvent:@"marker" withObject:nil withSource:self propagate:NO reportSuccess:NO errorCode:0 message:nil];
465 | }
466 | pthread_rwlock_unlock(&_markerLock);
467 | }
468 | }
469 |
470 | DEFINE_DEF_BOOL_PROP(willScrollOnStatusTap,YES);
471 | USE_VIEW_FOR_CONTENT_HEIGHT
472 | USE_VIEW_FOR_CONTENT_WIDTH
473 |
474 | @end
475 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewHeaderFooterReusableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TiCollectionviewHeaderFooterReusableView.h
3 | // TiCollectionView
4 | //
5 | // Created by Ayorinde Adesugba on 1/28/15.
6 | //
7 | //
8 |
9 | #import
10 |
11 | @interface TiCollectionviewHeaderFooterReusableView : UICollectionReusableView
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewHeaderFooterReusableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TiCollectionviewHeaderFooterReusableView.m
3 | // TiCollectionView
4 | //
5 | // Created by Ayorinde Adesugba on 1/28/15.
6 | //
7 | //
8 |
9 | #import "TiCollectionviewHeaderFooterReusableView.h"
10 |
11 | @implementation TiCollectionviewHeaderFooterReusableView
12 |
13 | - (id)initWithFrame:(CGRect)frame
14 | {
15 | self = [super initWithFrame:frame];
16 | if (self) {
17 | // Initialization code
18 | //[self setBounds:CGRectMake(0, 0, 320.f, 50.f)];
19 | //[self setBackgroundColor:[UIColor yellowColor]];
20 |
21 | }
22 | return self;
23 | }
24 |
25 | - (id)initWithCoder:(NSCoder *)aDecoder
26 | {
27 | self = [super initWithCoder:aDecoder];
28 | if (self) {
29 | // Initialization code
30 |
31 | }
32 | return self;
33 | }
34 |
35 | - (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
36 | {
37 |
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewModule.h:
--------------------------------------------------------------------------------
1 | /**
2 | * TiCollectionView
3 | *
4 | * Created by Your Name
5 | * Copyright (c) 2014 Your Company. All rights reserved.
6 | */
7 |
8 | #import "TiModule.h"
9 |
10 | @interface TiCollectionviewModule : TiModule
11 | {
12 | NSNumber* LAYOUT_GRID;
13 | NSNumber* LAYOUT_WATERFALL;
14 |
15 | NSNumber* DIRECTION_LEFT_TO_RIGHT;
16 | NSNumber* DIRECTION_RIGHT_TO_LEFT;
17 | NSNumber* DIRECTION_SHORTEST_FIRST;
18 | }
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewModule.m:
--------------------------------------------------------------------------------
1 | /**
2 | * TiCollectionView
3 | *
4 | * Created by Your Name
5 | * Copyright (c) 2014 Your Company. All rights reserved.
6 | */
7 |
8 | #import "TiCollectionviewModule.h"
9 | #import "TiBase.h"
10 | #import "TiHost.h"
11 | #import "TiUtils.h"
12 | #import "TiCollectionviewCollectionView.h"
13 | #import "CHTCollectionViewWaterfallLayout.h"
14 |
15 | @implementation TiCollectionviewModule
16 |
17 |
18 | #pragma mark Internal
19 |
20 | // this is generated for your module, please do not change it
21 | -(id)moduleGUID
22 | {
23 | return @"eef8bad5-aef3-49fb-bb8d-7cb781e9e7f5";
24 | }
25 |
26 | // this is generated for your module, please do not change it
27 | -(NSString*)moduleId
28 | {
29 | return @"Ti.collectionview";
30 | }
31 |
32 | MAKE_SYSTEM_PROP(LAYOUT_GRID, kLayoutTypeGrid);
33 | MAKE_SYSTEM_PROP(LAYOUT_WATERFALL, kLayoutTypeWaterfall );
34 |
35 | MAKE_SYSTEM_PROP(DIRECTION_LEFT_TO_RIGHT,CHTCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight);
36 | MAKE_SYSTEM_PROP(DIRECTION_RIGHT_TO_LEFT, CHTCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft);
37 | MAKE_SYSTEM_PROP(DIRECTION_SHORTEST_FIRST, CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst);
38 |
39 | MAKE_SYSTEM_PROP(SCROLL_HORIZONTAL, kScrollHorizontal);
40 | MAKE_SYSTEM_PROP(SCROLL_VERTICAL, kScrollVertical);
41 |
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewModuleAssets.h:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a generated file. Do not edit or your changes will be lost
3 | */
4 |
5 | @interface TiCollectionviewModuleAssets : NSObject
6 | {
7 | }
8 | - (NSData*) moduleAsset;
9 | - (NSData*) resolveModuleAsset:(NSString*)path;
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/ios/Classes/TiCollectionviewModuleAssets.m:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a generated file. Do not edit or your changes will be lost
3 | */
4 | #import "TiCollectionviewModuleAssets.h"
5 |
6 | extern NSData* filterDataInRange(NSData* thedata, NSRange range);
7 |
8 | @implementation TiCollectionviewModuleAssets
9 |
10 | - (NSData*) moduleAsset
11 | {
12 |
13 |
14 | return nil;
15 | }
16 |
17 | - (NSData*) resolveModuleAsset:(NSString*)path
18 | {
19 |
20 |
21 | return nil;
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/ios/Classes/TiSearchDisplayController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TiSearchDisplayController.h
3 | // TiCollectionView
4 | //
5 | // Created by Ayorinde Adesugba on 1/29/15.
6 | //
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @protocol TiSearchDisplayDelegate;
13 |
14 | @interface TiSearchDisplayController : NSObject
15 |
16 | - (id)initWithSearchBar:(UISearchBar *)searchBar contentsController:(UIViewController *)viewController;
17 | - (void)setActive:(BOOL)visible animated:(BOOL)animated;
18 |
19 | @property(nonatomic,assign) id delegate;
20 | @property(nonatomic, getter = isActive) BOOL active;
21 | @property(nonatomic, readonly) UISearchBar *searchBar;
22 | @property(nonatomic, readonly) UIViewController *searchContentsController;
23 | @property(nonatomic, readonly) UICollectionView *searchResultsCollectionView;
24 | @property(nonatomic, assign) id searchResultsDataSource;
25 | @property(nonatomic, assign) id searchResultsDelegate;
26 |
27 | @end
28 |
29 |
30 |
31 | @protocol TiSearchDisplayDelegate
32 |
33 | @optional
34 |
35 | - (void)searchDisplayControllerWillBeginSearch:(TiSearchDisplayController *)controller;
36 | - (void)searchDisplayControllerDidBeginSearch:(TiSearchDisplayController *)controller;
37 | - (void)searchDisplayControllerWillEndSearch:(TiSearchDisplayController *)controller;
38 | - (void)searchDisplayControllerDidEndSearch:(TiSearchDisplayController *)controller;
39 | - (void)textDidChange:(NSString *)searchText;
40 | - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope;
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ios/Classes/TiSearchDisplayController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TiSearchDisplayController.m
3 | // TiCollectionView
4 | //
5 | // Created by Ayorinde Adesugba on 1/29/15.
6 | //
7 | //
8 |
9 | #import "TiBase.h"
10 | #import "TiSearchDisplayController.h"
11 |
12 | @implementation TiSearchDisplayController
13 |
14 | - (id)initWithSearchBar:(UISearchBar *)searchBar contentsController:(UIViewController *)viewController {
15 | self = [super init];
16 |
17 | if (self) {
18 | _searchBar = searchBar;
19 | _searchBar.delegate = self;
20 | _searchContentsController = viewController;
21 |
22 | CGFloat y = 64.0f;
23 | CGFloat height = _searchContentsController.view.frame.size.height - y;
24 |
25 | UICollectionViewFlowLayout* layout = [[UICollectionViewFlowLayout alloc] init];
26 | _searchResultsCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0.0f, y, _searchContentsController.view.frame.size.width, height) collectionViewLayout:layout];
27 | _searchResultsCollectionView.scrollsToTop = NO;
28 | }
29 |
30 | return self;
31 | }
32 |
33 | - (void)setSearchResultsDataSource:(id)searchResultsDataSource {
34 | _searchResultsCollectionView.dataSource = searchResultsDataSource;
35 | }
36 |
37 | - (void)setSearchResultsDelegate:(id)searchResultsDelegate {
38 | _searchResultsCollectionView.delegate = searchResultsDelegate;
39 | }
40 |
41 | - (void)setActive:(BOOL)visible animated:(BOOL)animated {
42 | if (!visible) {
43 | [_searchBar resignFirstResponder];
44 | _searchBar.text = nil;
45 | _searchBar.showsCancelButton = NO;
46 | }
47 |
48 | if (visible && [self.delegate respondsToSelector:@selector(searchDisplayControllerWillBeginSearch:)]) {
49 | [self.delegate searchDisplayControllerWillBeginSearch:self];
50 | } else if (!visible && [self.delegate respondsToSelector:@selector(searchDisplayControllerWillEndSearch:)]) {
51 | [self.delegate searchDisplayControllerWillEndSearch:self];
52 | }
53 |
54 | [_searchContentsController.navigationController setNavigationBarHidden:visible animated:YES];
55 |
56 | float alpha = 0;
57 |
58 | if (visible) {
59 | [_searchContentsController.view addSubview:_searchResultsCollectionView];
60 | alpha = 0.2;
61 | }
62 |
63 | #pragma clang diagnostic push
64 | #pragma clang diagnostic ignored "-Wundeclared-selector"
65 | if ([_searchContentsController.view respondsToSelector:@selector(scrollEnabled)]) {
66 | ((UIScrollView *)_searchContentsController.view).scrollEnabled = !visible;
67 | }
68 | #pragma clang diagnostic pop
69 |
70 | if (animated) {
71 | [UIView animateWithDuration:0.2 animations:^{
72 | _searchResultsCollectionView.alpha = alpha;
73 | } completion:^(BOOL finished) {
74 | self.active = visible;
75 | }];
76 | } else {
77 | _searchResultsCollectionView.alpha = alpha;
78 | }
79 | }
80 |
81 | #pragma mark - UISearchBarDelegate
82 |
83 | - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {
84 | if ([self.delegate respondsToSelector:@selector(searchBar:selectedScopeButtonIndexDidChange:)]) {
85 | [self.delegate searchBar:searchBar selectedScopeButtonIndexDidChange:selectedScope];
86 | }
87 | }
88 |
89 | - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
90 | if ([self.delegate respondsToSelector:@selector(textDidChange:)]) {
91 | [self.delegate textDidChange:searchText];
92 | }
93 | }
94 |
95 | - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
96 | [searchBar setShowsCancelButton:YES animated:YES];
97 | [self setActive:YES animated:YES];
98 | [_searchResultsCollectionView reloadData];
99 | }
100 |
101 | - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
102 | [_searchResultsCollectionView reloadData];
103 | }
104 |
105 | - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
106 | [self setActive:NO animated:YES];
107 | [self.searchResultsCollectionView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
108 | }
109 |
110 |
111 | @end
112 |
--------------------------------------------------------------------------------
/ios/TiCollectionViewWorkspace.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/TiCollectionview_Prefix.pch:
--------------------------------------------------------------------------------
1 |
2 | #ifdef __OBJC__
3 | #import
4 |
5 | // Debug Logging
6 | //#if defined(DEBUG) || (TARGET_OS_SIMULATOR)
7 | //#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
8 | //#else
9 | #define DLog(...) {}
10 | //#endif
11 |
12 | #endif
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ios/manifest:
--------------------------------------------------------------------------------
1 | #
2 | # this is your module manifest and used by Titanium
3 | # during compilation, packaging, distribution, etc.
4 | #
5 | version: 3.0.1
6 | apiversion: 2
7 | description: TiCollectionView
8 | author: Marcel Pociot
9 | license: MIT
10 | copyright: Copyright (c) 2014-2015 by Marcel Pociot, 2015-Present by Nuno Costa
11 | architectures: armv7 i386 x86_64 arm64
12 |
13 | # these should not be edited
14 | name: TiCollectionView
15 | moduleid: ti.collectionview
16 | guid: eef8bad5-aef3-49fb-bb8d-7cb781e9e7f5
17 | platform: iphone
18 | minsdk: 5.5.1.GA
19 |
--------------------------------------------------------------------------------
/ios/metadata.json:
--------------------------------------------------------------------------------
1 | {"exports":[]}
--------------------------------------------------------------------------------
/ios/module.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // PLACE ANY BUILD DEFINITIONS IN THIS FILE AND THEY WILL BE
3 | // PICKED UP DURING THE APP BUILD FOR YOUR MODULE
4 | //
5 | // see the following webpage for instructions on the settings
6 | // for this file:
7 | // http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/XcodeBuildSystem/400-Build_Configurations/build_configs.html
8 | //
9 |
10 | //
11 | // How to add a Framework (example)
12 | //
13 | // OTHER_LDFLAGS=$(inherited) -framework Foo
14 | //
15 | // Adding a framework for a specific version(s) of iPhone:
16 | //
17 | // OTHER_LDFLAGS[sdk=iphoneos4*]=$(inherited) -framework Foo
18 | // OTHER_LDFLAGS[sdk=iphonesimulator4*]=$(inherited) -framework Foo
19 | //
20 | //
21 | // How to add a compiler define:
22 | //
23 | // OTHER_CFLAGS=$(inherited) -DFOO=1
24 | //
25 | //
26 | // IMPORTANT NOTE: always use $(inherited) in your overrides
27 | //
28 |
--------------------------------------------------------------------------------
/ios/timodule.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ios/titanium.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // CHANGE THESE VALUES TO REFLECT THE VERSION (AND LOCATION IF DIFFERENT)
4 | // OF YOUR TITANIUM SDK YOU'RE BUILDING FOR
5 | //
6 | //
7 | TITANIUM_SDK_VERSION = 7.0.1.GA
8 |
9 |
10 | //
11 | // THESE SHOULD BE OK GENERALLY AS-IS
12 | //
13 | TITANIUM_SDK = ~/Library/Application Support/Titanium/mobilesdk/osx/$(TITANIUM_SDK_VERSION)
14 | TITANIUM_BASE_SDK = "$(TITANIUM_SDK)/iphone/include"
15 | TITANIUM_BASE_SDK2 = "$(TITANIUM_SDK)/iphone/include/TiCore"
16 | TITANIUM_BASE_SDK3 = "$(TITANIUM_SDK)/iphone/include/JavaScriptCore"
17 | TITANIUM_BASE_SDK4 = "$(TITANIUM_SDK)/iphone/include/APSHTTPClient"
18 | HEADER_SEARCH_PATHS= $(TITANIUM_BASE_SDK) $(TITANIUM_BASE_SDK2) $(TITANIUM_BASE_SDK3) $(TITANIUM_BASE_SDK4)
19 |
--------------------------------------------------------------------------------
/lib/CollectionView.js:
--------------------------------------------------------------------------------
1 | function createCollectionView(options) {
2 | if( OS_IOS )
3 | {
4 | return require("ti.collectionview").createCollectionView(options);
5 | }
6 | var templates = options.templates;
7 | for (var binding in templates) {
8 | var currentTemplate = templates[binding];
9 | //process template
10 | processTemplate(currentTemplate);
11 | //process child templates
12 | processChildTemplates(currentTemplate);
13 | }
14 | Ti.API.info( JSON.stringify(options) );
15 | var listView = require("ti.collectionview").createCollectionView(options);
16 |
17 | return listView;
18 | }
19 |
20 | //Create ListItemProxy, add events, then store it in 'tiProxy' property
21 | function processTemplate(properties) {
22 | var cellProxy = require("ti.collectionview").createCollectionItem();
23 | properties.tiProxy = cellProxy;
24 | var events = properties.events;
25 | addEventListeners(events, cellProxy);
26 | }
27 |
28 | //Recursive function that process childTemplates and append corresponding proxies to
29 | //property 'tiProxy'. I.e: type: "Titanium.UI.Label" -> tiProxy: LabelProxy object
30 | function processChildTemplates(properties) {
31 | if (!properties.hasOwnProperty('childTemplates')) return;
32 |
33 | var childProperties = properties.childTemplates;
34 | if (childProperties === void 0 || childProperties === null) return;
35 |
36 | for (var i = 0; i < childProperties.length; i++) {
37 | var child = childProperties[i];
38 | var proxyType = child.type;
39 | if (proxyType !== void 0) {
40 | var creationProperties = child.properties;
41 | var creationFunction = lookup(proxyType);
42 | var childProxy;
43 | //create the proxy
44 | if (creationProperties !== void 0) {
45 | childProxy = creationFunction(creationProperties);
46 | } else {
47 | childProxy = creationFunction();
48 | }
49 | //add event listeners
50 | var events = child.events;
51 | addEventListeners(events, childProxy);
52 | //append proxy to tiProxy property
53 | child.tiProxy = childProxy;
54 | }
55 |
56 | processChildTemplates(child);
57 |
58 | }
59 |
60 |
61 | }
62 |
63 | //add event listeners
64 | function addEventListeners(events, proxy) {
65 | if (events !== void 0) {
66 | for (var eventName in events) {
67 | proxy.addEventListener(eventName, events[eventName]);
68 | }
69 | }
70 | }
71 |
72 | //convert name of UI elements into a constructor function.
73 | //I.e: lookup("Titanium.UI.Label") returns Titanium.UI.createLabel function
74 | function lookup(name) {
75 | var lastDotIndex = name.lastIndexOf('.');
76 | var proxy = eval(name.substring(0, lastDotIndex));
77 | if (typeof(proxy) == undefined) return;
78 |
79 | var proxyName = name.slice(lastDotIndex + 1);
80 | return proxy['create' + proxyName];
81 | }
82 |
83 | exports.createCollectionView = createCollectionView;
84 |
--------------------------------------------------------------------------------