【问题标题】:ComboBox with multiselect Feature in GXTGXT中具有多选功能的组合框
【发布时间】:2013-11-26 00:28:22
【问题描述】:

我正在尝试实现一个具有多选功能的组合框。每个字符串都会有一个复选框,用户可以从列表中选择一个或多个。 在 GXT 2 中,我们有 CheckBoxListView 让事情变得更容易。

我有以下实现它的想法。

使用一个网格,它可以将一列作为 CheckBox,另一列作为我要显示的字符串,然后将此网格的存储添加到 CheckBoxStore。但是,由于 Grid 和 ComboBoxes 的 ListStore 不一样,我尝试了但没有成功,因为两个 store 不同,接受的属性也不同。

应该有另一种方式,例如使用 ListView。但是,我不知道如何在 ListView 中使用 CheckBox

需要帮助

【问题讨论】:

标签: java checkbox combobox gxt


【解决方案1】:

这是我的代码.. 不完美,但某种临时解决方法..

import java.util.ArrayList;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArrayString;
import client.grid.model.IComboBoxProperties;
import client.model.ComboBoxModel;
import com.sencha.gxt.cell.core.client.form.CheckBoxCell;
import com.sencha.gxt.cell.core.client.form.ComboBoxCell.TriggerAction;
import com.sencha.gxt.core.client.Style.HideMode;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.widget.core.client.Window;
import com.sencha.gxt.widget.core.client.event.ExpandEvent;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler;
import com.sencha.gxt.widget.core.client.form.ComboBox;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;


public class MultiSelectBox extends ComboBox<ComboBoxModel>{

 private Window checkBoxListHolder;
 private Grid<ComboBoxModel> checkBoxGrid;
 private ListStore<ComboBoxModel> listStore;
 private boolean readOnly;
 private ColumnConfig<ComboBoxModel, String> cc1;
 private ColumnConfig<ComboBoxModel, Boolean> cc2;
 private ComboBoxModel setData;
 IComboBoxProperties props = GWT.create(IComboBoxProperties.class); 

public MultiSelectBox(ListStore<ComboBoxModel> store,
        LabelProvider<? super ComboBoxModel> labelProvider) {
    super(store, labelProvider);
    this.listStore=getStore();
    this.checkBoxGrid=getCheckBoxGrid();

    checkBoxListHolder = new Window(){

    };
    checkBoxListHolder.setClosable(false);
    checkBoxListHolder.setHeaderVisible(false);

    checkBoxListHolder.setResizable(false);
    checkBoxListHolder.setAutoHide(true);
    checkBoxListHolder.getButtonBar().setVisible(false);
    checkBoxGrid.setVisible(true);

    checkBoxGrid.focus();
    checkBoxListHolder.add(checkBoxGrid);
    checkBoxListHolder.addHideHandler(new HideHandler() {

        @Override
        public void onHide(HideEvent event) {
             checkBoxGrid.getView().refresh(false);
             checkBoxGrid.getStore().commitChanges();
             clear();
             setValue(parseCheckedValues(checkBoxGrid));
             getCell().setTriggerAction(TriggerAction.ALL);

        }
    }); 

    addExpandHandler(new ExpandEvent.ExpandHandler() {

        @Override
        public void onExpand(ExpandEvent event) {
             if (checkBoxListHolder.isVisible()) {
                 checkBoxGrid.getView().refresh(false);

                    checkBoxGrid.getStore().commitChanges();
                    checkBoxListHolder.hide();
                } else {
                    if(getValue()!=null)
                    {
                        setData=getValue();
                        updateGrid(setData);

                    }
                    else
                    {
                        updateGridStore(getStore());
                    }
                    checkBoxGrid.getStore().commitChanges();
                    checkBoxGrid.getView().refresh(false);
                    checkBoxListHolder.setPosition(getAbsoluteLeft(), getAbsoluteTop()+getOffsetHeight());
                     checkBoxListHolder.setSize(String.valueOf(getOffsetWidth()), "200");
                    checkBoxListHolder.show();

                }

             collapse();

        }
    });


}

