/ActionScript3/pl/deluxe/controls/ScrollableList.as

https://github.com/dogeroski/CodeReview · ActionScript · 406 lines · 286 code · 70 blank · 50 comment · 40 complexity · 9904f3a664f5a65365de089d64df32e4 MD5 · raw file

  1. package pl.deluxe.controls
  2. {
  3. import com.gskinner.motion.GTween;
  4. import com.gskinner.motion.easing.Exponential;
  5. import flash.display.DisplayObject;
  6. import flash.display.Sprite;
  7. import flash.events.Event;
  8. import pl.deluxe.display.IItemRenderer;
  9. import pl.deluxe.utils.GraphicsUtil;
  10. /**
  11. * Component for displaying items in horizontal or vertical list.
  12. */
  13. public class ScrollableList extends List
  14. {
  15. public static const DIRECTION_HORIZONTAL : String = "horizontal";
  16. public static const DIRECTION_VERTICAL : String = "vertical";
  17. private var _direction : String = DIRECTION_HORIZONTAL;
  18. private var _width : Number;
  19. private var _height : Number;
  20. private var _contentSize : Number;
  21. private var _itemsPerPage : uint;
  22. private var _itemSize : Number;
  23. private var _gap : Number;
  24. private var _tween : GTween;
  25. private var _container : Sprite;
  26. private var _mask : Sprite;
  27. private var _startIndex : int = 0;
  28. private var _tweenDirection : int;
  29. private var _animated : Boolean;
  30. /**
  31. * Constructor.
  32. *
  33. * @param width Width of the component.
  34. * @param height Height of the component.
  35. * @param itemsPerPage How many items should be displayed per page.
  36. * @param itemSize Width or height (depending on direction) of the item in list (required for calculating positions).
  37. * @param gap Gap between items.
  38. * @param mask If <code>true</code> then list will be masked according to its width and height.
  39. * @param tweenDuration Duration of the tween.
  40. */
  41. public function ScrollableList(width : Number,
  42. height : Number,
  43. itemsPerPage : uint,
  44. itemSize : Number,
  45. gap : Number,
  46. mask : Boolean = true,
  47. tweenDuration : Number = 0.6,
  48. animated : Boolean = true)
  49. {
  50. super();
  51. _width = width;
  52. _height = height;
  53. _itemsPerPage = itemsPerPage;
  54. _itemSize = itemSize;
  55. _contentSize = 0;
  56. _gap = gap;
  57. _container = new Sprite();
  58. _mask = GraphicsUtil.getSpriteBox(width, height);
  59. _tween = new GTween(_container, tweenDuration, null, {ease: Exponential.easeOut});
  60. _tween.onComplete = _tweenCompleteHandler;
  61. _tween.onChange = _onTweenChange;
  62. _animated = animated;
  63. addChild(_container);
  64. if(mask)
  65. {
  66. _container.mask = _mask;
  67. addChild(_mask);
  68. }
  69. }
  70. override protected function _addedToStageHandler(event : Event) : void
  71. {
  72. super._addedToStageHandler(event);
  73. reset();
  74. }
  75. private function _tweenCompleteHandler(tween : GTween) : void
  76. {
  77. var n : uint;
  78. var renderer : DisplayObject;
  79. if(_tweenDirection == 1)
  80. {
  81. n = _container.numChildren - itemsPerPage;
  82. for(var i:uint=0; i < n; i++)
  83. {
  84. renderer = _container.getChildAt(0);
  85. if(renderer && _container.contains(renderer))
  86. {
  87. _container.removeChild(renderer);
  88. _unuseRenderer(renderer as IItemRenderer);
  89. }
  90. }
  91. }
  92. if(_tweenDirection == -1)
  93. {
  94. n = _container.numChildren;
  95. for(i=itemsPerPage; i < n; i++)
  96. {
  97. renderer = _container.getChildAt(_container.numChildren-1);
  98. if(renderer && _container.contains(renderer))
  99. {
  100. _container.removeChild(renderer);
  101. _unuseRenderer(renderer as IItemRenderer);
  102. }
  103. }
  104. }
  105. }
  106. private function _onTweenChange(tween : GTween) : void
  107. {
  108. _container.x = Math.round(_container.x);
  109. _container.y = Math.round(_container.y);
  110. }
  111. /**
  112. * Scrolls list left (or top if direction is vertical).
  113. *
  114. * @param numItems How many items should be scrolled.
  115. */
  116. public function scrollLeft(numItems : uint = 1) : void
  117. {
  118. var oldIndex : int = _startIndex;
  119. var index : int = _startIndex - numItems;
  120. index = index < 0 ? 0 : index;
  121. if(oldIndex == index) return;
  122. var i : int = oldIndex - numItems;
  123. i = i < 0 ? 0 : i;
  124. var j : uint = 0;
  125. for(i; i < oldIndex; i++)
  126. {
  127. var item : DisplayObject = _getRenderer(dataProvider[oldIndex - j - 1]) as DisplayObject;
  128. var firstItem : DisplayObject = _container.getChildAt(0);
  129. IItemRenderer(item).itemIndex = i;
  130. if(direction == DIRECTION_HORIZONTAL)
  131. {
  132. item.x = firstItem.x;
  133. item.x -= (_itemSize ? _itemSize : item.width) + _gap;
  134. }
  135. if(direction == DIRECTION_VERTICAL)
  136. {
  137. item.y = firstItem.y;
  138. item.y -= (_itemSize ? _itemSize : item.height) + _gap;
  139. }
  140. _container.addChildAt(item, 0);
  141. j++;
  142. }
  143. _tweenDirection = -1;
  144. startIndex = index;
  145. }
  146. /**
  147. * Scrolls list right (or bottom if direction is vertical).
  148. *
  149. * @param numItems How many items should be scrolled.
  150. */
  151. public function scrollRight(numItems : uint = 1) : void
  152. {
  153. var oldIndex : int = _startIndex;
  154. var maxIndex : int = dataProvider.length - itemsPerPage ;
  155. var index : int = _startIndex;
  156. maxIndex = maxIndex < 0 ? 0 : maxIndex;
  157. index = index + numItems;
  158. index = index > maxIndex ? maxIndex : index;
  159. if(oldIndex == index) return;
  160. var i : int = oldIndex + itemsPerPage;
  161. var n : int = i + numItems;
  162. i = i >= dataProvider.length ? dataProvider.length : i;
  163. n = n >= dataProvider.length ? dataProvider.length : n;
  164. for(i; i < n; i++)
  165. {
  166. var item : DisplayObject = _getRenderer(dataProvider[i]) as DisplayObject;
  167. var lastItem : DisplayObject = _container.getChildAt(_container.numChildren-1);
  168. IItemRenderer(item).itemIndex = i;
  169. if(direction == DIRECTION_HORIZONTAL)
  170. {
  171. item.y = 0;
  172. item.x = lastItem.x;
  173. item.x += (_itemSize ? _itemSize : lastItem.width) + _gap;
  174. }
  175. if(direction == DIRECTION_VERTICAL)
  176. {
  177. item.x = 0;
  178. item.y = lastItem.y;
  179. item.y += (_itemSize ? _itemSize : lastItem.height) + _gap;
  180. }
  181. _container.addChild(item);
  182. }
  183. _tweenDirection = 1;
  184. startIndex = index;
  185. }
  186. /**
  187. * Resets list. This method should be called after some minor changes, like updating the <code>dataProvider</code>.
  188. */
  189. public function reset() : void
  190. {
  191. _tween.end();
  192. while(_container.numChildren)
  193. {
  194. var renderer : IItemRenderer = _container.removeChildAt(0) as IItemRenderer;
  195. _unuseRenderer(renderer);
  196. }
  197. _container.x = 0;
  198. _container.y = 0;
  199. _startIndex = 0;
  200. if(dataProvider == null) return;
  201. var j : uint = 0;
  202. var n : uint = _startIndex + itemsPerPage;
  203. n = n > dataProvider.length ? dataProvider.length : n;
  204. for(var i:uint=_startIndex; i < n; i++)
  205. {
  206. var item : DisplayObject = _getRenderer(dataProvider[i]) as DisplayObject;
  207. IItemRenderer(item).itemIndex = i;
  208. if(direction == DIRECTION_HORIZONTAL)
  209. {
  210. item.y = 0;
  211. if(_itemSize > 0)
  212. item.x = j * (_itemSize + _gap);
  213. else
  214. item.x = _contentSize + item.width + _gap;
  215. _contentSize = item.x + (_itemSize > 0 ? _itemSize : item.width);
  216. }
  217. if(direction == DIRECTION_VERTICAL)
  218. {
  219. item.x = 0;
  220. if(_itemSize > 0)
  221. item.y = j * (_itemSize + _gap);
  222. else
  223. item.y = _contentSize + item.height + _gap;
  224. _contentSize = item.y + (_itemSize > 0 ? _itemSize : item.height);
  225. }
  226. _container.addChild(item);
  227. j++;
  228. }
  229. }
  230. /**
  231. * How many items are displayed per page.
  232. */
  233. public function get itemsPerPage() : uint
  234. {
  235. return _itemsPerPage;
  236. }
  237. override public function set width(value : Number) : void
  238. {
  239. _width = value;
  240. }
  241. override public function get width() : Number
  242. {
  243. return _width;
  244. }
  245. override public function set height(value : Number) : void
  246. {
  247. _height = value;
  248. }
  249. override public function get height() : Number
  250. {
  251. return _height;
  252. }
  253. /**
  254. * Index of the item currently displayed as a first item in the list.
  255. */
  256. public function get startIndex() : int
  257. {
  258. return _startIndex;
  259. }
  260. public function set startIndex(value : int) : void
  261. {
  262. _startIndex = value;
  263. if(direction == DIRECTION_HORIZONTAL)
  264. {
  265. if(_animated)
  266. {
  267. _tween.setValues({x: -_startIndex * (_itemSize+_gap)});
  268. }else
  269. {
  270. _tween.target.x = -_startIndex * (_itemSize+_gap);
  271. _tweenCompleteHandler(_tween);
  272. }
  273. }
  274. if(direction == DIRECTION_VERTICAL)
  275. {
  276. if(_animated)
  277. {
  278. _tween.setValues({y: -_startIndex * (_itemSize+_gap)});
  279. }else
  280. {
  281. _tween.target.y = -_startIndex * (_itemSize+_gap);
  282. _tweenCompleteHandler(_tween);
  283. }
  284. }
  285. }
  286. /**
  287. * If <code>true</code>, then we can scroll list left (or top if direction is set to vertical).
  288. */
  289. public function get canScrollLeft() : Boolean
  290. {
  291. return startIndex > 0;
  292. }
  293. /**
  294. * If <code>true</code>, then we can scroll list right (or bottom if direction is set to vertical).
  295. */
  296. public function get canScrollRight() : Boolean
  297. {
  298. return startIndex + itemsPerPage < dataProvider.length;
  299. }
  300. /**
  301. * Direction of the list. Possible values are <code>ScrollableList.DIRECTION_HORIZONTAL</code> and <code>ScrollableList.DIRECTION_VERTICAL</code>.
  302. *
  303. * @default horizontal
  304. */
  305. public function get direction() : String
  306. {
  307. return _direction;
  308. }
  309. public function set direction(value : String) : void
  310. {
  311. _direction = value;
  312. }
  313. public function get contentWidth() : Number
  314. {
  315. return _container.width;
  316. }
  317. public function get contentHeight() : Number
  318. {
  319. return _container.height;
  320. }
  321. /**
  322. * Size (width or height, deppending on direction) of one item in the list.
  323. */
  324. public function get itemSize() : Number
  325. {
  326. return _itemSize;
  327. }
  328. /**
  329. * Gap (distance) between items in the list.
  330. */
  331. public function get gap() : Number
  332. {
  333. return _gap;
  334. }
  335. }
  336. }