[Resolvido] Drag and Drop

11 respostas
T

Boa noite pessoal. Sou novo aqui no fórum e em programação Java.
Eu tenho que fazer com que botões que estão num JPanel possam ser arrastados e quando o usuário soltá-los num outro JPanel eles devem permanecer lá, mas não tô conseguindo fazer isso. Pra começar o código pra arrastar objetos - que encontrei na web - tá um pouco diferente do que eu tô acostumado. Agradeço se puderem me ajudar. Já li a documentação de classes da Oracle, mas pra mim ainda continua obscuro o trabalho com drag and drop.

Obrigado.

MouseAdapter listener = new MouseAdapter() {
			
			Point p = null;
			 
		      @Override
		      public void mousePressed(MouseEvent e) {
		        p = e.getLocationOnScreen();
		      }
		 
		      @Override
		      public void mouseDragged(MouseEvent e) {
		        JComponent c = (JComponent) e.getSource(); // Não consegui entender essa linha, principalmente. Também não estou acostumado a usar Point e sim as coordenadas x e y
		        Point l = c.getLocation();
		        Point here = e.getLocationOnScreen();
		        c.setLocation(l.x + here.x - p.x, l.y + here.y - p.y);
		        p = here;
		      }
		      
		      public void mouseReleased (MouseEvent e){
		    	  JComponent c = (JComponent) e.getSource();
		    	  if (e.getX() >= 697 && e.getX() <= 656){ // Tentei fazer uma verificação se o objeto fosse solto nas coordenadas do JPanel então seria adicionado a ele, provavelmente eu devesse ter usado Point
		    		  pnlTentativa.add(c);
		    	  }
		      }
		      
		};

11 Respostas

M

eu ja implementei algo assim … vo te passa um exemplo, onde vc pode movimentar uma label em um frame …

public class JFMoveComp extends JFrame {
	private int baseX = -1;
	private int baseY = -1;
	private JLabel label;
	
	public JFMoveComp() {
		initGUI();
	}

	private void initGUI() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(null);
		setSize(500, 500);
		setLocationRelativeTo(null);
		{
			label = new JLabel(" MOVER");
			getContentPane().add(label);
			label.setOpaque(true);
			label.setBackground(Color.blue);
			label.setForeground(Color.white);
			label.setBounds(10, 10, 50, 50);
			label.addMouseMotionListener(new MouseMotionAdapter() {
				public void mouseDragged(MouseEvent e) {
					if ((baseX != -1) && (baseY != -1)) {  
						int x = label.getX() + e.getX() - baseX;  
						int y = label.getY() + e.getY() - baseY;  
						label.setLocation(x, y);  
						label.repaint();  
					}  
				}
			});
			label.addMouseListener(new MouseAdapter() {
				@Override
				public void mousePressed(MouseEvent e) {
					baseX = e.getX();  
					baseY = e.getY();
				}
				@Override
				public void mouseReleased(MouseEvent e) {
					baseX = -1;  
					baseY = -1;
				}
			});
		}
	}
	public static void main(String[] args) {
		new JFMoveComp().setVisible(true);
	}
}
M

é uma boa prática vc fazer uma classe que herda de MouseAdapter e depois só atribuir ela aos seus componentes …
exemplo

public class MoveComponentListener extends MouseAdapter{
	private int baseX = -1;
	private int baseY = -1;
	@Override
	public void mouseDragged(MouseEvent e) {
		if ((baseX != -1) && (baseY != -1)) {  
			int x = e.getComponent().getX() + e.getX() - baseX;  
			int y = e.getComponent().getY() + e.getY() - baseY;  
			e.getComponent().setLocation(x, y);  
			e.getComponent().repaint();  
		}  
	}
	@Override
	public void mousePressed(MouseEvent e) {
		baseX = e.getX();  
		baseY = e.getY();
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		baseX = -1;  
		baseY = -1;
	}
}

e depois apenas adiciona a uma label, por exemplo

MoveComponentListener l = new MoveComponentListener();
label.addMouseListener(l);
label.addMouseMotionListener(l);
T

