Verschiebbare Elemente in einem rollbaren Container

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen

Vielleicht möchtest du dieses Beispiel als Ausgangspunkt nehmen, um verschiebbare Elemente in einem Rollbaren Container zu implementieren:

package graphic;

/**
 * Model.java
 */
import java.awt.*;
import java.util.*;
import java.util.List;

public class Model {

    private List<Element> elements;
    private Rectangle bounds;
    private Element activeElement;

    public Model() {
        elements = new ArrayList<Element>();
        bounds = new Rectangle();
    }

    public List<Element> getElements() {
        return elements;
    }

    public void setElements(final List<Element> elements) {
        this.elements = elements;
    }

    public Rectangle getBounds() {
        return bounds;
    }

    public void setBounds(Rectangle bounds) {
        this.bounds = bounds;
    }

    public Element getActiveElement() {
        return activeElement;
    }

    public void setActiveElement(final Element activeElement) {
        this.activeElement = activeElement;
    }

    /**
     *
     * @param rect The bounds of any element
     * @return The smallest rectangle enclosing all elements
     */
    protected Rectangle getBounds(final Rectangle rect) {
        bounds = new Rectangle(rect);
        for (Element element : elements) {
            Rectangle elementBounds = new Rectangle(element.getBounds());
            elementBounds.x -= 5;
            elementBounds.y -= 5;
            elementBounds.width += 10;
            elementBounds.height += 10;
            bounds = bounds.union(elementBounds);
        }
        for (Element element : elements) {
            Rectangle elementBounds = element.getBounds();
            elementBounds.translate(-bounds.x, -bounds.y);
        }
        return bounds;
    }

    /**
     *
     * @param hitPoint the location to be analized for intersection with any element
     * @return the list of elements containing hitPoint within their bounds
     */
    protected List<Element> getElementsAt(final Point hitPoint) {
        List<Element> elementsAt = new ArrayList<Element>();
        for (Element element : elements) {
            Rectangle elementBounds = element.getBounds();
            if (elementBounds.contains(hitPoint)) {
                elementsAt.add(element);
            }
        }
        return elementsAt;
    }

    /**
     *
     * @param location the location of the element
     * @return the element added to this Model
     */
    protected Element add(final Point location) {
        Element newElement = new Element();
        Rectangle newBounds;
        newBounds = new Rectangle(location.x, location.y, 0, 0);
        newElement.setBounds(newBounds);
        newElement.getBounds().width = newElement.getMinimumSize().width;
        newElement.getBounds().height = newElement.getMinimumSize().height;
        elements.add(newElement);
        return newElement;
    }


    @Override
    public String toString() {
        return "number of elements: " + elements.size();
    }
}

package graphic;

/**
 * Element.java
 */
import java.awt.*;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class Element {

    private Rectangle bounds;
    private String text;
    private Dimension minimumSize;
    private Color color;
    private JTextArea tTemp;
    private JDialog dTemp;

    public Element() {
        bounds = new Rectangle();
        text = "";
        minimumSize = new Dimension(113, 50);
        color = Color.WHITE;
        tTemp = new JTextArea();
        dTemp = new JDialog();
        dTemp.add(new JScrollPane(tTemp));

    }

    public Rectangle getBounds() {
        return bounds;
    }

    public void setBounds(final Rectangle bounds) {
        this.bounds = bounds;
    }

    public String getText() {
        return text;
    }

    public void setText(final String text) {
        this.text = text;
    }

    public Dimension getMinimumSize() {
        return minimumSize;
    }

    public void setMinimumSize(final Dimension minimumSize) {
        this.minimumSize = minimumSize;
    }

    /**
     *
     * @return the background color of this element
     */
    public Color getColor() {
        return color;
    }

    /**
     *
     * @param color the background color of this element
     */
    public void setColor(final Color color) {
        this.color = color;
    }

    /**
     *
     * resize the element according to its text bounds
     */
    protected void pack() {
        // tTemp and dTemp are used for size calculation only:
        tTemp.setText(this.getText());
        dTemp.pack();
        Rectangle textBounds = new Rectangle(tTemp.getBounds());
        //respecting minimum size:
        if (textBounds.width < this.getMinimumSize().width) {
            textBounds.width = this.getMinimumSize().width;
        }
        if (textBounds.height < this.getMinimumSize().height) {
            textBounds.height = this.getMinimumSize().height;
        }
        //this coordinates:
        textBounds.x = this.getBounds().x;
        textBounds.y = this.getBounds().y;
        this.setBounds(textBounds);

    }

    @Override
    public String toString() {
        return text;
    }
}

package graphic;