   private  Grid<ComboBoxModel> getCheckBoxGrid()
   {

       ListStore<ComboBoxModel> gridStore = new ListStore<ComboBoxModel>(props.key());


      for(int i=0;i<listStore.size();i++)
       {
           gridStore.add(new ComboBoxModel(listStore.get(i).getKey(),listStore.get(i).getValue(),false));
       }
      gridStore.commitChanges();
       List<ColumnConfig<ComboBoxModel,?>> configs =  getColumnConfig();
       ColumnModel<ComboBoxModel> cm = new ColumnModel<ComboBoxModel>(configs); 

        final Grid<ComboBoxModel> grid = new Grid<ComboBoxModel>(gridStore, cm){

        };
        grid.setBorders(false);
        grid.getView().setStripeRows(false);
        grid.getView().setAutoFill(true);
        grid.getView().setColumnLines(false);
        grid.setHideHeaders(true);

       return grid;
   }
   private List<ColumnConfig<ComboBoxModel,?>> getColumnConfig(){
       List<ColumnConfig<ComboBoxModel,?>> columns = new ArrayList<ColumnConfig<ComboBoxModel,?>>();
          cc2 =new ColumnConfig<ComboBoxModel,Boolean>(props.checked(),20,"Select");
          cc2.setCell(new CheckBoxCell(){

          });
          columns.add(cc2);

       cc1 = new ColumnConfig<ComboBoxModel,String>(props.getValue(),50,"Choose Properties");
       columns.add(cc1);
       return columns;
   }


private ComboBoxModel parseCheckedValues(Grid<ComboBoxModel> grid) {

    ListStore<ComboBoxModel> list = grid.getStore();
    ComboBoxModel m = new ComboBoxModel();
    String buf="";
    String keys="";
    if (list != null) {
        for(int i=0;i<list.size();i++){
            if(list.get(i).getChecked().booleanValue())
            {
                buf=buf+list.get(i).getValue();
                buf=buf+",";
                keys=keys+list.get(i).getKey();
                keys=keys+",";
            }
        }
        if (buf.length() > 0 && buf.charAt(buf.length()-1)==',') {
            buf = buf.substring(0, buf.length()-1);
            }
        if (keys.length() > 0 && keys.charAt(keys.length()-1)==',') {
            keys = keys.substring(0, keys.length()-1);
            }

    }
    m.setKey(keys);
    m.setValue(buf);
    return m;
}

public JsArrayString getSelectedItems() {
    JsArrayString result = (JsArrayString) JsArrayString.createArray();
    ListStore<ComboBoxModel> store=checkBoxGrid.getStore();
    if (store != null){
        for(int i=0;i<store.size();i++){
            if(store.get(i).getChecked().booleanValue())
            {
                result.push(store.get(i).getKey());
            }
        }

    }
    return result;
}

 public List<ComboBoxModel> getSelectedItemCombos() {
    List<ComboBoxModel> list = new ArrayList<ComboBoxModel>();
      ListStore<ComboBoxModel> store=checkBoxGrid.getStore();
    if(store!=null){
        for(int i=0;i<store.size();i++){
            if(store.get(i).getChecked().booleanValue())
            {
                list.add(store.get(i));
            }
        }

    }
    return list;        
   } 
public void setCheckedItems(List<ComboBoxModel> list){

    ListStore<ComboBoxModel> liststore = checkBoxGrid.getStore();
    for(int i=0;i<liststore.size();i++)
    {
        for(int j=0;j<list.size();j++)
        {
            if(checkBoxGrid.getStore().get(i).getKey().equals(list.get(j).getKey()) && checkBoxGrid.getStore().get(i).getValue().equals(list.get(j).getValue()))
            {
                checkBoxGrid.getStore().get(i).setChecked(true);
                break;
            }
            else
                checkBoxGrid.getStore().get(i).setChecked(false);
        }
    }
       checkBoxGrid.getStore().commitChanges();
       setValue(parseCheckedValues(checkBoxGrid));
}




public void clearCheckedItems(){

    for (int i =0; i< checkBoxGrid.getStore().size(); i++){
         checkBoxGrid.getStore().get(i).setChecked(false);
    }

}
private ArrayList<ComboBoxModel> getSelectedValues(ComboBoxModel model)
{
    ArrayList<ComboBoxModel> list = new ArrayList<ComboBoxModel>();
    String [] values=model.getValue().split(",");
    String [] keys = model.getKey().split(",");
    int i=0;
    int len=values.length;
    for(i=0;i<len;i++)
    {
        list.add(new ComboBoxModel(keys[i],values[i],true));
    }


    return list;
}
private void updateGrid(ComboBoxModel model)
{
    String [] values=model.getValue().split(",");
    String [] keys = model.getKey().split(",");
    int i=0;
    int len=values.length;
    ListStore<ComboBoxModel> list = checkBoxGrid.getStore();

for(i=0;i<list.size();i++)
{

    for(int j=0;j<len;j++)
    {
        if(checkBoxGrid.getStore().get(i).getKey().equals(keys[j]) && checkBoxGrid.getStore().get(i).getValue().equals(values[j]))
        {
            checkBoxGrid.getStore().get(i).setChecked(true);
            break;
        }
        else
            checkBoxGrid.getStore().get(i).setChecked(false);
    }

}
checkBoxGrid.getStore().commitChanges();

}

public boolean isReadOnly() {
    return readOnly;
}

public void setReadOnly(boolean readOnly) {
    this.readOnly = readOnly;
}

public ListStore<ComboBoxModel> getListStore() {
    return listStore;
}

public void setListStore(ListStore<ComboBoxModel> listStore) {
    this.listStore = listStore;
}
private void updateGridStore(ListStore<ComboBoxModel> store)
{
     checkBoxGrid.getStore().clear();
    for(int i=0;i<store.size();i++)
       {
           checkBoxGrid.getStore().add(newComboBoxModel(listStore.get(i).getKey(),listStore.get(i).getValue(),false));
       }
    checkBoxGrid.getStore().commitChanges();
}
}