Vou tentar fazer isso, mas eu preciso adicionar os JButtons em um outro JPanel, ao arrastá-los. É possível fazer isso? Obrigado.

M

é possível sim … mas fica um pouco mais trabalhoso …
o seu componente conhece apenas o seu painel, que ele esta inserido
logo para trocar ele de painel, o listener vai ter que conhecer todos os paineis que ele pode ser transferido …
modifiquei o exemplo, da uma testada …

O JFrame agora com 4 paineis

public class JFMoveComp extends JFrame {
	private JLabel label;
	private JPanel panel1;
	private JPanel panel2;
	private JPanel panel4;
	private JPanel panel3;

	public JFMoveComp() {
		initGUI();
	}

	private void initGUI() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(null);
		this.setSize(714, 500);
		setLocationRelativeTo(null);
		getContentPane().setName("Teste");
		{
			panel1 = new JPanel();
			panel1.setName("Panel1");
			getContentPane().add(panel1);
			panel1.setLayout(null);
			panel1.setBounds(22, 20, 263, 174);
			panel1.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false));
		}
		{
			panel2 = new JPanel();
			panel2.setName("Panel2");
			getContentPane().add(panel2);
			panel2.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false));
			panel2.setLayout(null);
			panel2.setBounds(41, 217, 263, 237);
		}
		{
			panel3 = new JPanel();
			getContentPane().add(panel3);
			panel3.setBorder(new LineBorder(new java.awt.Color(0,0,0),1,false));
			panel3.setLayout(null);
			panel3.setName("panel3");
			panel3.setBounds(393, 43, 263, 174);
		}
		{
			panel4 = new JPanel();
			getContentPane().add(panel4);
			panel4.setBorder(new LineBorder(new java.awt.Color(0,0,0),1,false));
			panel4.setLayout(null);
			panel4.setName("panel4");
			panel4.setBounds(354, 261, 203, 130);
		}
		{
			label = new JLabel(" MOVER");
			panel2.add(label);
			label.setOpaque(true);
			label.setBackground(Color.blue);
			label.setForeground(Color.white);
			label.setBounds(13, 13, 50, 49);
                        //O LISTENER FOI ALTERADO PARA QUE VC PASSE POR PARÂMETRO OS PAINEIS
			MoveComponentListener l = new MoveComponentListener(panel1,panel2, panel3, panel4);
			label.addMouseListener(l);
			label.addMouseMotionListener(l);
		}
	}
	
	public static void main(String[] args) {
		new JFMoveComp().setVisible(true);
	}
}

e o listener, com algumas modificações…

public class MoveComponentListener extends MouseAdapter{
	private int baseX = -1;
	private int baseY = -1;
	private Rectangle2D r2d;
	private JPanel[] panels;

	public MoveComponentListener(JPanel... panels) {
		this.panels = panels; //O LISTENER PRECISA CONHECER OS PAINEIS
		this.r2d = new Rectangle2D.Double(); //RECTANGLE2D QUE REPRESENTA A POSIÇÃO DO COMPONENTE FORA DO SEU PAINEL
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		if ((baseX != -1) && (baseY != -1)) { 
			Component c = e.getComponent();
			Container p = c.getParent(); //O PAINEL QUE O COMPONENTE ESTA

			int x = c.getX() + e.getX() - baseX;  
			int y = c.getY() + e.getY() - baseY;  

			c.setLocation(x, y);  
			c.repaint();  

			//VERIFICA SE O COMPONENTE SAIU FORA DO SEU PAINEL
			if(c.getX() > p.getWidth() || c.getY() > p.getHeight() || c.getX()+c.getWidth()<0 || c.getY()+c.getHeight() < 0)
				mudaPanel(c);
		}  
	}

