[Resolvido] Drag and Drop

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);
		    	  }
		      }
		      
		};

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);
	}
}

é 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);

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

é 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;
			}
		}
	}
}

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

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(&quot;A&quot;+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;
        }

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

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
	}	
}

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/

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.

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