"Pull to Refresh" and "Infinite List" implementation for Feathers UI on Starling Framework. Now supports Starling 2.0 and Feathers 3.0
- Pull to Refresh for latest items
- Infinity Scroll for earlier items
- Visual indicators for loading, error and empty states
- Responsibility of loading items could be delegated to Data Provider
- Have no restriction for Header's and Footer's animation
PullToRefresh extends standard Feathers' List, it can be created as usual list:
this.pullToRefresh = new PullToRefresh();
this.pullToRefresh.loadImmediately = true; // indicates that loading of initial items will be started on list created
this.pullToRefresh.dataProvider = new ListCollection();
this.addChild(this.pullToRefresh);
Then define three methods for loading initial, earlier and latest items:
this.pullToRefresh.loadFunction = function(resultHandler:Function, errorHandler:Function):void
{
var result:Function = function(value:Object):*
{
resultHandler(value.data, value.hasMoreRecords);
}
Promise.delay({data : [{label : "M"}, {label : "N"}, {label : "O"}], hasMoreRecords : true}, 1000).then(result).otherwise(errorHandler);
};
this.pullToRefresh.proceedFunction = function(resultHandler:Function, errorHandler:Function):void
{
var result:Function = function(value:Object):*
{
resultHandler(value.data, value.hasMoreRecords);
}
Promise.delay({data : [{label : "X"}, {label : "Y"}, {label : "Z"}], hasMoreRecords : true}, 1000).then(result).otherwise(errorHandler);
};
this.pullToRefresh.refreshFunction = function(resultHandler:Function, errorHandler:Function):void
{
var result:Function = function(value:Object):*
{
resultHandler(value.data);
}
Promise.delay({data : [{label : "A"}, {label : "B"}, {label : "C"}]}, 1000).then(result).otherwise(errorHandler);
};
In this example we uses promise-as3 to simulate server response. Also take a look on hasMoreRecords
param, this is flag that indicates if there are more earlier data to load, and note that it is used only for load
and proceed
function, but not for refresh
.
The next visual components could be specified:
- Header that should implements Header interface,
- Footer that should implements Footer interface,
- ErrorIndicator (indicates error state) that could implements ErrorIndicator interface,
- EmptyIndicator (indicates empty state) that could implements EmptyIndicator interface and
- LoadingIndicator (indicates loading state) that have not special interface.
Note: The reason to implement correspond interfaces for error and empty indicators is they can receive error and empty strings. Each indicator is placed at the center of the PullToRefresh list.
Each of these components have default implementation, that could be overridden through factory method:
this.pullToRefresh.headerFactory = function():DisplayObject
{
return new CustomHeader();
};
this.pullToRefresh.footerFactory = function():DisplayObject
{
return new CustomFooter();
};
this.pullToRefresh.emptyIndicatorFactory = function():DisplayObject
{
return new DisplayObject();
};
this.pullToRefresh.errorIndicatorFactory = function():DisplayObject
{
return new DisplayObject();
};
this.pullToRefresh.loadingIndicatorFactory = function():DisplayObject
{
return new DisplayObject();
};
The default implementations of insert, append and prepend items could be overridden:
this.pullToRefresh.insertDataFunction = function(items:Array):void
{
this.pullToRefresh.data = items;
};
this.pullToRefresh.appendDataFunction = function(items:Array):void
{
this.pullToRefresh.addAllAt(new ListCollection(items), 0);
};
this.pullToRefresh.prependDataFunction = function(items:Array):void
{
this.pullToRefresh.addAll(new ListCollection(items));
};
The responsibility of work with server could be delegated to data provider. Just pass data provider that implements Provider:
public class TodoProvider extends ListCollection implements Provider
{
public function TodoProvider()
{
super();
}
public function get insertDataFunction():Function {return null;}
public function get appendDataFunction():Function {return null;}
public function get prependDataFunction():Function {return null;}
public function load(result:Function, error:Function):void
{
var result:Function = function(value:Object):*
{
resultHandler(value.data, value.hasMoreRecords);
}
Promise.delay({data : [{label : "M"}, {label : "N"}, {label : "O"}], hasMoreRecords : true}, 1000).then(result).otherwise(errorHandler);
}
public function refresh(result:Function, error:Function):void
{
var result:Function = function(value:Object):*
{
resultHandler(value.data);
}
Promise.delay({data : [{label : "A"}, {label : "B"}, {label : "C"}]}, 1000).then(result).otherwise(errorHandler);
}
public function proceed(result:Function, error:Function):void
{
var result:Function = function(value:Object):*
{
resultHandler(value.data, value.hasMoreRecords);
}
Promise.delay({data : [{label : "X"}, {label : "Y"}, {label : "Z"}], hasMoreRecords : true}, 1000).then(result).otherwise(errorHandler);
}
}
Support this project an others via Gratipay.