	@Override
	public void mousePressed(MouseEvent e) {
		baseX = e.getX();  
		baseY = e.getY();
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		baseX = -1;  
		baseY = -1;
		//NÃO DEIXA SOLTAR O COMPONENTE FORA DE UM PAINEL
		Component c = e.getComponent();
		Container p = c.getParent();

		int x = c.getX();
		int y = c.getY();

		if(c.getX()+c.getWidth() > p.getWidth() )
			x = p.getWidth() - c.getWidth();

		if(c.getY()+c.getHeight() > p.getHeight() )
			y = p.getHeight() - c.getHeight();

		if(c.getX() < 0)
			x = 0;

		if(c.getY() < 0)
			y = 0;

		c.setLocation(x, y);
	}

	private void mudaPanel(Component c) {
		Container atual = c.getParent();
		//RECTANGLE2D (POSIÇÃO) DO COMPONENTE FORA DO SEU PAINEL
		r2d.setRect(atual.getX()+c.getX(), atual.getY()+c.getY(), c.getWidth(),c.getHeight());

		for(JPanel novo:panels){ //PERCORRE OS PAINEIS QUE ELE CONHECE
			if(novo!=atual && r2d.intersects(novo.getBounds())){ //VERIFICA SE O COMPONENTE ESTA DENTRO DE OUTRO PAINEL
				atual.remove(c); //REMOVE ELE DO SEU PAINEL ATUAL
				novo.add(c); //E INSERE DENTRO DO NOVO PAINEL
				c.setLocation( //REPOSICIONA O COMPONENTE DENTRO DO SEU NOVO PAINEL
						(int)(r2d.getX()-novo.getX()),
						(int)(r2d.getY()-novo.getY())
						);
				break;
			}
		}
	}
}
T

Vou implementar agora no meu projeto. Espero que funcione. Obrigado pelo tempo gasto e pela ajuda.

T

Douglas, obrigado pela força cara, mas eu tô com uma complicação, tentei usar seu código adaptado dentro do meu projeto com um vetor de botões, mas surgem alguns problemas pra executar o projeto. Vou explicar melhor o que eu pretendo fazer. Eu tô desenvolvendo um jogo, Anagrama, onde a criança arrasta as letras de um panel pra outro, é feita um comparação da letra/botão arrastado com o conteúdo de um vetor String, se for verdadeiro a criança pontua, senão o botão retorna ao panel original. Isso tem me dado um tremendo trabalho, apesar de aparentemente ser simples, não tenho conseguido me concentrar muito bem nisso. Mais uma vez, obrigado.

