我的任务是实施排序和安排。过滤GWT CellTable中显示的数据。 值得庆幸的是,GWT已经支持排序,但看起来我必须将我自己的过滤支持黑客攻击。
更准确地说,我想要支持的内容类似于Excel提供的过滤功能,您可以单击列标题中的下拉菜单(例如)单击复选框以允许您根据筛选列的值筛选行。一张图片胜过千言万语:
我的问题:有关如何在GWT 2.2中实现此功能的任何建议?它甚至可能吗?
我正在考虑的一个选项是将自定义Header对象传递给CellTable.addColumn()。如果可能的话,我会将一个ClickHandler添加到Header,然后打开一个Popup,显示一个用于过滤的小部件。不确定如何实现这一点而不会对排序行为产生负面影响。
欢迎任何建议。
修改:
感谢下面的John,我有以下FilterableHeader
课程,它允许我至少在标题中添加一个图标。目前还不确定如何在该图标上获取ClickHandler,因为图像是通过HTML插入的,而不是使用GWT小部件。
public class FilterableHeader extends Header<String>
{
/**
* Image resources.
*/
public static interface Resources extends ClientBundle
{
ImageResource downArrow();
ImageResource upArrow();
}
private static final Resources RESOURCES = GWT.create(Resources.class);
private static final int IMAGE_WIDTH = 16;
private static final String DOWN_ARROW = makeImage(RESOURCES.downArrow());
private static final String UP_ARROW = makeImage(RESOURCES.upArrow());
private static String makeImage(ImageResource resource)
{
AbstractImagePrototype proto = AbstractImagePrototype.create(resource);
return proto.getHTML().replace("style='", "style='position:absolute;right:0px;top:0px;");
}
private String text;
public FilterableHeader(String text)
{
super(new ClickableTextCell());
this.text = text;
}
@Override
public String getValue()
{
return text;
}
@Override
public void render(Cell.Context context, SafeHtmlBuilder safe)
{
int imageWidth = IMAGE_WIDTH;
StringBuilder sb = new StringBuilder();
sb.append("<div style='position:relative;cursor:hand;cursor:pointer;");
sb.append("padding-right:");
sb.append(imageWidth);
sb.append("px;'>");
sb.append(UP_ARROW);
sb.append("<div>");
sb.append(text);
sb.append("</div></div>");
safe.append(SafeHtmlUtils.fromSafeConstant(sb.toString()));
}
}
答案 0 :(得分:2)
自定义标头是GWT 2.1用于排序的标头。 2.1 bikeshed包含使用自定义标头的示例,并使用一个进行排序,直到Mvp4g移动到2.2。要启用过滤,只需添加一个带有自己的点击处理程序的图像,你应该做得很好 - 点击它时不会触发排序行为,只有标题的其余部分会出现。
table.addColumn(new MyColumn(new MyCell()), new MyFilterHeader());
对于实际过滤,如果您正在使用数据库模型from the examples(ListDataProvider的包装类),那么我认为您只需要保留两个列表 - 已分配给它的已过滤列表ListDataProvider,以及它所基于的未过滤列表。
希望有所帮助!
在您的新示例代码中,您可能想要尝试使用其中包含ClickableTextCell的CompositeCell以及用于过滤部分的ActionCell - 如果您可以将图像粘贴到ClickableTextCell中,那么应该能够在ActionCell中加上你想要的鼠标行为。
答案 1 :(得分:1)
我使用鼠标单击位置将自定义点击事件添加到列标题。换句话说,您可以对其进行设置,以便在用户点击图像所在的“常规区域”时,您可以显示过滤屏幕。
这是一个例子,我忽略了我添加的文本字段的点击事件:
if(col.isFilterable()){
if (event.getClientY() > (getInputElement(parent).getAbsoluteTop() - 2) && event.getClientY() < (getInputElement(parent).getAbsoluteBottom() + 2)) {
//ignore on click in area of the text field
event.preventDefault();
} else {
//如果用户点击其他任何地方,则排序 trySort(父); }
并且,因为单元格分别侦听'keyup'事件,当用户点击enter(当单元格被聚焦时)执行过滤器。
if(event.getKeyCode()==13){
event.preventDefault();
handleSetFilterValue(parent);
tryFilter();
}
答案 2 :(得分:1)
我开发业务应用程序,其中典型的数据库查询可能返回数百或数千行。用户发现类似excel的过滤器和列排序非常有用。
因此,我实现了一个扩展ListDataProvider的类,以便与支持客户端类似excel的列过滤和排序的CellTable一起使用。在所有其他方面,它的行为很像ListDataProvider。
它取决于实现以下ColumnAccessor接口,为CellTable中的每个列提供符号名称,提供对列级数据的排序和过滤访问,用于排序的列的比较器以及显示标签标题。以下是ColumnAccessor类。它假设您有某种数据传输对象&lt; T&gt;对行进行建模。
/**
* Interface to provide access to a specific
* column within a data row.
* @param <T> Object that contains the column
* values in a cell table row. Typically a Data Transfer Object.
*/
public interface ColumnAccessor<T> {
/**
* Filter display value for blank/null column values
*/
public final String FILTER_SELECTOR_BLANK = "{Blank}";
/**
* Returns A row-unique symbolic name for the column. This name is
* used as a Map key to access the ColumnAccessor instance by
* name for filtering and sorting.
* @return
*/
public String getColumnName();
/**
* Returns text label to appear as column header in CellTable.
* @return
*/
public String getLabel();
/**
* Returns value of the column as a String
* @param t Object that models the column values in a
* cell table row (Typically a Data Transfer Object)
* @return
*/
public String getValue(T t);
/**
* Returns Comparator for sorting data rows and for sorting
* discrete values that appear in a filter's select/option list.
* While the getValue() method always returns a String,
* these comparators should sort the column's values in
* consideration for the data type (for example, dates sorted
* as dates, numbers sorted as numbers, strings sorted as strings).
* @return
*/
public Comparator comparator();
}
以下是FilterSortDataProvider类:
import com.google.gwt.cell.client.SelectionCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.view.client.ListDataProvider;
import java.util.*;
/**
* Class that extends a ListDataProvider but adds "Excel-Like" column filters and also
* includes click on column heading sorts.
* @param <T> Object that contains the column values in a cell table row. Typically a Data Transfer Object.
*/
public class FilterSortDataProvider<T> extends ListDataProvider {
private List<T> rows;
private List<T> filteredSortedRows;
public Map<String, DataColumn> dataColumnMap = new HashMap<String, DataColumn>();
private String lastSortColumn = "*";
private int lastSortDirection = 0;
/**
* Constructs the DataProvider and columns
* @param rows Collection of objects that contain column data for cell table rows, typically
* Data Transfer Objects.
* @param columnAccessors List of ColumnAccessor instances for each column that will appear in
* the cell table. Each accessor will render a sortable, filterable column header
* and provides access to column-level data.
*/
public FilterSortDataProvider(Collection<T> rows, List<ColumnAccessor> columnAccessors) {
this.rows = new ArrayList<T>(rows);
this.filteredSortedRows = new ArrayList<T>();
Iterator<ColumnAccessor> columnAccessorIterator = columnAccessors.iterator();
while (columnAccessorIterator.hasNext()) new DataColumn(columnAccessorIterator.next());
// Initialize filters
filter();
}
/**
* Returns defensive copy of the current collection of filtered/sorted data rows
* @return
*/
public List<T> getFilteredSortedRows() {
return new ArrayList(filteredSortedRows);
}
/**
* Returns a CellTable Header for the named column for use when setting up the CellTable (ie:
* used as Header value in cellTable.addColumn(TextColumn, Header) call. The header includes
* the columnAccessor.getLabel() value as a click-to-sort header label, and a drop-down filter
* where the options include all available values.
* @param columnName Same value as returned by this columns ColumnAccessor.getColumnName()
* @return
*/
public Header getColumnHeader(final String columnName) {
DataColumn column = dataColumnMap.get(columnName);
return (column != null ? new FilteredCellTableHeader(column) : null);
}
/**
* Called when user clicks on column header label. Repeated clicks on the same column header will
* reverse the sort direction. Can also be called prior to display of CellTable to establish an initial
* sort order.
* @param sortColumnName
*/
public void sort(String sortColumnName) {
if (!sortColumnName.equals("*")) {
DataColumn column = dataColumnMap.get(sortColumnName);
if (column != null) {
// Sort ascending
Collections.sort(this.filteredSortedRows, column);
// Re-Sort of same column
if (sortColumnName.equals(lastSortColumn)) {
lastSortDirection *= -1;
}
else {
lastSortDirection = 1;
lastSortColumn = sortColumnName;
}
if (lastSortDirection == -1) Collections.reverse(filteredSortedRows);
}
}
this.setList(filteredSortedRows);
}
/**
* Optional call to pre-set filter before initial display of CellTable
* @param columnName
* @param value
*/
public void filter(String columnName, String value) {
DataColumn column = dataColumnMap.get(columnName);
if (column != null) column.filter(value);
}
/**
* Filters the rows based on all of the filters, and re-builds the filter drop-down
* options.
*/
private void filter() {
// Build collection of rows that pass all filters
filteredSortedRows = new ArrayList<T>();
Iterator<T> rowIterator = this.rows.iterator();
while (rowIterator.hasNext()) {
T row = rowIterator.next();
if (rowPassesFilter(row, null)) filteredSortedRows.add(row);
}
// Build filter select/option list for each column based on rows
// that pass all filters EXCEPT for the column in question.
Iterator<DataColumn> columnIterator = dataColumnMap.values().iterator();
while (columnIterator.hasNext()) {
DataColumn column = columnIterator.next();
Set<String> optionsSet = new HashSet<String>();
rowIterator = this.rows.iterator();
while (rowIterator.hasNext()) {
T row = rowIterator.next();
if (rowPassesFilter(row, column)) {
optionsSet.add(column.filterOptionValue(row));
}
}
// Sort the options using the ColumnAccessor's comparator
List<String> optionsList = new ArrayList<String>(optionsSet);
Collections.sort(optionsList, column.comparator());
// Make blank option (if any) the last entry in the option list
if (optionsList.contains(ColumnAccessor.FILTER_SELECTOR_BLANK)) {
optionsList.remove(ColumnAccessor.FILTER_SELECTOR_BLANK);
optionsList.add(ColumnAccessor.FILTER_SELECTOR_BLANK);
}
// Add the wild-card "All" as the first entry in the option list
optionsList.add(0, "*");
// Set the new list of options in the column
column.filterOptions = optionsList;
}
// Re-sort the data with consideration for the current sort column and direction
lastSortDirection *= -1;
sort(lastSortColumn);
}
/**
* Returns true if the specified row passes all column filters.
* @param row Data row to test
* @param columnToIgnore When specified, this column is assumed to allow the row
* to pass the filter. This is used when building the list
* of filter select/option values.
* @return
*/
private boolean rowPassesFilter(T row, DataColumn columnToIgnore) {
Iterator<DataColumn> columnIterator = dataColumnMap.values().iterator();
boolean passes = true;
while (columnIterator.hasNext() && passes) {
DataColumn column = columnIterator.next();
if (column != columnToIgnore) {
passes = column.rowPassesFilter(row);
}
}
return passes;
}
/**
* Inner class that models a CellTable column, its ColumnAccessor, current filter value,
* and current filter option values.
*/
public class DataColumn implements Comparator<T> {
private String filterValue = "*";
private List<String> filterOptions = new ArrayList<String>();
private ColumnAccessor columnAccessor;
/**
* Constructs a filterable, sortable column
* @param columnAccessor
*/
public DataColumn(final ColumnAccessor columnAccessor) {
this.columnAccessor = columnAccessor;
FilterSortDataProvider.this.dataColumnMap.put(columnAccessor.getColumnName(), this);
}
/**
* Returns symbolic name of column
* @return
*/
public String getName() {
return this.columnAccessor.getColumnName();
}
/**
* Returns display label for column header
* @return
*/
public String getLabel() {
return columnAccessor.getLabel();
}
/**
* Returns value of column
* @param row
* @return
*/
public String getValue(T row) {
return columnAccessor.getValue(row);
}
/**
* Returns comparator define in ColumnAccessor for use when sorting
* data rows and for sorting filter options.
* @return
*/
public Comparator comparator() {
return columnAccessor.comparator();
}
/**
* Called when user changes the value of a column filter
* @param filterValue
*/
public void filter(String filterValue) {
if (this.filterOptions.contains(filterValue)) {
this.filterValue = filterValue;
FilterSortDataProvider.this.filter();
}
}
/**
* Called when user clicks on column label to sort rows
*/
public void sort() {
FilterSortDataProvider.this.sort(this.columnAccessor.getColumnName());
}
/**
* Used to sort data rows. Uses comparator specified in ColumnAccessor.
* @param row1
* @param row2
* @return
*/
public int compare(T row1, T row2) {
return comparator().compare(getValue(row1), getValue(row2));
}
/**
* Returns true if specified row passes this column's filter
* @param row
* @return
*/
public boolean rowPassesFilter(T row) {
return filterValue.equals("*") || filterValue.equals(filterOptionValue(row));
}
/**
* Returns value to appear in filter options list. Null or "blank" values appear in options
* list as {Blank}.
* @param row
* @return
*/
private String filterOptionValue(T row) {
String value = getValue(row);
return (value == null || value.trim().length() == 0 ? ColumnAccessor.FILTER_SELECTOR_BLANK : value);
}
/**
* Renders Html Select/Options tag for column filter
* @return
*/
public String toHtmlSelect() {
StringBuffer sb = new StringBuffer();
sb.append("<select size='1' style='width: 100%;'>");
Iterator<String> opts = filterOptions.iterator();
while (opts.hasNext()) {
String escapedOption = SafeHtmlUtils.htmlEscape(opts.next());
sb.append("\t<option value='" + escapedOption);
sb.append((escapedOption.equals(filterValue) ? "' SELECTED>" : "'>"));
sb.append(escapedOption + "</option>\n");
}
sb.append("</select>\n");
return sb.toString();
}
}
/**
* Inner class Header wrapper for FilteredSortedCellTableHeaderCell
*/
public class FilteredCellTableHeader extends Header {
public FilteredCellTableHeader(DataColumn column) {
super(new FilteredSortedCellTableHeaderCell(column));
}
public Object getValue() {
return null;
}
}
/**
* CellTable SelectionCell that includes filter and sort controls, renders controls, and
* handles onBrowserEvent()
*/
private class FilteredSortedCellTableHeaderCell extends SelectionCell {
private DataColumn column;
public FilteredSortedCellTableHeaderCell(final DataColumn column) {
super(new ArrayList<String>());
this.column = column;
}
/**
* Renders Html Submit button as sort control, and Html Select/Option tag for filter.
* @param context
* @param value
* @param sb
*/
@Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
String sortButton = "<input type='submit' value='" + SafeHtmlUtils.htmlEscape(column.getLabel()) +
"' style='text-align: center; width: 100%; background: none; border: none; font-weight: bold;'>";
sb.appendHtmlConstant(sortButton);
sb.appendHtmlConstant("<br>");
sb.appendHtmlConstant(column.toHtmlSelect());
}
/**
* Detects filter and sort user interaction events
* @param context
* @param parent
* @param value
* @param event
* @param valueUpdater
*/
@Override
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
String type = event.getType();
Element element = event.getEventTarget().cast();
String tagName = element.getTagName();
// Filter selection changed
if ("change".equals(type) && tagName.equals("SELECT")) {
// Set filter value and call filter routine
SelectElement se = (SelectElement)element;
String filterValue = se.getOptions().getItem(se.getSelectedIndex()).getValue();
column.filter(filterValue);
}
// Click on sort button
else if (type.equals("focus") && tagName.equals("INPUT")) {
column.sort();
}
}
}
}
我希望这对某人有帮助。