问题
I am trying to implement dragging of a J component within a JPanel. The JPanel has to use Gridbag layout manager.
I reviewed many dragging codes, including Moving Windows. They all use component.setLocation(x, y);
which has no effect when using Gridbag layout manager.
I need help with alternative approach.
回答1:
if the JComponent is the only component in the JPanel, the task is not that complicated. Here is a small demo program that does it (with a bonus of re-sizing the component in response to mouse wheel events) :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class GridbagDragDemo extends JPanel {
/**
* Pixel width and height of positioned component.
*/
private float width, height;
/**
* Layout manger for this.
*/
private GridBagLayout gbl;
/**
* Layout horizontal weights for left and right gap, and component.
*/
private float leftWeight,xCompWeight, rightWeight;
/**
* Layout vertical weights for top and right gap, and component.
*/
private float topWeight,yCompWeight, bottomWeight;
/**
* Min and max weight values.
* These values can be changed to change the sensitivity of dragging.
* For better responsiveness W_MAX can be changed in respect to the JPanl's size.
* (also a different W_MAX can be set to horizontal and vertical axis.
*/
private float W_MIN = 0, W_MAX = 2;
/**
* Keep sum an even number for calculating (int) W_SUM/2
*/
private float W_SUM = W_MIN + W_MAX;
/**
* Represents the change in ratio between left / right weights
* and top/bottom weights for every pixel of mouse drag.
* The higher the factor the faster / more sensitive the
* component move is.
* Try different values to get the responsiveness you need.
*/
private float WEIGHT_DELTA = 0.01f;
/**
* Represents the change (in pixels) in component width and height
* and top/bottom weights for every mouse wheel notch.
* The higher the factor the faster / more sensitive the
* component resize.
* Try different values to get the responsiveness you need.
*/
private static final int ZOOM_FACTOR = 4;
/**
* Store mouse pressed position.
*/
private float pX, pY;
/**
* The dragged component
*/
private JComponent component;
public GridbagDragDemo() {
//set initial position to center
leftWeight = W_SUM/2 ; xCompWeight = 0; rightWeight = W_SUM/2;
topWeight = W_SUM/2 ; yCompWeight = 0; bottomWeight = W_SUM/2;
setPreferredSize(new Dimension(400, 300));
gbl = new GridBagLayout();
gbl.columnWidths = new int[] {0, 0, 0};
gbl.rowHeights = new int[] {0, 0, 0};
gbl.columnWeights = new double[]{leftWeight , xCompWeight, rightWeight };
gbl.rowWeights = new double[]{topWeight,yCompWeight, bottomWeight};
setLayout(gbl);
setBackground(Color.YELLOW);
component = new JPanel();
component.setPreferredSize(new Dimension(75,75));
component.setMinimumSize(new Dimension(15,15));
component.setMaximumSize(new Dimension(225,225));
component.setBackground(Color.ORANGE);
component.setBorder(new LineBorder(Color.black, 3));
//add drag listeners
component.addMouseMotionListener(new MouseMotionAdapter(){
@Override
public void mouseDragged(MouseEvent me) {
int mouseX = me.getXOnScreen();
int mouseY = me.getYOnScreen();
float moveX = mouseX - pX;
float moveY = mouseY - pY;
pX = mouseX;
pY = mouseY;
moveComp(moveX , moveY);
}
});
component.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent me) {
//store pressed position
pX = me.getXOnScreen();
pY = me.getYOnScreen();
}
});
//add resize listener
component.addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseWheelMoved(MouseWheelEvent me) {
//change sign so rolling "up" will be positive
reSizeComp(- me.getWheelRotation());
}
});
GridBagConstraints gbc_panel = new GridBagConstraints();
gbc_panel.fill = GridBagConstraints.BOTH;
gbc_panel.gridx = 1;
gbc_panel.gridy = 1;
add(component, gbc_panel);
width = component.getPreferredSize().width;
height = component.getPreferredSize().height;
}
private void moveComp(float moveX, float moveY) {
if(Math.abs(moveX)>0) {
leftWeight += WEIGHT_DELTA * moveX;
leftWeight = (float) setValueInRange(leftWeight, W_MIN, W_MAX);
rightWeight = W_SUM - leftWeight;
}
if(Math.abs(moveY)>0) {
topWeight += WEIGHT_DELTA * moveY;
topWeight = (float) setValueInRange(topWeight, W_MIN, W_MAX );
bottomWeight = W_SUM - topWeight;
}
gbl.columnWeights = new double[]{leftWeight,xCompWeight, rightWeight};
gbl.rowWeights = new double[]{topWeight, yCompWeight, bottomWeight};
refresh();
}
/**
*
*/
private void refresh() {
revalidate();
getParent().repaint();
}
private void reSizeComp(int notches) {
width += notches*ZOOM_FACTOR ; height += notches *ZOOM_FACTOR ;
//respect min / max component size
width = (float) setValueInRange(width, component.getMinimumSize().getWidth(),
component.getMaximumSize().getWidth() );
height = (float) setValueInRange(height, component.getMinimumSize().getHeight(),
component.getMaximumSize().getHeight() );
component.setPreferredSize(new Dimension((int)width,(int)height));
refresh();
}
private double setValueInRange(double value, double min, double max) {
value = (value < min ) ? min : value;
value = (value > max ) ? max : value;
return value;
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Test Gridbag drag");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(400, 300));
frame.add(new GridbagDragDemo());
frame.pack();
frame.setVisible(true);
}
});
}
}
If the JPanel holds multiple components, it becomes a totally different ball game. With multiple components the functionality is similar to what you get using GUI builders like Eclipse' IDE Windowbuilder, where you can interactively move components.
来源:https://stackoverflow.com/questions/35830499/drag-component-within-a-jpanel-using-gridbag-layout-manager