{
        	for (int i = 0; i &lt; 5; i++){
        		btnLetra[i] = new JButton("A"+i);
        		panel1.add(btnLetra[i]);
        		btnLetra[i].setOpaque(true);  
        		btnLetra[i].setBackground(Color.blue);  
        		btnLetra[i].setForeground(Color.white);  
        		btnLetra[i].setBounds(x, 50, 49, 49);
        		MoveComponentListener l = new MoveComponentListener(panel1,panel2);
        		btnLetra[i].addMouseListener(l);
        		btnLetra[i].addMouseMotionListener(l);
        		x += 55;
        }
M

salve a posição e o painel que o botão esta, no método mousePressed, e faça a sua verificação no mouseReleased … se der falso volte o componente para a sua posição inicial, que foi salva … eu faria assim pelo menos, não deixando ele soltar fora da posição … nunca joguei esse jogo rsrs

T

Nem eu tinha jogado, só soube que o nome era esse agora… Valeu pela força, me ajudo muito mesmo. Mas vou ter que te encher o saco + um pouco. Eu tenho 2 classes no projeto, uma é do jogo e a outra é a do movimento dos objetos. Qual a melhor maneira de usar métodos/POO pra pegar o Container onde o botão tá e também as coordenadas dele e fazer uma verificação nesses métodos? As aulas de Java do meu curso não se focaram muito em conceitos/práticas de POO, a gente ainda continua programando de maneira estrutural. Gostaria de fazer diferente nesse projeto.

A ideia é um jogo como esse aqui http://rachacuca.com.br/palavras/anagramas/

Obrigado, abraço.

public class NivelFacil extends JFrame {
	
	private JLabel lblTeste;
	private JPanel panel1;
	private JPanel panel2;
	private JButton btnLetra;
	private Container CompInicial;
	private int inicioX;
	private int inicioY;
	
	public NivelFacil(){
		initGUI();
	}
	
	private void initGUI(){
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(null);
		setSize(720, 480);
		getContentPane().setName("Teste");
		
		{
			panel1 = new JPanel();
			panel1.setName("Panel1");
			getContentPane().add(panel1);
			panel1.setLayout(null);
			panel1.setBounds(100, 100, 300, 60);
			panel1.setBorder(new LineBorder(new Color(0,0,0)));
		}
		
		{
			panel2 = new JPanel();
			panel2.setName("Panel2");
			getContentPane().add(panel2);
			panel2.setLayout(null);
			panel2.setBounds(100, 300, 300, 60);
			panel2.setBorder(new LineBorder(new Color(0,0,0)));
		}
		
		{
			lblTeste = new JLabel("A");
			panel2.add(lblTeste);
			lblTeste.setOpaque(true);
			lblTeste.setBackground(Color.blue);
			lblTeste.setForeground(Color.white);
			lblTeste.setBounds(10, 10, 49, 49);
			CompInicial = panel2;
			inicioX = 10;
			inicioY = 10;
			MoveComponentListener l = new MoveComponentListener(panel1, panel2);
			lblTeste.addMouseMotionListener(l);
			lblTeste.addMouseListener(l);
		}
	}
	
}
public class MoveComponentListener extends MouseAdapter {
	
	private int baseX = -1;
	private int baseY = -1;
	private Rectangle2D r2d;
	private JPanel[] panels;
		
	public MoveComponentListener(JPanel...panels){
		this.panels = panels;
		this.r2d = new Rectangle2D.Double();		
	}
	
	public void mouseDragged(MouseEvent e){
		if ((baseX != -1) && (baseY != -1)){
			Component c = e.getComponent();
			Container p = c.getParent();
			
			int x = c.getX() + e.getX() - baseX;
			int y = c.getY() + e.getY() - baseY;
			
			c.setLocation(x, y);
			c.repaint();
			
			if (c.getX() > p.getWidth() || c.getY() > p.getHeight() ||
					c.getX() + c.getWidth() < 0 || c.getY() + c.getHeight() < 0)
				mudaPanel(c);
		}
	}
	
	public void mousePressed(MouseEvent e){
		baseX = e.getX();
		baseY = e.getY();
		
		// gostaria de chamar métodos que pegassem a posição, o texto do botão e o panel original
	}
	
	public void mouseReleased(MouseEvent e){
		baseX = -1;
		baseY = -1;
		
		Component c = e.getComponent();
		Container p = c.getParent();
		
		int x = c.getX();
		int y = c.getY();
		
		if (c.getX() + c.getWidth() > p.getWidth())
			x = p.getWidth() - c.getWidth();
		
		if (c.getY() + c.getHeight() > p.getHeight())
			y = p.getHeight() - c.getHeight();
		
		if (c.getX() < 0)
			x = 0;
		
		if (c.getY() < 0)
			x = 0;
		c.setLocation(x, y);
		
	}

	private void mudaPanel(Component c){
		Container atual = c.getParent();
		
		r2d.setRect(atual.getX() + c.getX(), atual.getY() + c.getY(), c.getWidth(), c.getHeight());
		
		for (JPanel novo:panels){
			if (novo != atual && r2d.intersects(novo.getBounds())){
				atual.remove(c);
				novo.add(c);
				c.setLocation((int)(r2d.getX() - novo.getX()),(int)(r2d.getY() - novo.getY()));
				break;
			}
		}

                            // e aqui método(s) onde fosse feita a verificação se o usuário arrastou o botão correto
                            // senão o botão retorna á posição original
	}	
}
M

Olhei o jogo que vc tem como objetivo final, e pra minha surpresa, não tem nada pra arrastar la hehe mas tudo bem …

O atributo painelInicial ficaria na classe que movimenta o componente, e vc poderia referenciar ele no mousePressed

alterei o exemplo mais uma vez, agora ele só deixa trocar de painel se formar a palavra ABC, caso contrário ele volta para o panel
que ele saiu, da uma testada ai …

O JFrame …

public class JFMoveComp extends JFrame {
	private JLabel labelA;
	private JLabel labelB;
	private JLabel labelC;
	private JPanel panel1;
	private JPanel panel2;
	private JPanel panel3;
	private JPanel panel4;

	public JFMoveComp() {
		initGUI();
	}

	private void initGUI() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(null);
		setSize(714, 500);
		setLocationRelativeTo(null);
		getContentPane().setBackground(Color.black);
		{
			panel1 = new JPanel();
			getContentPane().add(panel1);
			panel1.setLayout(null);
			panel1.setBounds(22, 20, 263, 174);
			panel1.setBorder(new LineBorder(Color.black));
		}
		{
			panel2 = new JPanel();
			getContentPane().add(panel2);
			panel2.setBorder(new LineBorder(Color.black));
			panel2.setLayout(null);
			panel2.setBounds(41, 217, 263, 237);
		}
		{
			panel3 = new JPanel();
			getContentPane().add(panel3);
			panel3.setBorder(new LineBorder(Color.black));
			panel3.setLayout(null);
			panel3.setBounds(393, 43, 263, 174);
		}
		{
			panel4 = new JPanel();
			getContentPane().add(panel4);
			panel4.setBorder(new LineBorder(Color.black));
			panel4.setLayout(null);
			panel4.setBounds(354, 261, 203, 130);
		}
		{
			labelA = new JLabel("A");
			panel1.add(labelA);
			labelA.setHorizontalAlignment(JLabel.CENTER);
			labelA.setOpaque(true);
			labelA.setBackground(Color.blue);
			labelA.setForeground(Color.white);
			labelA.setBounds(15, 15, 50, 49);
			MoveComponentListener l = new MoveComponentListener(panel1,panel2, panel3, panel4);
			labelA.addMouseListener(l);
			labelA.addMouseMotionListener(l);
		}
		{
			labelB = new JLabel("B");
			panel2.add(labelB);
			labelB.setHorizontalAlignment(JLabel.CENTER);
			labelB.setOpaque(true);
			labelB.setBackground(Color.red);
			labelB.setForeground(Color.white);
			labelB.setBounds(15, 15, 50, 49);
			MoveComponentListener l = new MoveComponentListener(panel1,panel2, panel3, panel4);
			labelB.addMouseListener(l);
			labelB.addMouseMotionListener(l);
		}
		{
			labelC = new JLabel("C");
			panel3.add(labelC);
			labelC.setHorizontalAlignment(JLabel.CENTER);
			labelC.setOpaque(true);
			labelC.setBackground(Color.green);
			labelC.setForeground(Color.white);
			labelC.setBounds(15, 15, 50, 49);
			MoveComponentListener l = new MoveComponentListener(panel1,panel2, panel3, panel4);
			labelC.addMouseListener(l);
			labelC.addMouseMotionListener(l);
		}
	}
	
	public static void main(String[] args) {
		new JFMoveComp().setVisible(true);
	}
}