/**
 * Graphic.java
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.List;

public class Graphic extends JComponent {

    private Model model;
    private JTextArea renderer;
    private transient Stroke normalStroke,  activeStroke;
    private Controller controller;
    //
    private Dimension preferredSize;

    public Graphic() {
        setFocusable(true);
        setControl(new Controller());
        setModel(new Model());
        controller.setView(this);
        normalStroke = new BasicStroke(1);
        activeStroke = new BasicStroke(3);
        renderer = new JTextArea();
        renderer.setFocusable(false);
        preferredSize = new Dimension();
    }

    public Model getModel() {
        return model;
    }

    /**
     * Use this method to set the model that you loaded from file
     * @param model the model to be used by this graphic
     */
    public void setModel(final Model model) {
        this.model = model;
        controller.setModel(model);
    }

    public Stroke getActiveStroke() {
        return activeStroke;
    }

    public void setActiveStroke(final Stroke activeStroke) {
        this.activeStroke = activeStroke;
    }

    public Stroke getNormalStroke() {
        return normalStroke;
    }

    public void setNormalStroke(final Stroke normalStroke) {
        this.normalStroke = normalStroke;
    }

    /**
     * 
     * @return the controller for user interactions
     */
    public Controller getController() {
        return controller;
    }

    private void setControl(Controller controller) {
        this.controller = controller;
        InputMap map = new ComponentInputMap(this);
        map.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.ALT_MASK), "add");

        ActionMap am = new ActionMap();
        am.put("add", controller.getAction(Controller.ADD));

        setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map);
        setActionMap(am);
    }

    @Override
    public Dimension getPreferredSize() {
        Rectangle bounds = null;
        List<Element> elements = model.getElements();
        if (elements.size() == 0) {
            bounds = new Rectangle();
        } else {
            bounds = model.getBounds(elements.get(0).getBounds());
        }
        preferredSize.width = bounds.width;
        preferredSize.height = bounds.height;
        return preferredSize;
    }

    @Override
    protected void paintComponent(final Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        for (Element element : model.getElements()) {
            g2d.setStroke(normalStroke);
            renderer.setText(element.getText());
            Rectangle bounds = element.getBounds();
            g2d.setColor(element.getColor());
            g2d.fillRect(bounds.x - 3, bounds.y, bounds.width + 6, bounds.height);
            g2d.setColor(Color.BLACK);
            renderer.setBackground(element.getColor());
            SwingUtilities.paintComponent(g2d, renderer, this, bounds);
            if (element == model.getActiveElement()) {
                g2d.setStroke(activeStroke);
            }
            g2d.drawRect(bounds.x - 3, bounds.y, bounds.width + 6, bounds.height);
        }
    }
}

package graphic;