【讨论】:

    【解决方案2】:

    我已经为 GXT 3.X 版本实现了具有几乎所有功能的 MultiSelectComboBox。

    这是我的代码:

    import com.google.gwt.cell.client.AbstractCell;
    import com.google.gwt.cell.client.Cell;
    import com.google.gwt.cell.client.ValueUpdater;
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.dom.client.Element;
    import com.google.gwt.dom.client.NativeEvent;
    import com.google.gwt.event.dom.client.ClickEvent;
    import com.google.gwt.event.dom.client.ClickHandler;
    import com.google.gwt.event.dom.client.HasKeyDownHandlers;
    import com.google.gwt.event.dom.client.KeyCodes;
    import com.google.gwt.event.dom.client.KeyDownEvent;
    import com.google.gwt.event.dom.client.KeyDownHandler;
    import com.google.gwt.event.shared.HandlerRegistration;
    import com.google.gwt.safecss.shared.SafeStyles;
    import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
    import com.google.gwt.safehtml.shared.SafeHtmlUtils;
    import com.google.gwt.user.client.Event;
    import com.google.gwt.user.client.ui.HorizontalPanel;
    import com.google.gwt.user.client.ui.Image;
    import com.sencha.gxt.core.client.IdentityValueProvider;
    import com.sencha.gxt.core.client.Style;
    import com.sencha.gxt.core.client.dom.XElement;
    import com.sencha.gxt.data.shared.LabelProvider;
    import com.sencha.gxt.data.shared.ListStore;
    import com.sencha.gxt.data.shared.ModelKeyProvider;
    import com.sencha.gxt.widget.core.client.event.RowClickEvent;
    import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent;
    import com.sencha.gxt.widget.core.client.form.TextField;
    import com.sencha.gxt.widget.core.client.grid.CheckBoxSelectionModel;
    import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
    import com.sencha.gxt.widget.core.client.grid.ColumnModel;
    import com.sencha.gxt.widget.core.client.grid.Grid;
    import com.sencha.gxt.widget.core.client.menu.Menu;
    import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
    import com.sencha.gxt.widget.core.client.tips.QuickTip;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MultiSelectComboBox<T> extends HorizontalPanel implements HasKeyDownHandlers {
    
        private static final Icons ICONS = GWT.create(Icons.class);
        private static final int SINGLE_ENTRY_HEIGHT = 22;
        private static final int IMAGE_WIDTH = 17;
    
        private TextField textField;
        private Menu menu;
        private Grid<SimpleValue> grid;
        private ModelKeyProvider<T> keyProvider;
        private LabelProvider<T> labelProvider;
        private List<T> values;
        private ListStore<SimpleValue> store;
        private GridClickHandler clickHandler;
        private CheckBoxSelectionModel<SimpleValue> sm;
        private String noSelectionLabel;
        private int listWidth;
        private int width;
    
        private MultiSelectComboBox(final ListStore<SimpleValue> store, ModelKeyProvider<T> keyProvider, LabelProvider<T> labelProvider, List<T> values, String noSelectionLabel, int width, int listWidth) {
            this.store = store;
            this.keyProvider = keyProvider;
            this.labelProvider = labelProvider;
            this.values = values;
            this.noSelectionLabel = noSelectionLabel;
            this.width = width;
            this.listWidth = listWidth;
            init();
        }
    
        public List<T> getValues() {
            List<T> selectedItems = new ArrayList<>();
            for (SimpleValue data : sm.getSelectedItems()) {
                selectedItems.add(findRealValueInternal(data.getKey()));
            }
            return selectedItems;
        }
    
        public void setValue(final T value) {
            setValues(new ArrayList<T>() {{add(value);}});
        }
    
        public void setValues(List<T> values) {
            for (T value : values) {
                boolean alreadyExists = findRealValueInternal(keyProvider.getKey(value)) != null;
                if (!alreadyExists) {
                    values.add(value);
                    grid.getStore().add(SimpleValue.create(value, keyProvider, labelProvider));
                }
            }
            for (T value : values) {
                SimpleValue simpleValue = findSimpleValueInternal(keyProvider.getKey(value));
                if (!sm.isSelected(simpleValue)) {
                    sm.select(simpleValue, true);
                }
            }
        }
    
        public void addAll(List<T> list) {
            for (T v : list) {
                add(v);
            }
            grid.setHeight(values.size() * SINGLE_ENTRY_HEIGHT);
        }
    
        public void add(T value) {
            boolean alreadyExists = findRealValueInternal(keyProvider.getKey(value))  != null;
            if (!alreadyExists) {
                values.add(value);
                grid.getStore().add(SimpleValue.create(value, keyProvider, labelProvider));
            }
        }
    
        public void clearStore() {
            values.clear();
            grid.getStore().clear();
        }
    
        private SimpleValue findSimpleValueInternal(String key) {
            SimpleValue value = null;
            for (SimpleValue v : grid.getStore().getAll()) {
                if (v.getKey().equals(key)) {
                    value = v;
                    break;
                }
            }
            return value;
        }
    
        public boolean isValid() {
            return textField.isValid();
        }
    
        @SuppressWarnings("GWTStyleCheck")
        private void init() {
            menu = new Menu();
    
            initGrid();
            menu.add(grid);
            textField = new TextField();
            textField.setReadOnly(true);
            textField.setValue(noSelectionLabel);
            textField.setWidth(width);
            textField.addStyleName("multiComboTextFieldStyle");
    
            textField.addKeyDownHandler(new KeyDownHandler() {
                @Override
                public void onKeyDown(KeyDownEvent event) {
                    if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
                        textField.finishEditing();
                        textField.sinkEvents(KeyCodes.KEY_ENTER);
                    }
                }
            });
    
            Image image = new Image(ICONS.arrowDown());
            image.addStyleName("multiComboButtonStyle");
            image.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent clickEvent) {
                    menu.showAt(textField.getAbsoluteLeft(), textField.getAbsoluteTop() + textField.getOffsetHeight());
                }
            });
    
            add(textField);
            add(image);
        }
    
        @SuppressWarnings({"unchecked", "GWTStyleCheck"})
        private void initGrid() {
            IdentityValueProvider<SimpleValue> identity = new IdentityValueProvider<>();
            sm = new CheckBoxSelectionModel<SimpleValue>(identity) {
                @Override
                protected void onRowClick(RowClickEvent event) {}
    
                @Override
                protected void onRowMouseDown(RowMouseDownEvent event) {
                    boolean left = event.getEvent().getButton() == Event.BUTTON_LEFT;
                    XElement target = event.getEvent().getEventTarget().cast();
                    if (left && this.getAppearance().isRowChecker(target)) {
                        controlSelection(listStore.get(event.getRowIndex()));
                    }
                }
            };
            sm.setSelectionMode(Style.SelectionMode.MULTI);
            sm.addSelectionChangedHandler(new SelectionChangedEvent.SelectionChangedHandler<SimpleValue>() {
                @Override
                public void onSelectionChanged(SelectionChangedEvent<SimpleValue> simpleValueSelectionChangedEvent) {
                    setValuesToTextField();
                }
            });
    
            ColumnModel columnModel = new ColumnModel(getConfigs());
            grid = new Grid<>(store, columnModel, new ZGridView<SimpleValue>());
            grid.setColumnReordering(true);
            grid.getView().setColumnLines(true);
            grid.setSelectionModel(sm);
            grid.setWidth(listWidth);
            grid.setHeight(values.size() * SINGLE_ENTRY_HEIGHT);
            grid.setHideHeaders(true);
            grid.getView().setColumnLines(false);
            grid.addStyleName("z-grid-style");
    
            new QuickTip(grid);
    
            clickHandler = new GridClickHandler() {
                @Override
                public void onClick(Cell.Context context, Object value) {
                    controlSelection((SimpleValue)((Object[])value)[0]);
                }
            };
        }
    
        @SuppressWarnings("unchecked")
        private List<ColumnConfig> getConfigs() {
            List<ColumnConfig> configs = new ArrayList<>();
    
            ColumnConfig<SimpleValue, String> columnConfig = new ColumnConfig(new ZEmptyValueProvider(), listWidth - sm.getColumn().getWidth());
            columnConfig.setCell(new AbstractCell<String>("click") {
                @Override
                public void render(Context context, String value, SafeHtmlBuilder sb) {
                    String htmlValue = store.get(context.getIndex()).getLabel();
                    sb.append(SafeHtmlUtils.fromTrustedString(htmlValue != null ? "<div style=\"font-size: 12px;\">" + htmlValue + "</div>": ""));
                }
    
                @Override
                public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
                    super.onBrowserEvent(context, parent, value, event, valueUpdater);
                    if (("click").equals(event.getType())) {
                        if (clickHandler != null) {
                            Object[] objects = new Object[1];
                            objects[0] = store.get(context.getIndex());
                            clickHandler.onClick(context, objects);
                        }
                    }
                }
            });
            columnConfig.setColumnStyle(new SafeStyles() {
                @Override
                public String asString() {
                    return "cursor: pointer;";
                }
            });
            columnConfig.setColumnStyle(new SafeStyles() {
                @Override
                public String asString() {
                    return "border: none;";
                }
            });
            configs.add(sm.getColumn());
            configs.add(columnConfig);
    
            return configs;
        }
    
        private void controlSelection(SimpleValue model) {
            if (model != null) {
                if (sm.isSelected(model)) {
                    sm.deselect(model);
                } else {
                    sm.select(model, true);
                }
            }
        }
    
        private void setValuesToTextField() {
            if (sm.getSelectedItems().size() > 0) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < sm.getSelectedItems().size(); i++) {
                    sb.append(sm.getSelectedItems().get(i).getLabel());
                    if (i != sm.getSelectedItems().size() - 1) {
                        sb.append(", ");
                    }
                }
                textField.setValue(sb.toString());
                textField.setToolTip(sb.toString());
            } else {
                textField.setValue(noSelectionLabel);
                textField.removeToolTip();
            }
        }
    
        private T findRealValueInternal(String key) {
            T value = null;
            for (T v : values) {
                if (key.equals(keyProvider.getKey(v))) {
                    value = v;
                    break;
                }
            }
            return value;
        }
    
        @Override
        public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
            return addDomHandler(handler, KeyDownEvent.getType());
        }
    
        public void setEnabled(boolean enabled) {
            textField.setEnabled(enabled);
        }
    
        public void setToolTip(String tooltip) {
            textField.setToolTip(tooltip);
        }
    
        public static class Builder<T> {
            private ModelKeyProvider<T> keyProvider;
            private LabelProvider<T> labelProvider;
            private String noSelectionLabel;
            private List<T> values = new ArrayList<>();
            private int listWidth;
            private int width = 130;
    
            public Builder<T> keyProvider(ModelKeyProvider<T> keyProvider) {
                this.keyProvider = keyProvider;
                return this;
            }
    
            public Builder labelProvider(LabelProvider<T> labelProvider) {
                this.labelProvider = labelProvider;
                return this;
            }
    
            public Builder noSelectionLabel(String noSelectionLabel) {
                this.noSelectionLabel = noSelectionLabel;
                return this;
            }
    
            public Builder values(List<T> values) {
                this.values = values;
                return this;
            }
    
            public Builder listWidth(int listWidth) {
                this.listWidth = listWidth;
                return this;
            }
    
            public Builder width(int width) {
                this.width = width;
                return this;
            }
    
            @SuppressWarnings("unchecked")
            public MultiSelectComboBox build() {
                if (keyProvider == null) {
                    throw new IllegalStateException("KeyProvider is required");
                }
                if (labelProvider == null) {
                    throw new IllegalStateException("LabelProvider is required");
                }
                if (noSelectionLabel == null) {
                    noSelectionLabel = "";
                }
                ListStore<SimpleValue> store = new ListStore<>(new ModelKeyProvider<SimpleValue>() {
                    @Override
                    public String getKey(SimpleValue item) {
                        return item.getKey();
                    }
                });
                if (values.size() > 0) {
                    for (T obj : values) {
                        store.add(SimpleValue.create(obj, keyProvider, labelProvider));
                    }
                }
                return new MultiSelectComboBox(store, keyProvider, labelProvider, values, noSelectionLabel, width - IMAGE_WIDTH, listWidth == 0 ? width : listWidth - IMAGE_WIDTH);
            }
        }
    
        public static class SimpleValue {
            private String key;
            private String label;
    
            public SimpleValue() {}
    
            public static <T> SimpleValue create(T obj, ModelKeyProvider<T> keyProvider, LabelProvider<T> labelProvider) {
                SimpleValue v = new SimpleValue();
                v.setKey(keyProvider.getKey(obj));
                v.setLabel(labelProvider.getLabel(obj));
                return v;
            }
    
            public String getKey() {
                return key;
            }
    
            public void setKey(String key) {
                this.key = key;
            }
    
            public String getLabel() {
                return label;
            }
    
            public void setLabel(String label) {
                this.label = label;
            }
        }
    }
    

    用法:

    MultiSelectComboBox<String> combo = new MultiSelectComboBox.Builder<String>()
                .values(Arrays.asList(Enum.values()))
                .noSelectionLabel("All Values")
                .width(150)
                .listWidth(180)
                .keyProvider(new ModelKeyProvider<String>() {
                    @Override
                    public String getKey(String s) {
                        return s;
                    }
                })
                .labelProvider(new LabelProvider<String>() {
                    @Override
                    public String getLabel(String s) {
                        return s;
                    }
                })
                .build();
    

    【讨论】:

      【解决方案3】:

      FWIW 我为工具栏实现了一些东西。

      MultiSelectButton 是一个 TextButton,带有一个充满复选框的菜单,并且在按钮标签中放置了一个摘要(例如:“Selected: 4/6”)。

      【讨论】:

        【解决方案4】:

        我从 David Lekishvili 的版本开始实现了我自己的版本,并尝试在某些方面对其进行改进(使用 GXT-3.1.4 和 GWT-2.7.0 进行测试)。

        代码如下:

        MultiSelectComboBox.java

        import com.google.gwt.cell.client.AbstractCell;
        import com.google.gwt.cell.client.Cell;
        import com.google.gwt.core.client.GWT;
        import com.google.gwt.event.dom.client.KeyUpEvent;
        import com.google.gwt.event.dom.client.KeyUpHandler;
        import com.google.gwt.event.logical.shared.ValueChangeEvent;
        import com.google.gwt.event.logical.shared.ValueChangeHandler;
        import com.google.gwt.resources.client.ClientBundle;
        import com.google.gwt.resources.client.CssResource;
        import com.google.gwt.safecss.shared.SafeStyles;
        import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
        import com.google.gwt.safehtml.shared.SafeHtmlUtils;
        import com.google.gwt.user.client.Event;
        import com.google.gwt.user.client.ui.HorizontalPanel;
        import com.sencha.gxt.core.client.IdentityValueProvider;
        import com.sencha.gxt.core.client.Style;
        import com.sencha.gxt.core.client.dom.XElement;
        import com.sencha.gxt.data.shared.LabelProvider;
        import com.sencha.gxt.data.shared.ListStore;
        import com.sencha.gxt.data.shared.ModelKeyProvider;
        import com.sencha.gxt.data.shared.Store;
        import com.sencha.gxt.widget.core.client.event.FocusEvent;
        import com.sencha.gxt.widget.core.client.event.RowClickEvent;
        import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent;
        import com.sencha.gxt.widget.core.client.event.TriggerClickEvent;
        import com.sencha.gxt.widget.core.client.form.CheckBox;
        import com.sencha.gxt.widget.core.client.form.TextField;
        import com.sencha.gxt.widget.core.client.grid.*;
        import com.sencha.gxt.widget.core.client.menu.Menu;
        import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
        import com.sencha.gxt.widget.core.client.tips.QuickTip;
        
        import java.util.ArrayList;
        import java.util.List;
        
        /**
         * Custom GXT component allowing selection of several elements in a combobox along with filtering of values
         * Custom rendering of values can be defined by providing a custom AbstractCell
         */
        public class MultiSelectComboBox<T> extends HorizontalPanel {
        
            /**
             * Number of values that will be displayed in the drop down list before adding a vertical scrollbar
             */
            private static final int NB_VALUES_WITHOUT_SCROLL = 15;
        
            private static final int SINGLE_ENTRY_HEIGHT = 22;
        
            /**
             * Field displaying label of every checked values
             */
            private MultiComboBoxTriggerField selectedItemsField = new MultiComboBoxTriggerField();
        
            /**
             * Menu contains the "select all checkbox" and the filtering text field
             */
            private Menu menu = new Menu();
        
            /**
             * Grid with only one column containing the combobox elements
             */
            private Grid<T> grid;
        
            private CheckBox selectAllCheckBox = new CheckBox();
        
            private ModelKeyProvider<T> keyProvider;
        
            private LabelProvider<T> labelProvider;
        
            private CheckBoxSelectionModel<T> sm;
        
            private ColumnConfig<T, String> columnConfig;
        
            /**
             * Cell used for rendering of values in the combobox drop down list
             */
            private AbstractCell cell;
        
            /**
             * Label to display in case no element is selected in the drop down list
             */
            private String noSelectionLabel = new String("");
        
            /**
             * Combobox field default width
             */
            private int selectedFieldWidth = 150;
        
            /**
             * Combobox drop down list default width
             */
            private int listWidth = 185;
        
            /**
             * This default cell is the one used in case no custom cell has been specified in constructor
             */
            private AbstractCell defaultCell = new AbstractCell() {
                @Override
                public void render(Cell.Context context, Object value, SafeHtmlBuilder sb) {
                    String label = labelProvider.getLabel(grid.getStore().get(context.getIndex()));
                    sb.append(SafeHtmlUtils.fromTrustedString(label != null ? "<div style=\"font-size: 12px;\">" + label + "</div>" : ""));
                }
            };
        
            /**
             * Default constructor
             *
             * @param keyProvider
             * @param labelProvider
             */
            public MultiSelectComboBox(ModelKeyProvider<T> keyProvider, LabelProvider<T> labelProvider) {
                this.keyProvider = keyProvider;
                this.labelProvider = labelProvider;
        
                cell = defaultCell;
        
                init();
            }
        
            /**
             * Constructor with a custom width for the drop down list
             *
             * @param keyProvider
             * @param labelProvider
             */
            public MultiSelectComboBox(ModelKeyProvider<T> keyProvider, LabelProvider<T> labelProvider, int listWidth) {
                this.keyProvider = keyProvider;
                this.labelProvider = labelProvider;
                this.listWidth = listWidth;
        
                cell = defaultCell;
        
                init();
            }
        
            /**
             * Constructor with a custom cell defined for values rendering
             *
             * @param keyProvider
             * @param labelProvider
             * @param customCell
             */
            public MultiSelectComboBox(ModelKeyProvider<T> keyProvider, LabelProvider<T> labelProvider, AbstractCell customCell) {
                this.keyProvider = keyProvider;
                this.labelProvider = labelProvider;
        
                cell = customCell;
        
                init();
            }
        
            private void init() {
                StyleResources.INSTANCE.multiSelectComboStyle().ensureInjected();
        
                initGrid();
        
                // Filter field
                final TextField filterField = new TextField();
                filterField.setEmptyText(IpWebParameters.INSTANCE.filterLabel());
        
                final Store.StoreFilter<T> filter = new Store.StoreFilter<T>() {
                    @Override
                    public boolean select(Store<T> store, T parent, T item) {
                        return labelProvider.getLabel(item).toUpperCase().contains(filterField.getText().toUpperCase());
                    }
                };
        
                // Filter values when the user fill in the filter field
                filterField.addKeyUpHandler(new KeyUpHandler() {
                    @Override
                    public void onKeyUp(KeyUpEvent event) {
                        if (filterField.getText().length() > 1) {
                            grid.getStore().removeFilters();
                            grid.getStore().addFilter(filter);
                            grid.getStore().setEnableFilters(true);
                        } else {
                            grid.getStore().removeFilters();
                        }
                    }
                });
        
                selectAllCheckBox.setStyleName(StyleResources.INSTANCE.multiSelectComboStyle().selectAllCheckbox());
                selectAllCheckBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
                    @Override
                    public void onValueChange(ValueChangeEvent<Boolean> event) {
                        if (sm.isSelectAllChecked()) {
                            sm.setSelectAllChecked(false);
                        } else {
                            sm.setSelectAllChecked(true);
                        }
                    }
                });
        
                HorizontalPanel filterPanel = new HorizontalPanel();
                filterPanel.add(selectAllCheckBox);
                filterPanel.add(filterField);
        
                // Menu contains filter fields and checkable items
                menu.add(filterPanel);
                menu.add(grid);
        
                selectedItemsField.addStyleName(StyleResources.INSTANCE.multiSelectComboStyle().multiComboTextField());
        //        selectedItemsField.addStyleOnOver(selectedItemsField.getElement(), StyleResources.INSTANCE.multiSelectComboStyle().multiComboTextField());
        
                selectedItemsField.setReadOnly(false);
                selectedItemsField.setValue(noSelectionLabel);
                selectedItemsField.setWidth(selectedFieldWidth);
        
                selectedItemsField.addTriggerClickHandler(new TriggerClickEvent.TriggerClickHandler() {
                    @Override
                    public void onTriggerClick(TriggerClickEvent event) {
                        menu.showAt(selectedItemsField.getAbsoluteLeft(), selectedItemsField.getAbsoluteTop() + selectedItemsField.getOffsetHeight());
                    }
                });
        
                selectedItemsField.addFocusHandler(new FocusEvent.FocusHandler() {
                    @Override
                    public void onFocus(FocusEvent event) {
                        menu.showAt(selectedItemsField.getAbsoluteLeft(), selectedItemsField.getAbsoluteTop() + selectedItemsField.getOffsetHeight());
                    }
                });
        
                add(selectedItemsField);
            }
        
            /**
             * Creation of the grid object required for rendering of values in the drop down list
             */
            private void initGrid() {
                IdentityValueProvider<T> identity = new IdentityValueProvider<>();
        
                // Check elements when clicking on the checkbox or on the row
                sm = new CheckBoxSelectionModel<T>(identity) {
                    @Override
                    protected void onRowClick(RowClickEvent event) {
                        boolean left = event.getEvent().getButton() == Event.BUTTON_LEFT;
                        XElement target = event.getEvent().getEventTarget().cast();
                        if (left && !this.getAppearance().isRowChecker(target)) {
                            controlSelection(listStore.get(event.getRowIndex()));
                        }
                    }
        
                    @Override
                    protected void onRowMouseDown(RowMouseDownEvent event) {
                        boolean left = event.getEvent().getButton() == Event.BUTTON_LEFT;
                        XElement target = event.getEvent().getEventTarget().cast();
                        if (left && this.getAppearance().isRowChecker(target)) {
                            controlSelection(listStore.get(event.getRowIndex()));
                        }
                    }
                };
        
                // allow the user to select multiple values
                sm.setSelectionMode(Style.SelectionMode.MULTI);
        
                // Manage selectAll check box depending on already selected values
                sm.addSelectionChangedHandler(new SelectionChangedEvent.SelectionChangedHandler<T>() {
                    @Override
                    public void onSelectionChanged(SelectionChangedEvent<T> simpleValueSelectionChangedEvent) {
                        // automatically check or uncheck the "select all checkbox" depending on current user selection
                        if (selectAllCheckBox.getValue() && !isAllSelected()) {
                            selectAllCheckBox.setValue(false);
                        } else if (!selectAllCheckBox.getValue() && isAllSelected()) {
                            selectAllCheckBox.setValue(true);
                        }
        
                        setValuesToTextField();
                    }
                });
        
        
                ColumnModel columnModel = new ColumnModel(getColumnConfig());
                ListStore<T> store = new ListStore<>(keyProvider);
                grid = new Grid<>(store, columnModel, new GridView<T>());
                grid.setSelectionModel(sm);
                grid.setAllowTextSelection(false);
                grid.setHideHeaders(true);
                grid.setBorders(false);
        
                // Define grid view properties
                grid.getView().setColumnLines(false);
                grid.getView().setAdjustForHScroll(false);
                grid.getView().setTrackMouseOver(false);
        
                refreshGridHeight();
        
                new QuickTip(grid);
            }
        
            /**
             * @return the list of currently selected items
             */
            public List<T> getSelectedItems() {
                return sm.getSelectedItems();
            }
        
            /**
             * Add a list of checkable item in the drop down list
             *
             * @param list
             */
            public void addAll(List<T> list) {
                for (T v : list) {
                    add(v);
                }
            }
        
            /**
             * Add a checkable item in the drop down list
             *
             * @param item the item to add
             */
            public void add(T item) {
                boolean alreadyExists = false;
                if (!alreadyExists) {
                    grid.getStore().add(item);
                }
        
                refreshGridHeight();
                refreshColumnWidth();
            }
        
            /**
             * @return all items contained in the store
             */
            public List<T> getAllItems() {
                return grid.getStore().getAll();
            }
        
            public void clearStore() {
                grid.getStore().clear();
            }
        
            private List<ColumnConfig> getColumnConfig() {
                List<ColumnConfig> configs = new ArrayList<>();
        
                // Width depends on the presence of a scrollbar
                columnConfig = new ColumnConfig(new IdentityValueProvider<T>());
        
                columnConfig.setCell(cell);
        
                columnConfig.setColumnStyle(new SafeStyles() {
                    @Override
                    public String asString() {
                        return "border: none;";
                    }
                });
        
                configs.add(sm.getColumn());
                configs.add(columnConfig);
        
                return configs;
            }
        
            /**
             * Adjust drop down list height depending on the number of elements in the list. A scrollbar is added if this number exceeds NB_VALUES_WITHOUT_SCROLL
             */
            private void refreshGridHeight() {
                if (getAllItems().size() > NB_VALUES_WITHOUT_SCROLL) {
                    grid.setHeight(NB_VALUES_WITHOUT_SCROLL * SINGLE_ENTRY_HEIGHT);
                } else {
                    grid.setHeight(getAllItems().size() * SINGLE_ENTRY_HEIGHT);
                }
            }
        
            /**
             * Refresh width of columns. Width depends on the presence of a scrollbar
             */
            private void refreshColumnWidth() {
                int columnWidth = listWidth - sm.getColumn().getWidth() - 25;
                if (getAllItems().size() < NB_VALUES_WITHOUT_SCROLL) {
                    columnWidth = listWidth - sm.getColumn().getWidth();
                }
                columnConfig.setWidth(columnWidth);
            }
        
            /**
             * Switch the selection status of the given item
             *
             * @param item
             */
            private void controlSelection(T item) {
                if (item != null) {
                    if (sm.isSelected(item)) {
                        sm.deselect(item);
                    } else {
                        sm.select(item, true);
                    }
                }
            }
        
            /**
             * Manage the label to display in the "selected item field". Label of each selected values is displayed or "All" if all elements are selected
             */
            private void setValuesToTextField() {
                if (sm.getSelectedItems().size() > 0) {
                    if (isAllSelected()) {
                        selectedItemsField.setValue(IpWebParameters.INSTANCE.all());
                        selectedItemsField.setToolTip(IpWebParameters.INSTANCE.all());
                    } else {
                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < sm.getSelectedItems().size(); i++) {
                            sb.append(labelProvider.getLabel(sm.getSelectedItems().get(i)));
                            if (i != sm.getSelectedItems().size() - 1) {
                                sb.append(", ");
                            }
                        }
                        selectedItemsField.setValue(sb.toString());
                        selectedItemsField.setToolTip(sb.toString());
                    }
                } else {
                    selectedItemsField.setValue(noSelectionLabel);
                    selectedItemsField.removeToolTip();
                }
            }
        
            public void setEnabled(boolean enabled) {
                selectedItemsField.setEnabled(enabled);
            }
        
            /**
             * Set the matching model item as checked
             *
             * @param item the item to check
             */
            public void select(T item) {
                if (!sm.isSelected(item)) {
                    sm.select(true, item);
                }
            }
        
            /**
             * Set the matching model items as checked
             *
             * @param items the item to check
             */
            public void select(List<T> items) {
                for (T item : items) {
                    this.select(item);
                }
            }
        
            /**
             * Check all items in the drop down list
             */
            public void selectAll() {
                for (T item : getAllItems()) {
                    this.select(item);
                }
            }
        
            /**
             * deselect all items in the drop down list
             */
            public void deselectAll() {
                sm.deselectAll();
            }
        
            /**
             * @return true if all items are selected
             */
            public boolean isAllSelected() {
                return sm.getSelection().size() == getAllItems().size();
            }
        
            public interface ComboStyle extends CssResource {
                String multiComboTextField();
        
                String selectAllCheckbox();
            }
        
            public interface StyleResources extends ClientBundle {
                StyleResources INSTANCE = GWT.create(StyleResources.class);
        
                @Source("multiselectcombo.css")
                ComboStyle multiSelectComboStyle();
            }
        
        }
        

        MultiComboBoxTriggerField.java

        import com.google.gwt.event.shared.HandlerRegistration;
        import com.sencha.gxt.cell.core.client.form.TriggerFieldCell;
        import com.sencha.gxt.widget.core.client.event.CollapseEvent;
        import com.sencha.gxt.widget.core.client.event.ExpandEvent;
        import com.sencha.gxt.widget.core.client.form.PropertyEditor;
        import com.sencha.gxt.widget.core.client.form.TriggerField;
        
        /**
         * Trigger Field for multi select combo box
         */
        public class MultiComboBoxTriggerField extends TriggerField<String> implements ExpandEvent.HasExpandHandlers, CollapseEvent.HasCollapseHandlers {
        
            public MultiComboBoxTriggerField() {
                this(new TriggerFieldCell<String>(), new MultiComboBoxPropertyEditor());
            }
        
            protected MultiComboBoxTriggerField(TriggerFieldCell<String> cell, PropertyEditor<String> propertyEditor) {
                super(cell, propertyEditor);
            }
        
            @Override
            public HandlerRegistration addExpandHandler(ExpandEvent.ExpandHandler handler) {
                return addHandler(handler, ExpandEvent.getType());
            }
        
            @Override
            public HandlerRegistration addCollapseHandler(CollapseEvent.CollapseHandler handler) {
                return addHandler(handler, CollapseEvent.getType());
            }
        }
        

        MultiComboBoxPropertyEditor.java

        import com.sencha.gxt.widget.core.client.form.PropertyEditor;
        
        import java.text.ParseException;
        
        /**
         * Property editor for multi select combo box
         */
        public class MultiComboBoxPropertyEditor extends PropertyEditor<String> {
        
            @Override
            public String parse(CharSequence text) throws ParseException {
                return text.toString();
            }
        
            @Override
            public String render(String object) {
                return object;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-07
          • 2015-02-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多