e o listener

public class MoveComponentListener extends MouseAdapter{
	private int baseX = -1;
	private int baseY = -1;
	private Rectangle2D r2d;
	private JPanel[] panels;
	private JPanel panelIni; //Painel Inicial(que o componente volta caso esteja no lugar errado)
	private Point posIni; // posição que ele volta

	public MoveComponentListener(JPanel... panels) {
		this.panels = panels; //O LISTENER PRECISA CONHECER OS PAINEIS
		this.r2d = new Rectangle2D.Double(); //RECTANGLE2D QUE REPRESENTA A POSIÇÃO DO COMPONENTE FORA DO SEU PAINEL
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		if ((baseX != -1) && (baseY != -1)) { 
			Component c = e.getComponent();
			Container p = c.getParent(); //O PAINEL QUE O COMPONENTE ESTA

			int x = c.getX() + e.getX() - baseX;  
			int y = c.getY() + e.getY() - baseY;  

			c.setLocation(x, y);  
			c.repaint();  

			//VERIFICA SE O COMPONENTE SAIU FORA DO SEU PAINEL
			if(c.getX() > p.getWidth() || c.getY() > p.getHeight() || c.getX()+c.getWidth()<0 || c.getY()+c.getHeight() < 0)
				mudaPanel(c);
		}  
	}