/**
 * Controller.java
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.List;
import java.beans.*;
import java.io.*;

public class Controller extends MouseInputAdapter {

    private WindowListener saveOnClose = (WindowListener) EventHandler.create(
            WindowListener.class, this, "saveModel", null, "windowClosing");
    private DocumentListener documentHandler = (DocumentListener) EventHandler.create(
            DocumentListener.class, this, "processText", null);
    private Action addAction;
    private Model model;
    private Graphic graphic;
    private JTextArea editor;
    //
    private Point hitPoint;
    private String filename = "Graphic.xml";
    private boolean userEditing;
    public final static int ADD = 0;

    public Controller() {
        hitPoint = new Point(20, 20);
        userEditing = true;
        //
        addAction = new AbstractAction("Element") {

            public void actionPerformed(ActionEvent e) {
                addElement();
            }
        };
    }

    /**
     * 
     * @return a WindowListener for saving the model on windowClosing
     */
    public WindowListener getSaveOnClose() {
        return saveOnClose;
    }

    /**
     * Use this method to set actions to buttons and menu items
     * @param action the constant identifying the action
     * @return the action identified by the parameter
     */
    public Action getAction(final int action) {
        switch (action) {
            case ADD:
                return addAction;
            default:
                return null;
        }

    }

    protected void setModel(final Model model) {
        this.model = model;
        Element activeElement = model.getActiveElement();
        if (activeElement != null) {
            editor.setText(activeElement.getText());
        }
    }

    protected void setView(final Graphic graphic) {
        this.graphic = graphic;
        graphic.addMouseListener(this);
        graphic.addMouseMotionListener(this);
    }

    /**
     * This method should be called after the model is set
     * @param editor the JTextArea used for text editing
     */
    public void setEditor(final JTextArea editor) {
        if (model == null) {
            throw new RuntimeException("Model ist not set");
        }
        this.editor = editor;
        editor.getDocument().addDocumentListener(documentHandler);
        Element activeElement = model.getActiveElement();
        if (activeElement != null) {
            editor.setText(activeElement.getText());
        }
    }

    @Override
    public void mouseDragged(final MouseEvent evt) {
        Element activeElement = model.getActiveElement();
        if (activeElement != null) {
            Point dragPoint = evt.getPoint();
            int dx = dragPoint.x - hitPoint.x;
            int dy = dragPoint.y - hitPoint.y;
            activeElement.getBounds().translate(dx, dy);
            hitPoint = dragPoint;
            graphic.repaint();
        }
    }

    @Override
    public void mousePressed(final MouseEvent evt) {
        hitPoint = evt.getPoint();
        List<Element> hitElements = model.getElementsAt(hitPoint);
        Element hitElement = null;
        if (hitElements.size() > 0) {
            hitElement = hitElements.get(0);
        }
        model.setActiveElement(hitElement);
        editText(hitElement);
    }

    @Override
    public void mouseReleased(final MouseEvent evt) {
        graphic.revalidate();
        graphic.repaint();
    }

    private void scrollToVisible(final Element element) {
        if (element != null) {
            Rectangle bounds = new Rectangle(element.getBounds());
            bounds.x -= 10;
            bounds.y -= 10;
            bounds.width += 20;
            bounds.height += 20;
            graphic.scrollRectToVisible(bounds);
        }
    }

    private void addElement() {
        Element added = model.add(hitPoint);
        graphic.revalidate();
        scrollToVisible(added);
        graphic.repaint();
    }

    /**
     * Save the Model to file
     */
    public void saveModel() {
        try {
            XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(filename)));
            encoder.writeObject(model);
            encoder.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Load the Model from file
     * @return the Model loaded
     */
    public Model loadModel() {
        XMLDecoder decoder = null;
        Object result = null;
        try {
            decoder = new XMLDecoder(new BufferedInputStream(new FileInputStream(filename)));
            result = decoder.readObject();
            decoder.close();
        } catch (FileNotFoundException ex) {
        }
        return (Model) result;
    }

    /**
     * Update the active element while the user is typing text into editor area.
     * Called by internal DocumentListener.
     * Generally there is no reason to call this method from client application.
     *
     */
    public void processText() {
        if (userEditing) {
            Element activeElement = model.getActiveElement();
            if (activeElement != null) {
                activeElement.setText(editor.getText());
                activeElement.pack();
                //refresh the view:
                graphic.revalidate();
                scrollToVisible(activeElement);
                graphic.repaint();
            } else {
                JOptionPane.showMessageDialog(graphic, "Please select an element " +
                        "before typing text,\n or else the text will be lost.",
                        "Controller message", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     *
     * @param element the element whose text is to be displayed in the editor area
     */
    private void editText(final Element element) {
        userEditing = false;
        editor.setText(element == null ? "" : element.getText());
        graphic.repaint();
        Runnable action = new Runnable() {

            public void run() {
                userEditing = true;
            }
        };
        SwingUtilities.invokeLater(action);

    }
}

package sample;

/*
 * GraphicDemo.java
 *
 */
import java.awt.*;
import javax.swing.*;
import graphic.*;

public class GraphicDemo extends JFrame {

    private JButton btAdd;
    private JTextArea editor;
    private JScrollPane scrollEditor;
    private JScrollPane scroller;
    private JSplitPane splitPane;
    private JToolBar toolbar;
    private Graphic graphic;
    private Controller controller;

    public GraphicDemo() {
        super("GraphicDemo");
        graphic = new Graphic();
        controller = graphic.getController();
        addWindowListener(controller.getSaveOnClose());
        initComponents();
        scroller.setViewportView(graphic);
        controller.setEditor(editor);
        Model modelLoaded = controller.loadModel();
        if (modelLoaded != null) {
            //set the model loaded from file:
            graphic.setModel(modelLoaded);
        }
        splitPane.setResizeWeight(1);//1 = top component gets all extra space
        Runnable setDividerLocation = new Runnable() {

            public void run() {
                splitPane.setDividerLocation(0.8d);
                editor.requestFocusInWindow();
            }
        };
        SwingUtilities.invokeLater(setDividerLocation);
    }

    private void initComponents() {

        toolbar = new JToolBar();
        btAdd = new JButton();
        splitPane = new JSplitPane();
        scroller = new JScrollPane();
        scrollEditor = new JScrollPane();
        editor = new JTextArea();

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        toolbar.setRollover(true);

        btAdd.setAction(controller.getAction(Controller.ADD));
        btAdd.setText("Element");
        btAdd.setFocusable(false);
        btAdd.setHorizontalTextPosition(SwingConstants.CENTER);
        btAdd.setVerticalTextPosition(SwingConstants.BOTTOM);
        toolbar.add(btAdd);

        getContentPane().add(toolbar, BorderLayout.PAGE_START);

        splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
        splitPane.setTopComponent(scroller);

        scrollEditor.setViewportView(editor);

        splitPane.setBottomComponent(scrollEditor);

        getContentPane().add(splitPane, BorderLayout.CENTER);

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-800)/2, (screenSize.height-600)/2, 800, 600);
    }

    public static void main(final String[] args) {
        Runnable gui = new Runnable() {

            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                new GraphicDemo().setVisible(true);
            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}