Wednesday, February 1, 2012

Three steps to tree view

This time something easy:). I was preparing new navigation using the GWT TreeView widget and as usual I wanted to share some of my experience with you. Basically everything works just by design, so here the three steps of creation.

First step: you have to create your cell tree model. This is in my case just the class with the name of the node and his sub nodes. I call the class CellTreeDAOModel.

 
import java.util.ArrayList;
import java.util.List;
 
public class CellTreeDAOModel {
 
    private final String name;
    private final List<String> sublevels = new ArrayList<String>();
 
    public CellTreeDAOModel(String name) {
        this.name = name;
    }
 
    public void addSubLevels(String name) {
        sublevels.add(name);
    }
 
    public String getName() {
        return name;
    }
 
    public List<String> getSubLevels() {
        return sublevels;
    }
}

Second step: create your cell tree composer. The composer contains your cell tree mode. In my case this is the CellTreeModelComposer.



import java.util.ArrayList;
import java.util.List;
 
public class CellTreeModelComposer {
 
    private final String name;
    private final List<CellTreeDAOModel> sublists = new ArrayList<CellTreeDAOModel>();
 
    public CellTreeModelComposer(String name) {
        this.name = name;
    }
 
    public CellTreeDAOModel addPlaylist(CellTreeDAOModel playlist) {
        sublists.add(playlist);
        return playlist;
    }
 
    public String getName() {
        return name;
    }
 
    public List<CellTreeDAOModel> getSubLists() {
        return sublists;
    }
}

Third step: create your tree view mode. Here you set up the nodes and define how all those nodes has be rendered:



import java.util.ArrayList;
import java.util.List;
 
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.TreeViewModel;
 
public class CustomTreeModel implements TreeViewModel {
 
    private final List<CellTreeModelComposer> composers;
 
    /**
     * This selection model is shared across all leaf nodes. A selection model
     * can also be shared across all nodes in the tree, or each set of child
     * nodes can have its own instance. This gives you flexibility to determine
     * how nodes are selected.
     */
    private final SingleSelectionModel<String> selectionModel = new SingleSelectionModel<String>();
 
    public CustomTreeModel() {
        composers = new ArrayList<CellTreeModelComposer>();
 
        // Add compositions
        {
            CellTreeModelComposer level1 = new CellTreeModelComposer("Level 1");
            composers.add(level1);
 
            CellTreeDAOModel sublevel_1_1 = level1
                    .addPlaylist(new CellTreeDAOModel("Sub Level 1.1"));
            sublevel_1_1.addSubLevels("Level 1.1.1");
            sublevel_1_1.addSubLevels("Level 1.1.2");
            sublevel_1_1.addSubLevels("Level 1.1.3");
            sublevel_1_1.addSubLevels("Level 1.1.4");
            sublevel_1_1.addSubLevels("Level 1.1.5");
 
            CellTreeDAOModel sublevel_1_2 = level1
                    .addPlaylist(new CellTreeDAOModel("Sub Level 1.2"));
            sublevel_1_2.addSubLevels("Level 1.2.1");
            sublevel_1_2.addSubLevels("Level 1.2.2");
            sublevel_1_2.addSubLevels("Level 1.2.3");
 
            CellTreeDAOModel sublevel_1_3 = level1
                    .addPlaylist(new CellTreeDAOModel("Sub Level 1.3"));
            sublevel_1_3.addSubLevels("Level 1.3.4");
            sublevel_1_3.addSubLevels("Level 1.3.5");
 
            CellTreeDAOModel sublevel_1_4 = level1
                    .addPlaylist(new CellTreeDAOModel("Sub Level 1.4"));
            sublevel_1_4.addSubLevels("Level 1.4.1");
            sublevel_1_4.addSubLevels("Level 1.4.2");
            sublevel_1_4.addSubLevels("Level 1.4.3");
            sublevel_1_4.addSubLevels("Level 1.4.4");
            sublevel_1_4.addSubLevels("Level 1.4.5");
            sublevel_1_4.addSubLevels("Level 1.4.6");
        }
 
        // Add compositions
        {
            CellTreeModelComposer level_2 = new CellTreeModelComposer("Level 2");
            composers.add(level_2);
            CellTreeDAOModel sublevel_2_1 = level_2
                    .addPlaylist(new CellTreeDAOModel("Sub Level 2_1"));
            sublevel_2_1.addSubLevels("Level 2_1_1");
            sublevel_2_1.addSubLevels("Level 2_1_2");
            sublevel_2_1.addSubLevels("Level 2_1_3");
            sublevel_2_1.addSubLevels("Level 2_1_4");
 
            CellTreeDAOModel sublevel_2_2 = level_2
                    .addPlaylist(new CellTreeDAOModel("Sub Level 2_2"));
            sublevel_2_2.addSubLevels("Level 2_2");
            sublevel_2_2.addSubLevels("Level 2_2");
            sublevel_2_2.addSubLevels("Level 2_2");
            sublevel_2_2.addSubLevels("Level 2_2");
 
            CellTreeDAOModel sublevel_2_3 = level_2
                    .addPlaylist(new CellTreeDAOModel("Sub Level 2_3"));
            sublevel_2_3.addSubLevels("Level 2_3_1");
            sublevel_2_3.addSubLevels("Level 2_3_2");
 
            CellTreeDAOModel sublevel_2_4 = level_2
                    .addPlaylist(new CellTreeDAOModel("Sub Level 2_4"));
            sublevel_2_4.addSubLevels("Level 2_4_1");
            sublevel_2_4.addSubLevels("Level 2_4_2");
            sublevel_2_4.addSubLevels("Level 2_4_3");
            sublevel_2_4.addSubLevels("Level 2_4_4r");
        }
 
        // Add compositions
        {
            CellTreeModelComposer sublevel_3 = new CellTreeModelComposer(
                    "Level 3");
            composers.add(sublevel_3);
            CellTreeDAOModel sublevel_3_1 = sublevel_3
                    .addPlaylist(new CellTreeDAOModel("Sub Level 3_1"));
            sublevel_3_1.addSubLevels("Sub Level 3_1_1");
            sublevel_3_1.addSubLevels("Sub Level 3_1_2");
            sublevel_3_1.addSubLevels("Sub Level 3_1_3");
            sublevel_3_1.addSubLevels("Sub Level 3_1_4");
            sublevel_3_1.addSubLevels("Sub Level 3_1_5");
        }
    }
 