	@Override
	public void mousePressed(MouseEvent e) {
		baseX = e.getX();  
		baseY = e.getY();
		if(panelIni == null)
			panelIni = (JPanel)e.getComponent().getParent(); //salva o painel inicial
		if(posIni == null)
			posIni = e.getComponent().getLocation(); //salva a posição inicial
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		baseX = -1;  
		baseY = -1;
		//NÃO DEIXA SOLTAR O COMPONENTE FORA DE UM PAINEL
		Component c = e.getComponent();
		Container p = c.getParent();

		int x = c.getX();
		int y = c.getY();

		if(c.getX()+c.getWidth() > p.getWidth() )
			x = p.getWidth() - c.getWidth();

		if(c.getY()+c.getHeight() > p.getHeight() )
			y = p.getHeight() - c.getHeight();

		if(c.getX() < 0)
			x = 0;

		if(c.getY() < 0)
			y = 0;

		c.setLocation(x, y);

		if( p!=panelIni && !verificaPalavra(p) ) { //verifica se não esta movendo dentro do próprio panel dele
			//caso a verificação seja falsa, volta para o panel inicial e para a sua posição
			p.remove(c); 
			panelIni.add(c);
			c.setLocation(posIni);
			p.repaint();
		} else { //senão salva o seu novo painel como sendo o inicial, e tambem para a posição
			panelIni = (JPanel)p; 
			posIni = c.getLocation();
		}
	}

	private boolean verificaPalavra(Container p) {
		String palavra = ""; 
		for(Component c:p.getComponents())//percorre os componentes do painel que ele esta
			palavra += ((JLabel)c).getText(); // forma a palavra com o texto dos componentes
		System.out.println(palavra+"\n"); 
		return (palavra.equals("A")||palavra.equals("AB")||palavra.equals("ABC")); //verifica se é uma palavra válida (boolean)
	}

	private void mudaPanel(Component c) {
		Container atual = c.getParent();
		//RECTANGLE2D (POSIÇÃO) DO COMPONENTE FORA DO SEU PAINEL
		r2d.setRect(atual.getX()+c.getX(), atual.getY()+c.getY(), c.getWidth(),c.getHeight());

		for(JPanel novo:panels){ //PERCORRE OS PAINEIS QUE ELE CONHECE
			if(novo!=atual && r2d.intersects(novo.getBounds())){ //VERIFICA SE O COMPONENTE ESTA DENTRO DE OUTRO PAINEL
				atual.remove(c); //REMOVE ELE DO SEU PAINEL ATUAL
				novo.add(c); //E INSERE DENTRO DO NOVO PAINEL
				c.setLocation( //REPOSICIONA O COMPONENTE DENTRO DO SEU NOVO PAINEL
						(int)(r2d.getX()-novo.getX()),
						(int)(r2d.getY()-novo.getY())
						);
				break;
			}
		}
	}
}

se vc é iniciante em java, e quer aprender o básico sobre orientação a objetos, estude a apostila fj-11 da Caelum, é a melhor que eu conheço …
vc pode baixar gratuitamente aqui:
http://www.caelum.com.br/curso/fj-11-java-orientacao-objetos/

T

Mais uma vez obrigado, Douglas. Esse joguinho tava me deixando meio louco. Eu tinha desenvolvido uma lógica usando clique, nunca tinha usado Drag and Drop no Java (faz + ou - 1 ano que mexo no Java), e a professora do meu curso pediu com DnD, então acabei ficando meio perdido. Como eu em outro post, não tivemos muitas noções/conceitos de POO, começar a aprender agora. :oops:

De qualquer maneira, obrigado.

M

q nada, faz parte… quaquer coisa posta aew
abraço

Criado 6 de março de 2012
Ultima resposta 13 de mar. de 2012
Respostas 11
Participantes 2