Sascha's Wiki

Erstellen einer BeanTreeView mit Drag & Drop - Funktion

Dependencies

Folgende Dependencies müssen im Projekt hinzugefügt werden:

  • Actions API
  • Explorer and Property Sheet API
  • Nodes API

Allgemein

Bei einem Tree-View wird die Hierarchie der Knoten wie folgt dargestellt:

  • Root
    • Node
      • Children Node

Für die Umsetzung der jeweilgen Knotenhierarche bzw. -stufe werden jeweils drei Klassen benötigt. Im folgenden Beispiel werden als Node Kategorien dargestellt und als Children Node Filme. Die Aufgaben der drei Klassen teilen sich wie folgt auf (Beispiel Category):

  • Category (Definition der Kategorie)
  • CategoryChildren (Erzeugung der Knoten)
  • CategoryNode (Methoden)

Umsetzung

Anfangs wird ein Window erstellt und eine JScrollPane eingefügt. Bei der Initiierung der Componenten muss der Code angepasst werden:

jScrollPane1 = new BeanTreeView();

Die Klasse MyFirstTopComponent implementiert den Explorer Manager, welche für die Verwaltung der Knoten inkl. dem View verantwortlich ist. Im Konstruktor wird die Klasse „CategoryChildren“ als Node der Root und in der Action-Map die Menu-Items (Cut, Copy, Paste) festgelegt.

public final class MyFirstTopComponent extends TopComponent implements ExplorerManager.Provider {

    private transient ExplorerManager explorerManager = new ExplorerManager();

    public MyFirstTopComponent() {
        initComponents();
        setName(Bundle.CTL_MyFirstTopComponent());
        setToolTipText(Bundle.HINT_MyFirstTopComponent());

        associateLookup(ExplorerUtils.createLookup(explorerManager, getActionMap()));
        explorerManager.setRootContext(new RootNode(new CategoryChildren()));
        explorerManager.getRootContext().setDisplayName("Name Wurzel");

        ActionMap map = getActionMap();
        map.put(DefaultEditorKit.copyAction, ExplorerUtils.actionCopy(explorerManager));
        map.put(DefaultEditorKit.cutAction, ExplorerUtils.actionCut(explorerManager));
        map.put(DefaultEditorKit.pasteAction, ExplorerUtils.actionPaste(explorerManager));
        map.put("delete", ExplorerUtils.actionDelete(explorerManager, true));

    }

Der Rückgabewert der implementierten Funktion muss wie folgt angepasst werden:

    @Override
    public ExplorerManager getExplorerManager() {
        return explorerManager;
    }

Nodes

Die Klasse „Category“ legt fest, dass es nur einen Namen gibt.

public class Category {

    private String name;

    /**
     * Creates a new instance of Category
     */
    public Category() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

In der Klasse CategoryChildren werden die jeweiligen Nodes in Form von Key-Objekten erzeugt. addNotify erstellt eine Liste der Keys und fügt diese anschließend ein. In der Methode createNodes werden die Nodes anschließend erstellt.

public class CategoryChildren extends Children.Keys {

    private String[] Categories = new String[]{
        "Kategorie 1",
        "Kategorie 2",
        "Kategorie 3",
        "Kategorie 4",
        "Kategorie 5"};

    public CategoryChildren() {
    }

    @Override
    protected Node[] createNodes(Object key) {
        Category obj = (Category) key;
        return new Node[]{new CategoryNode(obj)};
    }

    protected void addNotify() {
        super.addNotify();
        Category[] objs = new Category[Categories.length];
        for (int i = 0; i < objs.length; i++) {
            Category cat = new Category();
            cat.setName(Categories[i]);
            objs[i] = cat;
        }
        setKeys(objs);
    }
}

Für die schlussendliche Implementation wird die CategoryNode - Klasse erstellt. Sie erbt von der Klasse AbstractNode, welche für die Verwirklichung der Nodes zuständig ist. Sie ist verantwortlich für die Erstellung des Displaynamens und für das Handling der Icons usw.

In der Klasse werden die notwendigen Methoden aufgelistet, welche einfach unverändert stets übernommen werden können, um u.a. das Drag & Drop realisieren zu können.

public class CategoryNode extends AbstractNode {

    /**
     * Creates a new instance of CategoryNode
     */
    public CategoryNode(Category category) {
        super(new MovieChildren(category), Lookups.singleton(category));
        setDisplayName(category.getName());
        setIconBaseWithExtension("c/walter/NodeTool_2/1394112522_sections.png");
    }

    public PasteType getDropType(Transferable t, final int action, int index) {
        final Node dropNode = NodeTransfer.node(t,
                DnDConstants.ACTION_COPY_OR_MOVE + NodeTransfer.CLIPBOARD_CUT);
        if (null != dropNode) {
            final Movie movie = (Movie) dropNode.getLookup().lookup(Movie.class);
            if (null != movie && !this.equals(dropNode.getParentNode())) {
                return new PasteType() {
                    public Transferable paste() throws IOException {
                        getChildren().add(new Node[]{new MovieNode(movie)});
                        if ((action & DnDConstants.ACTION_MOVE) != 0) {
                            dropNode.getParentNode().getChildren().remove(new Node[]{dropNode});
                        }
                        return null;
                    }
                };
            }
        }
        return null;
    }

