blob: 39977d73a8e9c03974281199cd4d0f566a4ae1ed [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package spark.components
{
import mx.collections.ICollectionView;
import mx.collections.ISort;
import mx.collections.ISortField;
import mx.core.ClassFactory;
import mx.core.ScrollPolicy;
import mx.core.mx_internal;
import spark.collections.Sort;
import spark.components.supportClasses.MobileGridColumn;
import spark.components.supportClasses.MobileGridHeader;
import spark.components.supportClasses.MobileGridRowRenderer;
import spark.events.MobileGridHeaderEvent;
import spark.layouts.MobileGridLayout;
import spark.layouts.VerticalLayout;
import spark.layouts.supportClasses.LayoutBase;
use namespace mx_internal;
/**
* Dispatched when the user releases the mouse button on a column header
* to request the control to sort the grid contents based on the contents of the column.
* Only dispatched if the column is sortable and the data provider supports
* sorting.
*
* @eventType spark.events.MobileGridHeaderEvent
*
*/
[Event(name="sortChange", type="spark.events.MobileGridHeaderEvent")]
/**
* The MobileGrid displays a collection of items in a grid of rows and columns, with column headers, optimized for mobile devices.
* The MobileGrid component provides the following features:
* <ul>
* <li> user can swipe through the rows in the datagrid. </li>
* <li> supports single selection of a row. </li>
* <li> rows can be sorted according to a given column by clicking on the column's header. </li>
* <li> cells can be displayed as text in different fonts and formats, as images, or using a custom renderer. </li>
* <li> default skin uses dark shades of gray, and is available in different screen densities.</li>
* </ul>
*
* <p> It's important to understand that MobileGrid does not have all the capabilities and flexibility of it's desktop equivalent,
* in order to ensure optimal display and scrolling performance on mobile devices. </p>
* <p>Typically, the following features are not available in MobileGrid:
* <ul>
* <li>the list of columns is static and cannot be changed at runtime</li>
* <li>multiple selection is not supported </li>
* <li>it's not possible to interactively reorder columns </li>
* <li>custom cell renderers must be designed with care, preferably in ActionScript, to ensure good display performance </li>
* </ul>
* </p>
* <p>Internally, MobileGrid inherits for Mobile spark.List component rather than any Grid or DataGrid, which means that all cell renderers
* in a single row are managed by one single MobileGridRowRenderer that delegates the individual cell renderers to light-weight sub-renderers. </p>
* <p> You usually don't access this internal row renderer yourself, and will rather define the individual cell renderers.</p>
* <p> This technique ensures optimal display and memory performance, which is critical for mobile devices,, at the price of less flexibility for cell renderers. </p>
*
* @see spark.components.supportClasses.MobileGridColumn
*
* @langversion 3.0
* @playerversion AIR 3.8
* @productversion Flex 4.11
*/
public class MobileGrid extends List
{
[SkinPart(required="true")]
public var headerGroup:MobileGridHeader;
private var _columns:Array;
private var _columnsChanged:Boolean = false;
private var _sortableColumns:Boolean = true;
private var lastSortIndex:int = -1;
private var sortIndex:int = -1;
private var sortColumn:MobileGridColumn;
public function MobileGrid()
{
layout = getDefaultLayout();
scrollSnappingMode = ScrollSnappingMode.LEADING_EDGE;
setStyle("horizontalScrollPolicy", ScrollPolicy.OFF);
useVirtualLayout = true;
columns = [];
}
/**
* An array of MobileGridColumn objects, one for each column that can be displayed.
* <p>Contrary to desktop DataGrid, this property must be set explicitly , or no columns will be displayed.</p>
* <p>If you want to change the set of columns,you need to re-assign the new array to the columns property.
* Changes to the original array without assigning the property will have no effect.</p>
*
* @see spark.components.supportClasses.MobileGridColumn
*
*/
[Inspectable(arrayType="spark.components.supportClasses.MobileGridColumn")]
public function set columns(value:Array):void
{
_columns = value;
_columnsChanged = true;
invalidateProperties();
// copy to vector and set indices
}
public function get columns():Array
{
return _columns.concat();
}
/**
* A flag that indicates whether the user can sort the rows
* by clicking on a column header cell.
* If <code>true</code>, the user can sort the data provider items by
* clicking on a column header cell.
* If <code>true</code>, individual columns can be made to not respond
* to a click on a header by setting the column's <code>sortable</code>
* property to <code>false</code>.
*
* <p>When a user releases the mouse button over a header cell, the DataGrid
* control dispatches a <code>headerRelease</code> event if both
* this property and the column's sortable property are <code>true</code>.
* If no handler calls the <code>preventDefault()</code> method on the event, the
* DataGrid sorts using that column's <code>MobileGridColumn.dataField</code> or
* <code>MobileGridColumn.labelFunction</code> properties.</p>
*
* @default true
*
* @see spark.components.supportClasses.MobileGridColumn#sortable
*/
public function get sortableColumns():Boolean
{
return _sortableColumns && (dataProvider is ICollectionView);
}
public function set sortableColumns(value:Boolean):void
{
_sortableColumns = value;
}
override protected function commitProperties():void
{
super.commitProperties();
if (_columnsChanged)
{
_columnsChanged = false;
for (var i:int = 0; i < _columns.length; i++)
{
MobileGridColumn(_columns[i]).colNum = i;
}
initDefaultItemRenderer(_columns);
if (headerGroup)
headerGroup.columns = _columns;
}
}
/* default layout for row item renderers */
protected function getDefaultLayout():LayoutBase
{
var l:VerticalLayout = new MobileGridLayout(this);
l.horizontalAlign = "contentJustify";
l.gap = 0;
return l;
}
protected function initDefaultItemRenderer(pcolumns:Array):void
{
var cf:ClassFactory;
cf = new ClassFactory(MobileGridRowRenderer);
cf.properties = {
columns: Vector.<MobileGridColumn>(pcolumns)
};
this.itemRenderer = cf;
}
override protected function partAdded(partName:String, instance:Object):void
{
if (instance === headerGroup)
{
headerGroup.dataGrid = this;
headerGroup.columns = _columns;
headerGroup.addEventListener(MobileGridHeaderEvent.SORT_CHANGE, headerGroup_sortChangeHandler);
}
super.partAdded(partName, instance);
}
override protected function partRemoved(partName:String, instance:Object):void
{
if (instance === headerGroup)
{
headerGroup.columns = null;
headerGroup.removeEventListener(MobileGridHeaderEvent.SORT_CHANGE, headerGroup_sortChangeHandler);
}
super.partRemoved(partName, instance);
}
private function headerGroup_sortChangeHandler(event:MobileGridHeaderEvent):void
{
var e:MobileGridHeaderEvent = MobileGridHeaderEvent(event.clone());
dispatchEvent(e)
if (!e.isDefaultPrevented())
{
sortByColumn(e.columnIndex);
}
}
/* roughly same behavior as mx:DataGrid */
private function sortByColumn(index:int):void
{
var collection:ICollectionView = dataProvider as ICollectionView;
var c:MobileGridColumn = _columns[index];
if (!c.sortable)
return;
var desc:Boolean = c.sortDescending;
// do the sort if we're allowed to
if (collection == null)
return;
var s:ISort = collection.sort;
var f:ISortField;
if (s)
{
s.compareFunction = null;
// analyze the current sort to see what we've been given
var sf:Array = s.fields;
if (sf)
{
for (var i:int = 0; i < sf.length; i++)
{
if (sf[i].name == c.dataField)
{
// we're part of the current sort
f = sf[i];
// flip the logic so desc is new desired order
desc = !f.descending;
break;
}
}
}
}
else
s = new Sort();
if (!f)
f = c.sortField;
c.sortDescending = desc;
// set the grid's sortIndex
lastSortIndex = sortIndex;
sortIndex = index;
sortColumn = c;
f.name = c.dataField;
f.descending = desc;
s.fields = [f];
collection.sort = s;
collection.refresh();
// update header
headerGroup.setSort(sortIndex, desc);
}
override public function validateSize(recursive:Boolean = false):void
{
super.validateSize(recursive);
}
}
}