    @Override
    public <T> NodeInfo<?> getNodeInfo(T value) {
        if (value == null) {
            // Create a data provider that contains the list of composers
            ListDataProvider<CellTreeModelComposer> dataProvider = new ListDataProvider<CellTreeModelComposer>(
                    composers);
 
            // Create a cell to display
            Cell<CellTreeModelComposer> cell = new AbstractCell<CellTreeModelComposer>() {
                @Override
                public void render(Context context,
                        CellTreeModelComposer value, SafeHtmlBuilder sb) {
                    if (value != null) {
                        sb.appendEscaped(value.getName());
                    }
                }
            };
 
            // Return a node info that pairs the data provider and the cell
            return new DefaultNodeInfo<CellTreeModelComposer>(dataProvider,
                    cell);
 
        } else if (value instanceof CellTreeModelComposer) {
            // get the first sub level
            ListDataProvider<CellTreeDAOModel> dataProvider = new ListDataProvider<CellTreeDAOModel>(
                    ((CellTreeModelComposer) value).getSubLists());
            Cell<CellTreeDAOModel> cell = new AbstractCell<CellTreeDAOModel>() {
                @Override
                public void render(Context context, CellTreeDAOModel value,
                        SafeHtmlBuilder sb) {
                    if (value != null) {
                        sb.appendEscaped(value.getName());
                    }
                }
            };
 
            return new DefaultNodeInfo<CellTreeDAOModel>(dataProvider, cell);
 
        } else if (value instanceof CellTreeDAOModel) {
            // get the third sub level
            ListDataProvider<String> dataProvider = new ListDataProvider<String>(
                    ((CellTreeDAOModel) value).getSubLevels());
 
            // Use the shared selection model
            return new DefaultNodeInfo<String>(dataProvider, new TextCell(),
                    selectionModel, null);
        }
 
        return null;
    }
 
    @Override
    public boolean isLeaf(Object value) {
 
        if (value instanceof String) {
            return true;
        }
        return false;
 
    }
 
}

The function getNodeInfo from the TreeViewModel interface is the most important. You define into it how your tree will render the content.


If you want to use now together with UiBinding you have to define your UiField with the property provided set to true like this:



@UiField(provided = true)
CellTree cellTree;

and then inside your constructor you can use this:



// add the new tree
cellTree = new CellTree(model, null);
cellTree.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
 
//
TreeNode rootNode = cellTree.getRootTreeNode();
TreeNode firstPlaylist = rootNode.setChildOpen(0, true);
firstPlaylist.setChildOpen(0, true);

Don’t forget to put also the tree declaration inside your UiBinder XML file:



<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui" 
        xmlns:p1="urn:import:com.google.gwt.user.cellview.client">
    <ui:style>
        .important {
            font-weight: bold;
        }
    </ui:style>
    <g:HTMLPanel>
        <g:VerticalPanel>
            <p1:CellTree ui:field="cellTree"/>
        </g:VerticalPanel>
    </g:HTMLPanel>
</ui:UiBinder> 
cheers

2 comments:

  1. Hi Lyudmil,
    could you please post all the classes/code for TreeView

    ReplyDelete
  2. Hi Anonymous,
    the source code for all examples is available under Google Code here:
    https://code.google.com/p/gwt2go/

    ReplyDelete