Verschiebbare Elemente in einem rollbaren Container
Aus Byte-Welt Wiki
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); } }