Implement ListModel and TreeModel"
Jumperchen (talk | contribs) |
|||
Line 3: | Line 3: | ||
The default implementation of models, such as <javadoc>org.zkoss.zul.ListModelList</javadoc> and <javadoc>org.zkoss.zul.DefaultTreeModel</javadoc> assumes all data are available in the memory. It is not practical if a model has a lot of data. For huge amount of data, it is suggested to implement your own model by loading and caching only one portion of data at a time. | The default implementation of models, such as <javadoc>org.zkoss.zul.ListModelList</javadoc> and <javadoc>org.zkoss.zul.DefaultTreeModel</javadoc> assumes all data are available in the memory. It is not practical if a model has a lot of data. For huge amount of data, it is suggested to implement your own model by loading and caching only one portion of data at a time. | ||
− | To implement your own model, you could extend from <javadoc>org.zkoss.zul.AbstractListModel</javadoc>, <javadoc>org.zkoss.zul.AbstractGroupsModel</javadoc> and <javadoc>org.zkoss.zul.DefaultTreeModel</javadoc> as described in [[ZK Developer's Reference/MVC/Model|the Model section]]. To implement a model that supports sorting, you have to implement <javadoc type="interface">org.zkoss.zul. | + | To implement your own model, you could extend from <javadoc>org.zkoss.zul.AbstractListModel</javadoc>, <javadoc>org.zkoss.zul.AbstractGroupsModel</javadoc> and <javadoc>org.zkoss.zul.DefaultTreeModel</javadoc> as described in [[ZK Developer's Reference/MVC/Model|the Model section]]. To implement a model that supports sorting, you have to implement <javadoc type="interface">org.zkoss.zul.ext.Sortable</javadoc> too. Each time an user requires sorting, <javadoc type="interface" method="sort(java.util.Comparator, boolean)">org.zkoss.zul.ext.Sortable</javadoc> will be called and the implementation usually clears the cache and re-generate the SQL statement accordingly. |
Here is some pseudo code: | Here is some pseudo code: | ||
<source lang="java"> | <source lang="java"> | ||
− | public class FooListModel extends AbstractListModel implements | + | public class FooListModel extends AbstractListModel implements Sortable { |
private int _size = -1; | private int _size = -1; | ||
private Object[] _cache; | private Object[] _cache; | ||
Line 14: | Line 14: | ||
private String _orderBy; | private String _orderBy; | ||
private boolean _ascending, _descending; | private boolean _ascending, _descending; | ||
− | + | private Comparator _sorting; | |
+ | |||
public int getSize() { | public int getSize() { | ||
if (_size < 0) | if (_size < 0) | ||
− | _size = /*SELECT COUNT(*) FROM ... | + | _size = /**SELECT COUNT(*) FROM ...*/ |
return _size; | return _size; | ||
} | } | ||
public Object getElementAt(int index) { | public Object getElementAt(int index) { | ||
if (_cache == null || index < _beginOffset || index >= _beginOffset + _cache.length) { | if (_cache == null || index < _beginOffset || index >= _beginOffset + _cache.length) { | ||
− | + | loadToCache(index, 100); //SELECT ... FROM .... OFFSET index LIMIT 100 | |
//if _ascending, ORDER BY _orderBy ASC | //if _ascending, ORDER BY _orderBy ASC | ||
//if _descending, ORDER BY _orderBy DSC | //if _descending, ORDER BY _orderBy DSC | ||
Line 28: | Line 29: | ||
return _cache[index - _beginOffset]; | return _cache[index - _beginOffset]; | ||
} | } | ||
+ | @Override | ||
public void sort(Comparator cmpr, boolean ascending) { | public void sort(Comparator cmpr, boolean ascending) { | ||
_cache = null; //purge cache | _cache = null; //purge cache | ||
Line 33: | Line 35: | ||
_descending = !(_ascending = ascending); | _descending = !(_ascending = ascending); | ||
_orderBy = ((FieldComparator)cmpr).getRawOrderBy(); | _orderBy = ((FieldComparator)cmpr).getRawOrderBy(); | ||
+ | _sorting = cmpr; | ||
//Here we assume sort="auto(fieldName)" is specified in ZUML, so cmpr is FieldComparator | //Here we assume sort="auto(fieldName)" is specified in ZUML, so cmpr is FieldComparator | ||
//On other hand, if you specifies your own comparator, such as sortAscending="${mycmpr}", | //On other hand, if you specifies your own comparator, such as sortAscending="${mycmpr}", | ||
Line 38: | Line 41: | ||
fireEvent(ListDataEvent.CONTENTS_CHANGED, -1, -1); | fireEvent(ListDataEvent.CONTENTS_CHANGED, -1, -1); | ||
} | } | ||
+ | |||
+ | public String getSortDirection(Comparator cmpr) { | ||
+ | if (Objects.equals(_sorting, cmpr)) | ||
+ | return _ascending ? | ||
+ | "ascending" : "descending"; | ||
+ | return "natural"; | ||
+ | } | ||
} | } | ||
</source> | </source> | ||
− | The implementation of <javadoc type="interface" method="sort(java.util.Comparator, boolean)">org.zkoss.zul. | + | The implementation of <javadoc type="interface" method="sort(java.util.Comparator, boolean)">org.zkoss.zul.ext.Sortable</javadoc> generally has to purge the cache, store the sorting direction and field, and then fire <javadoc method="CONTENTS_CHANGED">org.zkoss.zul.event.ListDataEvent</javadoc> to reload the content. |
The field to sort against has to be retrieved from the given comparator. If you specify <code>"auto(fieldName)"</code> to <javadoc method="setSort(java.lang.String)">org.zkoss.zul.Listheader</javadoc>, then the comparator is an instance of <javadoc>org.zkoss.zul.FieldComparator</javadoc>, and you could retrieve the field's name from <javadoc method="getRawOrderBy()">org.zkoss.zul.FieldComparator</javadoc>. | The field to sort against has to be retrieved from the given comparator. If you specify <code>"auto(fieldName)"</code> to <javadoc method="setSort(java.lang.String)">org.zkoss.zul.Listheader</javadoc>, then the comparator is an instance of <javadoc>org.zkoss.zul.FieldComparator</javadoc>, and you could retrieve the field's name from <javadoc method="getRawOrderBy()">org.zkoss.zul.FieldComparator</javadoc>. | ||
− | If you'd like to use your own comparator, you have to carry the information in it and then retrieve it back when <javadoc type="interface" method="sort(java.util.Comparator, boolean)">org.zkoss.zul. | + | If you'd like to use your own comparator, you have to carry the information in it and then retrieve it back when <javadoc type="interface" method="sort(java.util.Comparator, boolean)">org.zkoss.zul.ext.Sortable</javadoc> is called. |
Also notice that we cache the size to improve the performance, since <javadoc method="getSize()" type="interface">org.zkoss.zul.ListModel</javadoc> might be called multiple times. | Also notice that we cache the size to improve the performance, since <javadoc method="getSize()" type="interface">org.zkoss.zul.ListModel</javadoc> might be called multiple times. |
Revision as of 08:15, 10 February 2012
The default implementation of models, such as ListModelList and DefaultTreeModel assumes all data are available in the memory. It is not practical if a model has a lot of data. For huge amount of data, it is suggested to implement your own model by loading and caching only one portion of data at a time.
To implement your own model, you could extend from AbstractListModel, AbstractGroupsModel and DefaultTreeModel as described in the Model section. To implement a model that supports sorting, you have to implement Sortable too. Each time an user requires sorting, Sortable.sort(Comparator, boolean) will be called and the implementation usually clears the cache and re-generate the SQL statement accordingly.
Here is some pseudo code:
public class FooListModel extends AbstractListModel implements Sortable {
private int _size = -1;
private Object[] _cache;
private int _beginOffset;
private String _orderBy;
private boolean _ascending, _descending;
private Comparator _sorting;
public int getSize() {
if (_size < 0)
_size = /**SELECT COUNT(*) FROM ...*/
return _size;
}
public Object getElementAt(int index) {
if (_cache == null || index < _beginOffset || index >= _beginOffset + _cache.length) {
loadToCache(index, 100); //SELECT ... FROM .... OFFSET index LIMIT 100
//if _ascending, ORDER BY _orderBy ASC
//if _descending, ORDER BY _orderBy DSC
}
return _cache[index - _beginOffset];
}
@Override
public void sort(Comparator cmpr, boolean ascending) {
_cache = null; //purge cache
_size = -1; //so size will be reloaded
_descending = !(_ascending = ascending);
_orderBy = ((FieldComparator)cmpr).getRawOrderBy();
_sorting = cmpr;
//Here we assume sort="auto(fieldName)" is specified in ZUML, so cmpr is FieldComparator
//On other hand, if you specifies your own comparator, such as sortAscending="${mycmpr}",
//then, cmpr will be the comparator you assigned
fireEvent(ListDataEvent.CONTENTS_CHANGED, -1, -1);
}
public String getSortDirection(Comparator cmpr) {
if (Objects.equals(_sorting, cmpr))
return _ascending ?
"ascending" : "descending";
return "natural";
}
}
The implementation of Sortable.sort(Comparator, boolean) generally has to purge the cache, store the sorting direction and field, and then fire ListDataEvent.CONTENTS_CHANGED to reload the content.
The field to sort against has to be retrieved from the given comparator. If you specify "auto(fieldName)"
to Listheader.setSort(String), then the comparator is an instance of FieldComparator, and you could retrieve the field's name from FieldComparator.getRawOrderBy().
If you'd like to use your own comparator, you have to carry the information in it and then retrieve it back when Sortable.sort(Comparator, boolean) is called.
Also notice that we cache the size to improve the performance, since ListModel.getSize() might be called multiple times.
For a real example, please refer to Small Talk: Handling sortable huge data using ZK and/or Small Talk: Handling huge data using ZK.
Version History
Version | Date | Content |
---|---|---|