La idea es hacer un pekeño Paint cutre donde podamos dibujar líneas a nuestro antojo y luego moverlas.
En este tutorial se verá algo de Java2D, eventos y un poco de Swing
Comienzo explicando un poco sobre las clases que vamos a usar, yo he usado 3:
1º PanelDibujo
En esta clase capturaremos los eventos y dibujaremos las líneas.
2º Formas
Es donde guardaremos los atributos de nuestras líneas, tiene este aspecto:
public class Formas {
private Color color;
private Line2D linia;
private Shape boxIni;
private Shape boxFin;
private String key;
public Formas(){}
3º Main
Será la UI del programa donde tendremos un JFrame, una toolbar y el panel de dibujo.
En resumen, tendremos un Main que será la UI del usuario donde habrá un panel de dibujo en el cual dibujaremos Formas.
Panel de dibujo
Necesitaremos estas variables:
public static final int OP_MANO = 0;
public static final int OP_LINEA = 1;
private Color colorBorde;
private HashMap<String, Formas> mapFormas;
private Point puntoActual;
private Point puntoInicial;
private int operacion;
private boolean lineaInicial = true;
private boolean drag = false;
private boolean dragIni = false;
private boolean dragFin = false;
private boolean dragAll = false;
private String dragKey = null;
private int x;
private int y;
private int i;
y el constructor sera:
public PanelDibujo(){
mapFormas = new HashMap<String, Formas>();
puntoActual = new Point();
puntoInicial = new Point();
colorBorde = Color.black;
addMouseListener(this);
addMouseMotionListener(this);
setBackground(Color.WHITE);
setVisible(true);
}
Donde hemos puesto los listeners para que actúen los eventos dentro del panel de dibujo.
Tenemos 2 Operaciones posibles:
- Dibujar línea
- Mover línea
Y esto lo gestionamos con otro evento, cuando arrastramos el ratón:
public void mouseDragged(MouseEvent e) {
switch (operacion)
{
case OP_LINEA:
lineaOp(e);
break;
case OP_MANO:
manoOp(e);
break;
}
}
Cuando la operación de dibujo de línea esta activa y se detecta un evento de arrastre se llama a la función de dibujo de línea (lineaOp(e)).
private void lineaOp(MouseEvent e) {
//Si es la primera línea se crea una nueva forma y se añade con una Key única en el HashMap.
//La función setGraphicalDefaults(e) simplemente pone a 0 todos los puntos
if (lineaInicial){
Formas line = new Formas();
setGraphicalDefaults(e);
line.setLinia(new Line2D.Double(new Point(puntoInicial), new Point(puntoActual)));
line.setColor(colorBorde);
mapFormas.put("line"+i, line);
line.setKey("line"+i);
lineaInicial=false;
}
//Si se detecta movimiento (función mouseHasMoved) se actualizan los atributos de la linea (el punto final)
if (mouseHasMoved(e)){
Formas line = mapFormas.get("line"+i);
puntoActual.setLocation(e.getX(), e.getY());
line.getLinia().setLine(line.getLinia().getP1(), (new Point(puntoActual)));
}
repaint();
}
Una vez creada la línea hacemos un repaint() para actualizar el panel de dibujo en el cual hemos sobrescrito la función paintComponent de esta manera:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g.create();
for (Formas forma : mapFormas.values()) {
if(operacion == OP_LINEA){
g2D.setColor(forma.getColor());
g2D.draw(forma.getLinia());
}else if(operacion == OP_MANO){
g2D.setColor(forma.getColor());
g2D.draw(forma.getLinia());
createBoxes(forma);
g2D.setColor(Color.black);
g2D.draw(forma.getBoxIni());
g2D.draw(forma.getBoxFin());
}
}
g2D.dispose();
}
Donde también se diferencia por tipo de operación, si es la operación de dibujo de líneas solo se dibujarán las líneas recorriendo el hashMap con un foreach.
Si la operación es la de arrastre (OP_MANO) se dibujarán las lineas + unas boxes que definirán los puntos iniciales y finales de cada linea para poderlas agarrar.
Estas boxes se han de actualizar ya que luego se irán moviendo a medida que arrastramos, por eso pongo la función createBoxes dentro del paint.
private void createBoxes(Formas forma) {
int boxSize = 5;
if(forma.getBoxIni()==null || forma.getBoxFin()==null){
Shape boxI = new Rectangle2D.Double(forma.getLinia().getX1(), forma.getLinia().getY1(), boxSize, boxSize);
Shape boxF = new Rectangle2D.Double(forma.getLinia().getX2(), forma.getLinia().getY2(), boxSize, boxSize);
forma.setBoxIni(boxI);
forma.setBoxFin(boxF);
}else{
((Rectangle2D) forma.getBoxIni()).setRect(forma.getLinia().getX1(), forma.getLinia().getY1(), boxSize, boxSize);
((Rectangle2D) forma.getBoxFin()).setRect(forma.getLinia().getX2(), forma.getLinia().getY2(), boxSize, boxSize);
}
}
Algo simple, 2 shapes que se van actualizando dependiendo de la posición de los puntos de las lineas.
Y finalmente la función más “compleja” la operación de arrastre
private void manoOp(MouseEvent e) {
int dx = e.getX() - x;
int dy = e.getY() - y;
for (Formas forma : mapFormas.values()) {
if(forma.getBoxIni().contains(x,y)){
if(!drag){
dragIni = true;
drag = true;
dragKey = forma.getKey();
}
}else if(forma.getBoxFin().contains(x,y)){
if(!drag){
dragFin = true;
drag = true;
dragKey = forma.getKey();
}
}else if(forma.getLinia().intersects(new Rectangle2D.Double(x,y, 8, 8))){
if(!drag){
dragAll = true;
drag = true;
dragKey = forma.getKey();
}
}
if(dragIni && forma.getKey().equals(dragKey)){
int ix = (int) forma.getLinia().getX1();
int iy = (int) forma.getLinia().getY1();
Point2D prevP2 = forma.getLinia().getP2();
ix += dx;
iy += dy;
forma.getLinia().setLine(new Point(ix,iy), prevP2);
}else if(dragFin && forma.getKey().equals(dragKey)){
Point2D prevP1 = forma.getLinia().getP1();
int fx = (int) forma.getLinia().getX2();
int fy = (int) forma.getLinia().getY2();
fx += dx;
fy += dy;
forma.getLinia().setLine(prevP1, new Point(fx,fy));
}else if(dragAll && forma.getKey().equals(dragKey)){
int ix = (int) forma.getLinia().getX1();
int iy = (int) forma.getLinia().getY1();
int fx = (int) forma.getLinia().getX2();
int fy = (int) forma.getLinia().getY2();
ix += dx;
iy += dy;
fx += dx;
fy += dy;
forma.getLinia().setLine(new Point(ix,iy), new Point(fx,fy));
}
}
repaint();
x += dx;
y += dy;
}
Esta función recorre el hashmap de formas y va mirando si el raton está sobre alguna Box de la linea, si está permite actualizar ese punto de la linea. También si intercepta la linea podemos moverla completamente.
El programa final tendrá un aspecto así
Espero que os sirva de utilidad y cualquier duda peguntad.
Adjunto es src porque hay mucha cosa que no se ha explicado:
http://www.megaupload.com/?d=WFLS4QC1