    public Cookie getCookie(Class clazz) {
        Children ch = getChildren();

        if (clazz.isInstance(ch)) {
            return (Cookie) ch;
        }

        return super.getCookie(clazz);
    }

    protected void createPasteTypes(Transferable t, List s) {
        super.createPasteTypes(t, s);
        PasteType paste = getDropType(t, DnDConstants.ACTION_COPY, -1);
        if (null != paste) {
            s.add(paste);
        }
    }

    @Override
    public Action[] getActions(boolean context) {
        return new Action[]{
            SystemAction.get(NewAction.class),
            SystemAction.get(PasteAction.class)};
    }

    public boolean canDestroy() {
        return true;
    }
}

Children Nodes

Gemäß der oben genannten Logik werden zu den aktuellen Nodes dazugehörige Children-Nodes hinzugefügt. Diese werden per Drag & Drop verschoben und können zudem kopiert, ausgeschnitten oder gelöscht werden. Im aktuellen Beispiel handelt es sich um „Movies“.

Da sich die Vorgänge wiederholen, wird im Folgenden nicht mehr auf Details eingegangen.

  • Erstellung Klasse „Movie“
  • Erstellung Klasse „MovieChildren“

In der Klasse MovieChildren werden die ChildrenNodes abhängig von der darüberliegenden Node erstellt. Beispielsweise wird „River of No Return“ der Kategorie eins zugewiesen.

 ArrayList childrenNodes = new ArrayList(items.length);
        for (int i = 0; i < items.length; i++) {
            if (category.getName().equals(items[i][1])) {

Die ganze Klasse:

public class MovieChildren extends Index.ArrayChildren {

    private Category category;
    private String[][] items = new String[][]{
        {"0", "Kategorie 1", "River of No Return"},
        {"1", "Kategorie 2", "All About Eve"},
        {"2", "Kategorie 2", "Home Town Story"},
        {"3", "Kategorie 3", "We're Not Married!"},
        {"4", "Kategorie 3", "Love Happy"},
        {"5", "Kategorie 5", "Some Like It Hot"},
        {"6", "Kategorie 4", "Let's Make Love"},
        {"7", "Kategorie 4", "How to Marry a Millionaire"},
        {"8", "Kategorie 4", "Don't Bother to Knock"},
        {"9", "Kategorie 1", "Niagara"},};

    public MovieChildren(Category Category) {
        this.category = Category;
    }

    @Override
    protected java.util.List<Node> initCollection() {
        ArrayList childrenNodes = new ArrayList(items.length);
        for (int i = 0; i < items.length; i++) {
            if (category.getName().equals(items[i][1])) {
                Movie item = new Movie();
                item.setNumber(new Integer(items[i][0]));
                item.setCategory(items[i][1]);
                item.setTitle(items[i][2]);
                childrenNodes.add(new MovieNode(item));
            }
        }
        return childrenNodes;
    }
}

  • Erstellung Klasse „MovieNode“

In den Children-Nodes soll bei Rechtsklick ein MenuItem wie anfangs angesprochen erscheinen. Dies wird nun zusätzlich in der Klasse „MovieNode“ festgelegt.

public class MovieNode extends AbstractNode {

    private Movie movie;

    /**
     * Creates a new instance of InstrumentNode
     */
    public MovieNode(Movie key) {
        super(Children.LEAF, Lookups.fixed(new Object[]{key}));
        this.movie = key;
        setDisplayName(key.getTitle());
        setIconBaseWithExtension("c/walter/NodeTool_2/1394120322_pinterest1.png");
    }

    public boolean canCut() {

        return true;
    }

    public boolean canDestroy() {
        return true;
    }

    public Action[] getActions(boolean popup) {
        return new Action[]{
            SystemAction.get(CopyAction.class),
            SystemAction.get(CutAction.class),
            null,
            SystemAction.get(DeleteAction.class)};
    }
}

Der letzte Schritt legt die Darstellung der zu den Children-Nodes dazugehörigen Icons fest abhängig von deren Zustand (geschlossen oder expandiert).

public class RootNode extends AbstractNode {

    /**
     * Creates a new instance of RootNode
     */
    public RootNode(Children children) {
        super(children);
    }

    public Image getIcon(int type) {
        return Utilities.loadImage("c/walter/...");
    }

    public Image getOpenedIcon(int type) {
        return Utilities.loadImage("c/walter/...");
    }
}

java/netbeans/beantreeview.txt · Zuletzt geändert: 2016/04/21 09:15 (Externe Bearbeitung)