Drag component within a JPanel using Gridbag layout manager - java

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.

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.

Related

How to resize JComponent forcefully in a null layout?

I am trying to resize a panel in a container panel where the container panel's layout is set to null.
I am setting the location and changing the size like this:
panelCapture.setLocation(
scale.scale(((capture.getTotalBounds().width + 500) / 2) - (capture.getTotalBounds().width / 2)),
scale.scale(((capture.getTotalBounds().height + 500) / 2) - (capture.getTotalBounds().height / 2))
);
Dimension pcSize = panelCapture.getSize();
Dimension pcNewSize = new Dimension(scale.scale(pcSize.width), scale.scale(pcSize.height));
panelCapture.setSize(pcNewSize);
panelCapture.setPreferredSize(pcNewSize);
The scaling multiplies it by my current scale which is 1.0f, 1.1f, etc.
Changing the position seems to work but changing the size does not.
Instead of using setLocation and setSize, try setBounds(...) or setPreferredSize(...). You may also need a call to repaint() after that. You may want to refer to the tutorial on using absolute/null layout here.
Here's a sample I wrote to change the size of a JPanel inside another JPanel which has a null layout (using only setBounds).
public class Capture
{
JPanel capture;
JPanel panelCapture;
JTextField scaleField;
JButton changeScale;
static final int PARENT_WIDTH = 800;
static final int PARENT_HEIGHT = 600;
static final int CHILD_WIDTH = 100;
static final int CHILD_HEIGHT = 100;
public static void main(String [] args)
{
Capture c = new Capture();
c.doStuff();
}
public void doStuff()
{
capture = new JPanel();
capture.setLayout(null);
capture.setPreferredSize(new Dimension(PARENT_WIDTH, PARENT_HEIGHT));
scaleField = new JTextField();
scaleField.setBounds(100, 550, 200, 25);
capture.add(scaleField);
changeScale = new JButton("Scale");
changeScale.setBounds(325, 550, 100, 25);
changeScale.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
panelCapture.setBounds(getBounds(CHILD_WIDTH * Float.parseFloat(scaleField.getText()), CHILD_HEIGHT * Float.parseFloat(scaleField.getText())));
}
});
capture.add(changeScale);
panelCapture = new JPanel();
panelCapture.setBackground(Color.blue);
panelCapture.setBounds(getBounds(CHILD_WIDTH, CHILD_HEIGHT));
capture.add(panelCapture);
JFrame frame = new JFrame();
frame.setContentPane(capture);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private Rectangle getBounds(float width, float height)
{
int left = (int) (PARENT_WIDTH - width) / 2;
int top = (int) (PARENT_HEIGHT - height) / 2;
return new Rectangle(left, top, (int) width, (int) height);
}
}

How to slowly change object color from one to another?

I am trying to achieve a scenario where the color of an object changes slowly from one color to another color.
I have the initial color as targetColor and final color as updateColor. The changingSpeed variable is set to 5.
The mechanism that I have to use is
Use getRed(), getGreen(), getBlue() to obtain the red, green and blue color
Compute the difference of color of target bytargetColor–color = [ dr dg db]
Normalize [ dr dg db] by dividing the norm of the vector [ dr dg db] T(Beware of div by zero)
Multiply it by changingSpeed to control the speed in changing the color
Update the color to color + [ dr’ dg’ db’ ]
So far, i have been able to make the following code:
dr=targetColor.getRed()-updateColor.getRed();
dg=targetColor.getGreen()-updateColor.getGreen();
db=targetColor.getBlue()-updateColor.getBlue();
double nrml= Math.sqrt((dr*dr)+(dg*dg)+(db*db));
dr=dr/nrml;
dg=dg/nrml;
db=db/nrml;
How to execute the 4th and 5th steps?
Can please anyone specify how to do this via code example?
Also please check if the above code is correct.
Here is an example the fades the background as you move from component to component:
import java.awt.*;
import java.awt.event.*;
import java.util.Hashtable;
import java.util.ArrayList;
import javax.swing.*;
public class Fader
{
// background color when component has focus
private Color fadeColor;
// steps to fade from original background to fade background
private int steps;
// apply transition colors at this time interval
private int interval;
// store transition colors from orginal background to fade background
private Hashtable backgroundColors = new Hashtable();
/*
* Fade from a background color to the specified color using
* the default of 10 steps at a 50 millisecond interval.
*
* #param fadeColor the temporary background color
*/
public Fader(Color fadeColor)
{
this(fadeColor, 10, 50);
}
/*
* Fade from a background color to the specified color in the
* specified number of steps at the default 5 millisecond interval.
*
* #param fadeColor the temporary background color
* #param steps the number of steps to fade in the color
*/
public Fader(Color fadeColor, int steps)
{
this(fadeColor, steps, 50);
}
/*
* Fade from a background color to the specified color in the
* specified number of steps at the specified time interval.
*
* #param fadeColor the temporary background color
* #param steps the number of steps to fade in the color
* #param intevral the interval to apply color fading
*/
public Fader(Color fadeColor, int steps, int interval)
{
this.fadeColor = fadeColor;
this.steps = steps;
this.interval = interval;
}
/*
* Add a component to this fader.
*
* The fade color will be applied when the component gains focus.
* The background color will be restored when the component loses focus.
*
* #param component apply fading to this component
*/
public Fader add(JComponent component)
{
// Get colors to be used for fading
ArrayList colors = getColors( component.getBackground() );
// FaderTimer will apply colors to the component
new FaderTimer( colors, component, interval );
return this;
}
/*
** Get the colors used to fade this background
*/
private ArrayList getColors(Color background)
{
// Check if the color ArrayList already exists
Object o = backgroundColors.get( background );
if (o != null)
{
return (ArrayList)o;
}
// Doesn't exist, create fader colors for this background
ArrayList colors = new ArrayList( steps + 1 );
colors.add( background );
int rDelta = ( background.getRed() - fadeColor.getRed() ) / steps;
int gDelta = ( background.getGreen() - fadeColor.getGreen() ) / steps;
int bDelta = ( background.getBlue() - fadeColor.getBlue() ) / steps;
for (int i = 1; i < steps; i++)
{
int rValue = background.getRed() - (i * rDelta);
int gValue = background.getGreen() - (i * gDelta);
int bValue = background.getBlue() - (i * bDelta);
colors.add( new Color(rValue, gValue, bValue) );
}
colors.add( fadeColor );
backgroundColors.put(background, colors);
return colors;
}
class FaderTimer implements FocusListener, ActionListener
{
private ArrayList colors;
private JComponent component;
private Timer timer;
private int alpha;
private int increment;
FaderTimer(ArrayList colors, JComponent component, int interval)
{
this.colors = colors;
this.component = component;
component.addFocusListener( this );
timer = new Timer(interval, this);
}
public void focusGained(FocusEvent e)
{
alpha = 0;
increment = 1;
timer.start();
}
public void focusLost(FocusEvent e)
{
alpha = steps;
increment = -1;
timer.start();
}
public void actionPerformed(ActionEvent e)
{
alpha += increment;
component.setBackground( (Color)colors.get(alpha) );
if (alpha == steps || alpha == 0)
timer.stop();
}
}
public static void main(String[] args)
{
// Create test components
JComponent textField1 = new JTextField(10);
textField1.setBackground( Color.YELLOW );
JComponent textField3 = new JTextField(10);
JComponent textField4 = new JTextField(10);
JComponent button = new JButton("Start");
JComponent checkBox = new JCheckBox("Check Box");
JFrame frame = new JFrame("Fading Background");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().add(textField1, BorderLayout.NORTH );
frame.getContentPane().add(button, BorderLayout.SOUTH );
frame.getContentPane().add(textField3, BorderLayout.WEST );
frame.getContentPane().add(textField4, BorderLayout.EAST );
frame.getContentPane().add(checkBox);
// Gradual Fading (using defaults)
// Fader fader = new Fader( new Color(155, 255, 155) );
Fader fader = new Fader( new Color(155, 255, 155), 10, 50 );
fader.add( textField1 );
fader.add( textField3 );
fader.add( checkBox );
// Instant Fading
fader = new Fader( new Color(255, 155, 155), 1, 1 );
fader.add( textField4 );
fader.add( button );
frame.pack();
frame.setVisible( true );
}
}
It uses a Timer to update the backgrounds at the specified interval. It then just interpolates between the two color based on the number of steps desired.
Look at this example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test {
public static void main(String args[]) {
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// set some random initial color
final Component comp = frame.getContentPane();
comp.setBackground(new Color(
(float) Math.random(),
(float) Math.random(),
(float) Math.random()));
frame.setVisible(true);
final Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
final Color targetColor = new Color(30,40,50);
final int changingSpeed = 5;
final Color currentColor = comp.getBackground();
// step 1
int r = currentColor.getRed();
int g = currentColor.getGreen();
int b = currentColor.getBlue();
// step 2
double dr = targetColor.getRed() - r;
double dg = targetColor.getGreen() - g;
double db = targetColor.getBlue() - b;
// step 3
double norm = Math.sqrt(dr*dr+dg*dg+db*db);
if (norm < .001) {
((Timer)(evt.getSource())).stop();
return;
}
dr /= norm;
dg /= norm;
db /= norm;
// step 4
dr *= Math.min(changingSpeed, norm);
dg *= Math.min(changingSpeed, norm);
db *= Math.min(changingSpeed, norm);
// step 5
r += dr;
g += dg;
b += db;
comp.setBackground(new Color(r,g,b));
frame.repaint();
}
});
timer.start();
}
}
A few things to note:
Use a timer to fire the updates. This ensures, they are done in the EventThread, what is mandatory for manipulating Swing GUIs.
Test the norm for beeing very small. This means, your delta is near zero and you should stop updating the color.
Use the minimum of the norm and your changing speed. If your changing speed is high enough, your color will alternate around the target color and your delta will swap the sign forever and the process doesn't terminate. So if your delta is smaller than your changing speed, use the delta!
Don't forget to call repaint, when you are done with manipulating the color.
Yea, instead of using the for loop in line
while(i<=10);
you can actually use your changing speed. Meanwhile your changingSpeed should
be an array of length 3 because the different offset of colors interval i.e
dr, dg, db so that they can be taking care of independently.
like this...
int [] changingSpeed(int []Offset){
int loop = 5;
// 5 means the color should be change 5 times
int [] changeSpeed = new int[3];
changeSpeed[0]= offset[0]/loop;
changeSpeed[1]= offset[1]/loop;
changeSpeed[2]= offset[2]/loop;
return changeSpeed;
}
// your update method will now look like this
updateColor(int [] changeSpeed) throws AWTException{
int dr = changeSpeed[0];
int dg = changeSpeed[1];
int db = changeSpeed[2];
Robot slow = new Robot();
int i=0;
int f= loop; // the number of time you want the color to change
while(i<=f){
slow.delay(1000)
//sleep will sleep for 1000ms
setColor(targetColor.getRed() + dr/10, targetColor.getGreen(),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen() + (dg/10),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen(),targetColor.getBlue() + db/10);
i++;
}
}
I wouldnt rely on a specific delay time to compute the next step in an animation as this will certainly provide different execution times in different machines, although it might not be a relevant difference.
Instead of a changing speed factor your could use a long representing the animation total time and use a Thread (or another multithreading mecanism) to control the animation lifecycle (such as calculating how much time has passed since the last repaint and whats the percentage of completion for the next iteration).
I don't know if I followed your math instructions.
I created a Swing GUI so i could watch the transition from one color to another.
The Reset Colors button generates a random starting color and a random finishing color. The Start button transforms the bottom panel from the starting color to the finishing color. Each transformation moves about 5 values in either the red, green, or blue dimension, with a delay of 300 milliseconds.
I tended to watch the numbers change, rather than the color. I wanted to make sure that the transformation was fairly even.
Anyway, here's the code. You can use this to model other Swing GUI's. I put all the classes together in one file so I could paste the code here. You should separate the classes into separate files.
package com.ggl.testing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ColorChangeTest implements Runnable {
private static final Insets normalInsets = new Insets(10, 10, 0, 10);
private Color currentColor;
private Color targetColor;
private ColorPanel changingColorPanel;
private ColorPanel currentColorPanel;
private ColorPanel targetColorPanel;
private JLabel changingLabel;
private JLabel currentLabel;
private JLabel targetLabel;
public static void main(String args[]) {
SwingUtilities.invokeLater(new ColorChangeTest());
}
#Override
public void run() {
JFrame frame = new JFrame("Color Change Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(createColorPanel(), BorderLayout.NORTH);
mainPanel.add(createChangingPanel(), BorderLayout.CENTER);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createColorPanel() {
JPanel colorPanel = new JPanel();
setNewColors();
JPanel currentPanel = new JPanel();
currentPanel.setLayout(new BorderLayout());
JPanel currentLabelPanel = new JPanel();
currentLabelPanel.setLayout(new BorderLayout());
JLabel startLabel = new JLabel("Starting Color");
startLabel.setHorizontalAlignment(JLabel.CENTER);
currentLabelPanel.add(startLabel, BorderLayout.NORTH);
currentLabel = new JLabel(getColorString(currentColor));
currentLabel.setHorizontalAlignment(JLabel.CENTER);
currentLabelPanel.add(currentLabel, BorderLayout.SOUTH);
currentPanel.add(currentLabelPanel, BorderLayout.NORTH);
currentColorPanel = new ColorPanel(100, 100, currentColor);
currentPanel.add(currentColorPanel, BorderLayout.CENTER);
colorPanel.add(currentPanel);
JPanel targetPanel = new JPanel();
targetPanel.setLayout(new BorderLayout());
JPanel targetLabelPanel = new JPanel();
targetLabelPanel.setLayout(new BorderLayout());
JLabel finishLabel = new JLabel("Finishing Color");
finishLabel.setHorizontalAlignment(JLabel.CENTER);
targetLabelPanel.add(finishLabel, BorderLayout.NORTH);
targetLabel = new JLabel(getColorString(targetColor));
targetLabel.setHorizontalAlignment(JLabel.CENTER);
targetLabelPanel.add(targetLabel, BorderLayout.SOUTH);
targetPanel.add(targetLabelPanel, BorderLayout.NORTH);
targetColorPanel = new ColorPanel(100, 100, targetColor);
targetPanel.add(targetColorPanel, BorderLayout.CENTER);
colorPanel.add(targetPanel);
colorPanel.add(createButtonPanel());
return colorPanel;
}
private JPanel createButtonPanel() {
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridBagLayout());
int gridy = 0;
JButton resetButton = new JButton("Reset Colors");
resetButton.addActionListener(new ResetColorsListener(this));
addComponent(buttonPanel, resetButton, 0, gridy++, 1, 1, normalInsets,
GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);
JButton startButton = new JButton("Start");
startButton.addActionListener(new ColorChangeListener(this));
addComponent(buttonPanel, startButton, 0, gridy++, 1, 1, normalInsets,
GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);
return buttonPanel;
}
private JPanel createChangingPanel() {
JPanel changingPanel = new JPanel();
changingPanel.setLayout(new BorderLayout());
changingLabel = new JLabel(getColorString(currentColor));
changingLabel.setHorizontalAlignment(JLabel.CENTER);
changingPanel.add(changingLabel, BorderLayout.NORTH);
changingColorPanel = new ColorPanel(300, 200, currentColor);
changingPanel.add(changingColorPanel, BorderLayout.CENTER);
return changingPanel;
}
public void setChangingColorLabelText(Color color) {
changingLabel.setText(getColorString(color));
}
public void setNewColors() {
currentColor = getRandomColor();
targetColor = getRandomColor();
}
public void displayNewColors() {
currentLabel.setText(getColorString(currentColor));
targetLabel.setText(getColorString(targetColor));
changingLabel.setText(getColorString(currentColor));
currentColorPanel.setColor(currentColor);
targetColorPanel.setColor(targetColor);
changingColorPanel.setColor(currentColor);
}
public Color getCurrentColor() {
return currentColor;
}
public Color getTargetColor() {
return targetColor;
}
public ColorPanel getChangingColorPanel() {
return changingColorPanel;
}
private Color getRandomColor() {
return new Color((float) Math.random(), (float) Math.random(),
(float) Math.random());
}
private String getColorString(Color color) {
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
return "(" + r + ", " + g + ", " + b + ")";
}
private void addComponent(Container container, Component component,
int gridx, int gridy, int gridwidth, int gridheight, Insets insets,
int anchor, int fill) {
GridBagConstraints gbc = new GridBagConstraints(gridx, gridy,
gridwidth, gridheight, 1.0D, 1.0D, anchor, fill, insets, 0, 0);
container.add(component, gbc);
}
public class ColorPanel extends JPanel {
private static final long serialVersionUID = -2894328511698328096L;
private Color color;
public ColorPanel(int width, int height, Color color) {
this.color = color;
this.setPreferredSize(new Dimension(width, height));
}
public void setColor(Color color) {
this.color = color;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class ResetColorsListener implements ActionListener {
private ColorChangeTest colorChangeTest;
public ResetColorsListener(ColorChangeTest colorChangeTest) {
this.colorChangeTest = colorChangeTest;
}
#Override
public void actionPerformed(ActionEvent event) {
colorChangeTest.setNewColors();
colorChangeTest.displayNewColors();
}
}
public class ColorChangeListener implements ActionListener {
private ColorChangeTest colorChangeTest;
public ColorChangeListener(ColorChangeTest colorChangeTest) {
this.colorChangeTest = colorChangeTest;
}
#Override
public void actionPerformed(ActionEvent event) {
ColorChange colorChange = new ColorChange(colorChangeTest);
new Thread(colorChange).start();
}
}
public class ColorChange implements Runnable {
private static final long sleepTime = 300L;
private double r, g, b, dr, dg, db;
private int tr, tg, tb, cr, cg, cb;
private ColorChangeTest colorChangeTest;
public ColorChange(ColorChangeTest colorChangeTest) {
this.colorChangeTest = colorChangeTest;
}
#Override
public void run() {
calculateColorChange();
sleep(sleepTime);
while (calculateNextColor()) {
sleep(sleepTime);
}
setColor(colorChangeTest.getTargetColor());
}
private void calculateColorChange() {
double increment = 5D;
// step 1
r = cr = colorChangeTest.getCurrentColor().getRed();
g = cg = colorChangeTest.getCurrentColor().getGreen();
b = cb = colorChangeTest.getCurrentColor().getBlue();
// step 2
tr = colorChangeTest.getTargetColor().getRed();
tg = colorChangeTest.getTargetColor().getGreen();
tb = colorChangeTest.getTargetColor().getBlue();
dr = tr - cr;
dg = tg - cg;
db = tb - cb;
// step 3
double d = Math.sqrt(dr * dr + dg * dg + db * db);
int steps = (int) (d / increment);
dr /= (double) steps;
dg /= (double) steps;
db /= (double) steps;
setColor(new Color(cr, cg, cb));
}
private boolean calculateNextColor() {
// step 5
r += dr;
g += dg;
b += db;
if (isFinished()) {
return false;
} else {
setColor(new Color(round(r), round(g), round(b)));
return true;
}
}
private boolean isFinished() {
return isColorFinished(cr, tr, round(r))
|| isColorFinished(cg, tg, round(g))
|| isColorFinished(cb, tb, round(b));
}
private int round(double value) {
return (int) Math.round(value);
}
private boolean isColorFinished(int original, int target, int current) {
boolean isFinished = false;
if (current < 0 || current > 255) {
isFinished = true;
} else if ((target >= original) && (current >= target)) {
isFinished = true;
} else if ((target <= original) && (current <= target)) {
isFinished = true;
}
return isFinished;
}
private void setColor(final Color color) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
colorChangeTest.getChangingColorPanel().setColor(color);
colorChangeTest.setChangingColorLabelText(color);
}
});
}
private void sleep(long sleepTime) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
}
}
In order to achieve the act of slowness, it might be a good idea to use the instance of Robot class.
//Method to obtain the offset of the color
static int [] getColorOffset(Color initial, Color final){
int [] colorOffset = new int[3];
colorOffset[0]= final.getRed()-initial.getRed();
colorOffset[1] = final.getGreen()-initial.getGreen();
colorOffset[2]= final.getBlue()-initial.getBlue();
return colorOffset;
}
updateColor(int [] colorOffset) throws AWTException{
int dr = colorOffset[0];
int dg = colorOffset[1];
int db = colorOffset[2];
Robot slow = new Robot();
int i=0;
while(i<=10){
slow.delay(1000)
//sleep will sleep for 1000ms
setColor(targetColor.getRed() + dr/10, targetColor.getGreen(),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen() + (dg/10),targetColor.getBlue());
setColor(targetColor.getRed(), targetColor.getGreen(),targetColor.getBlue() + db/10);
i++;
}
}
public static void main(String args[]) throws AWTException{
Color initial = Color.black;
Color final = Color,white;
int [] colorOffset = getColorOffset(initial, final);
updateColor(colorOffset);
}

How to get the (x,y) coordinates of JButton

Is there a method to get coordinates in the (x,y) format for a Jbutton similar to the pt.distance() method. The jbutton utilizes setLayout(null) and setBounds(x,y,0,0).
How would I compare the results of a pt.distance() and Jbutton(x,y)?
Lastly, how is (x,y) calculated?
Point pt = evt.getPoint();
double u = pt.distance((double)x,(double)y);
double k = st1.getAlignmentX();
double p = st1.getAlignmentY();
if(u > ){ // u has to be measured to (x,y) value of jbutton
tim.setDelay(500);
}
if(u < ){
tim.setDelay(100);
}
If by pt.distance(), you are referring to the Point2D.distance() method then you could proceed like this:
Point location = button.getLocation(); // where button is your JButton object
double distance = pt.distance(location); // where pt is your Point2D object
Or:
double distance = pt.distance(button.getX(), button.getY());
The Point will then contain your button's x and y coordinate. If you are not using layouts, these values will be what you set them to. But if you are using layouts, then the LayoutManager of the parent is responsible for calculating the values.
In response to your edit:
I don't understand what you are trying to do. Calling setLayout(null) on the JButton will not let you set the coordinates of the button, only it's children. I think this is what you are trying to achieve:
Point pt = evt.getPoint();
double distance = pt.distance(button);
int someLength = 100; // the distance away from the button the point has to be to decide the length of the delay
if (distance < someLength) {
tim.setDelay(500);
} else {
tim.setDelay(100);
}
Howabout getLocation, which returns the coordinate of a component on its parent component, or getLocationOnScreen, which returns the coordinate of the component on the display?
Your second question about how x and y are calculated, I'm not sure what you mean by 'calculated'. The coordinates will be relative to something. Usually either the parent component (like a JPanel on which the JButton is sitting) or a location on the screen (such as getLocation would return for a JFrame).
A method like Point.distance will subtract the two coordinates' x and y values and tell you the difference. This is just basic geometry.
For example, here is a method that will return the distance of a point from the center of a JButton:
public static double getDistance(Point point, JComponent comp) {
Point loc = comp.getLocation();
loc.x += comp.getWidth() / 2;
loc.y += comp.getHeight() / 2;
double xdif = Math.abs(loc.x - point.x);
double ydif = Math.abs(loc.y - point.y);
return Math.sqrt((xdif * xdif) + (ydif * ydif));
}
This returns the hypotenuse of a triangle as a measurement in pixels, which means if the point you give it (like cursor coordinates) is at a diagonal it will give you the useful distance.
Point.distance will do something like this.
I've noticed this old answer of mine has gotten quite a few views, so here is a better way to do the above (but doesn't really show the math):
public static double distance(Point p, JComponent comp) {
Point2D.Float center =
// note: use (0, 0) instead of (getX(), getY())
// if the Point 'p' is in the coordinates of 'comp'
// instead of the parent of 'comp'
new Point2D.Float(comp.getX(), comp.getY());
center.x += comp.getWidth() / 2f;
center.y += comp.getHeight() / 2f;
return center.distance(p);
}
Here's a simple example showing this kind of geometry in a Swing program:
This draws a line to wherever the mouse cursor is and displays the length of the line (which is the distance from the center of the JPanel to the cursor).
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
class DistanceExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DistanceExample());
}
#Override
public void run() {
JLabel distanceLabel = new JLabel("--");
MousePanel clickPanel = new MousePanel();
Listener listener =
new Listener(distanceLabel, clickPanel);
clickPanel.addMouseListener(listener);
clickPanel.addMouseMotionListener(listener);
JPanel content = new JPanel(new BorderLayout());
content.setBackground(Color.white);
content.add(distanceLabel, BorderLayout.NORTH);
content.add(clickPanel, BorderLayout.CENTER);
JFrame frame = new JFrame();
frame.setContentPane(content);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
static class MousePanel extends JPanel {
Point2D.Float mousePos;
MousePanel() {
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (mousePos != null) {
g.setColor(Color.red);
Point2D.Float center = centerOf(this);
g.drawLine(Math.round(center.x),
Math.round(center.y),
Math.round(mousePos.x),
Math.round(mousePos.y));
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
}
static class Listener extends MouseAdapter {
JLabel distanceLabel;
MousePanel mousePanel;
Listener(JLabel distanceLabel, MousePanel mousePanel) {
this.distanceLabel = distanceLabel;
this.mousePanel = mousePanel;
}
#Override
public void mouseMoved(MouseEvent e) {
Point2D.Float mousePos =
new Point2D.Float(e.getX(), e.getY());
mousePanel.mousePos = mousePos;
mousePanel.repaint();
double dist = distance(mousePos, mousePanel);
distanceLabel.setText(String.format("%.2f", dist));
}
#Override
public void mouseExited(MouseEvent e) {
mousePanel.mousePos = null;
mousePanel.repaint();
distanceLabel.setText("--");
}
}
static Point2D.Float centerOf(JComponent comp) {
Point2D.Float center =
new Point2D.Float((comp.getWidth() / 2f),
(comp.getHeight() / 2f));
return center;
}
static double distance(Point2D p, JComponent comp) {
return centerOf(comp).distance(p);
}
}
Something like this?:
JButton b = new JButton();
b.getAlignmentX();
b.getAlignmentY();
You can also use this:
Rectangle r = b.getBounds(); // The bounds specify this component's width, height,
// and location relative to its parent.

java swing resize ImageIcon according to jLabel

i am a new programmer here and i have something to ask, i have browse a picture into my GUI (and set the path in text box also)which displays on a Label, but the label dimension is set only 100,100 while the picture is much bigger so when i open/display it into the label it get cropped , is there anyway to make it auto resize to the label size? below is my logic code on browse button and open dialog box
please any one tell me where i am wrong..
public class ImagePath extends javax.swing.JFrame {
private JPanel contentPane;
JLabel jLabel1;
String s2;
File targetFile;
BufferedImage targetImg;
public ImagePath() {
initComponents();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser jFileChooser1 = new JFileChooser();
int state = jFileChooser1.showOpenDialog(new JFrame());
jTextField1.setText("");
if (state == JFileChooser.APPROVE_OPTION) {
JOptionPane.showMessageDialog(new JFrame(), "hii");
File file = jFileChooser1.getSelectedFile();
s2 = file.toString();
jTextField1.setText(s2);
jLabel1 = new JLabel();
jLabel1.setName(s2);
jLabel1.setLocation(50, 50);
jLabel1.setSize(300, 300);
add(jLabel1);
BufferedImage bi1;
try {
bi1 = ImageIO.read(file);
ImageIcon icon1 = new ImageIcon(bi1);
jLabel1.setIcon(icon1);
Image img = icon1.getImage();
ImageIcon icon = new ImageIcon(file.getPath());
Image scaleImage = icon.getImage().getScaledInstance(28, 28, Image.SCALE_DEFAULT);
repaint();
pack();
} catch (Exception e) {
System.out.println(e);
}
} else if (state == JFileChooser.CANCEL_OPTION) {
JOptionPane.showMessageDialog(new JFrame(), "Canceled");
}
}
}
Unless you really want a nasty head ache, I would suggest taking advantage of the layout management system.
Instead of trying to set the size and location of components, let them decide how they wan to be displayed where possible.
While I personally take advantage of Netbeans form designer, I would encourage you to take the time to learn how to build your UI's by hand, it will give a greater apprication for what the form designers can do, as well ideas you can employee to make advanced UIs - IMHO
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class SimpleImageBrowser {
public static void main(String[] args) {
new SimpleImageBrowser();
}
public SimpleImageBrowser() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SimpleImageBrowser.ImageBrowserPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ImageBrowserPane extends JPanel {
private JFileChooser fcImage = new JFileChooser();
private SimpleImageBrowser.ImagePane imagePane;
public ImageBrowserPane() {
setLayout(new BorderLayout());
imagePane = new SimpleImageBrowser.ImagePane();
add(new JScrollPane(imagePane));
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int state = fcImage.showOpenDialog(SimpleImageBrowser.ImageBrowserPane.this);
switch (state) {
case JFileChooser.APPROVE_OPTION:
File file = fcImage.getSelectedFile();
try {
BufferedImage bi1 = ImageIO.read(file);
ImageIcon icon1 = new ImageIcon(bi1);
JLabel label = new JLabel(icon1);
label.setText(file.getPath());
label.setHorizontalTextPosition(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.BOTTOM);
label.setForeground(Color.WHITE);
label.setBorder(new LineBorder(Color.WHITE));
imagePane.add(label);
imagePane.revalidate();
} catch (Exception exp) {
exp.printStackTrace();
}
}
}
});
JPanel buttons = new JPanel();
buttons.add(add);
add(buttons, BorderLayout.NORTH);
}
}
public class ImagePane extends JPanel {
public ImagePane() {
setLayout(new SimpleImageBrowser.WrapLayout());
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return getComponentCount() == 0 ? new Dimension(200, 200) : super.getPreferredSize();
}
}
/**
* FlowLayout subclass that fully supports wrapping of components.
*/
public class WrapLayout extends FlowLayout {
private Dimension preferredLayoutSize;
/**
* Constructs a new
* <code>WrapLayout</code> with a left alignment and a default 5-unit
* horizontal and vertical gap.
*/
public WrapLayout() {
super();
}
/**
* Constructs a new
* <code>FlowLayout</code> with the specified alignment and a default 5-unit
* horizontal and vertical gap. The value of the alignment argument must be
* one of
* <code>WrapLayout</code>,
* <code>WrapLayout</code>, or
* <code>WrapLayout</code>.
*
* #param align the alignment value
*/
public WrapLayout(int align) {
super(align);
}
/**
* Creates a new flow layout manager with the indicated alignment and the
* indicated horizontal and vertical gaps.
* <p>
* The value of the alignment argument must be one of
* <code>WrapLayout</code>,
* <code>WrapLayout</code>, or
* <code>WrapLayout</code>.
*
* #param align the alignment value
* #param hgap the horizontal gap between components
* #param vgap the vertical gap between components
*/
public WrapLayout(int align, int hgap, int vgap) {
super(align, hgap, vgap);
}
/**
* Returns the preferred dimensions for this layout given the
* <i>visible</i> components in the specified target container.
*
* #param target the component which needs to be laid out
* #return the preferred dimensions to lay out the subcomponents of the
* specified container
*/
#Override
public Dimension preferredLayoutSize(Container target) {
return layoutSize(target, true);
}
/**
* Returns the minimum dimensions needed to layout the <i>visible</i>
* components contained in the specified target container.
*
* #param target the component which needs to be laid out
* #return the minimum dimensions to lay out the subcomponents of the
* specified container
*/
#Override
public Dimension minimumLayoutSize(Container target) {
Dimension minimum = layoutSize(target, false);
minimum.width -= (getHgap() + 1);
return minimum;
}
/**
* Returns the minimum or preferred dimension needed to layout the target
* container.
*
* #param target target to get layout size for
* #param preferred should preferred size be calculated
* #return the dimension to layout the target container
*/
private Dimension layoutSize(Container target, boolean preferred) {
synchronized (target.getTreeLock()) {
// Each row must fit with the width allocated to the containter.
// When the container width = 0, the preferred width of the container
// has not yet been calculated so lets ask for the maximum.
int targetWidth = target.getSize().width;
if (targetWidth == 0) {
targetWidth = Integer.MAX_VALUE;
}
int hgap = getHgap();
int vgap = getVgap();
Insets insets = target.getInsets();
int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
int maxWidth = targetWidth - horizontalInsetsAndGap;
// Fit components into the allowed width
Dimension dim = new Dimension(0, 0);
int rowWidth = 0;
int rowHeight = 0;
int nmembers = target.getComponentCount();
for (int i = 0; i < nmembers; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
// Can't add the component to current row. Start a new row.
if (rowWidth + d.width > maxWidth) {
addRow(dim, rowWidth, rowHeight);
rowWidth = 0;
rowHeight = 0;
}
// Add a horizontal gap for all components after the first
if (rowWidth != 0) {
rowWidth += hgap;
}
rowWidth += d.width;
rowHeight = Math.max(rowHeight, d.height);
}
}
addRow(dim, rowWidth, rowHeight);
dim.width += horizontalInsetsAndGap;
dim.height += insets.top + insets.bottom + vgap * 2;
// When using a scroll pane or the DecoratedLookAndFeel we need to
// make sure the preferred size is less than the size of the
// target containter so shrinking the container size works
// correctly. Removing the horizontal gap is an easy way to do this.
Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
if (scrollPane != null && target.isValid()) {
dim.width -= (hgap + 1);
}
return dim;
}
}
/*
* A new row has been completed. Use the dimensions of this row
* to update the preferred size for the container.
*
* #param dim update the width and height when appropriate
* #param rowWidth the width of the row to add
* #param rowHeight the height of the row to add
*/
private void addRow(Dimension dim, int rowWidth, int rowHeight) {
dim.width = Math.max(dim.width, rowWidth);
if (dim.height > 0) {
dim.height += getVgap();
}
dim.height += rowHeight;
}
}
}
I've included WrapLayout by Rob Camick (who lurks about the place) because, to be quite frank, none of the other layout managers gave me the effect I wanted.
I also set the label's text with the path of the image
Take a look at Using layout Managers and A Visual Guide to Layout Managers
Take a look at WrapLayout
Updated with text fields
If you want to associate a text field with the image, then I suggest you add both the label and text field to a JPanel (using something like a BorderLayout) and then add that to your image pane...
public class ImagePane extends JPanel {
public ImagePane() {
setLayout(new SimpleImageBrowser.WrapLayout());
setBackground(Color.BLACK);
}
public void addImage(File file) throws IOException {
BufferedImage bi1 = ImageIO.read(file);
ImageIcon icon1 = new ImageIcon(bi1);
JPanel imgPane = new JPanel(new BorderLayout());
imgPane.setOpaque(false);
JLabel label = new JLabel(icon1);
imgPane.add(label);
JTextField field = new JTextField(file.getPath(), 20);
field.setEditable(false);
imgPane.add(field, BorderLayout.SOUTH);
add(imgPane);
revalidate();
}
#Override
public Dimension getPreferredSize() {
return getComponentCount() == 0 ? new Dimension(200, 200) : super.getPreferredSize();
}
}
And the updated ActionListener...
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int state = fcImage.showOpenDialog(SimpleImageBrowser.ImageBrowserPane.this);
switch (state) {
case JFileChooser.APPROVE_OPTION:
File file = fcImage.getSelectedFile();
try {
imagePane.addImage(file);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
});
You should cut out the call to label.setSize. You should also place all your components in in the form designer instead of producing them when you click the button. Maybe you could also add a layout manager in the bargain? This will really involve a revolt against NetBeans.

problems revalidating/repainting button panel

I've been googling for hours trying a million different subtle changes all to no avail, so I figure my best bet is to pick the brains of people more skilled than I.
I'm writing a class that loads a bunch of buttons from a database and the goal is to offer the user the ability to arrange the buttons to their liking, however, for some reason, I can't seem to get the frame to revalidate or repaint. The buttons will move around, but they will not rearrange as I have coded them to. The organization seems to be working correctly, ie the code involved in making the re arrangement when the mouse button is released, its just that the components will only stay where they are dragged and dropped, even though they are re ordered in their respective List.
The code is long, I didn't want to have to post the whole class as it may turn some people off but I don't know where I'm making the mistake so I think it would be in my best interest to post the whole thing. The main area of concern is mouseReleased(MouseEvent e) {...} and the repaint()/refresh() method, however, there could be something that I'm missing elsewhere.
tl;dr:
I'm basically just trying to perform a setBounds() after the user drags and drops the buttons in the order they want but the buttons stay in the same spot they are dragged and dropped, and won't revalidate() or repaint(). I can't even removeAll to clear the panel of components and reload.
Thank you in advanced. Here's my code:
public class AdminButtonEditor extends javax.swing.JFrame {
public AdminButtonEditor(OrderView parent) { ...
...
Component[] components = buttonsPanel.getComponents();
for (int i = 0; i < components.length; i++) {
Component c = components[i];
if (c instanceof JButton) {
JButton jb = (JButton) c;
jb.setFocusable(false);
buttons.add(new MyJButton(...));
}
}
for (int i = 0; i < buttons.size(); i++) {
buttons.get(i).addTo(editPanel);
buttons.get(i).orderIndex=modButtonList.get(i).menuModifier.getViewOrderValue();
buttons.get(i).idx=i;
}
EventHandler eh = new EventHandler();
addWindowListener(eh);
editPanel.addMouseMotionListener(eh);
editPanel.addMouseListener(eh);
contentPane.add(editPanel, BorderLayout.CENTER);
}
protected void refresh() {
if (!buttons.isEmpty() && buttons.get(0) != null) {
contentPane.remove(editPanel);
editPanel.removeAll();
for (int i = 0; i < buttons.size(); i++) {
MyJButton s = buttons.get(i);
s.addTo(editPanel);
}
contentPane.add(editPanel, BorderLayout.CENTER);
editPanel.repaint();
}
}
public void paint(Graphics g) {
refresh();
super.paint(g);
}
private int getSelectionIndex(int x, int y) {
int s=-1;
for (int i=buttons.size()-1; i>=0;i--){
if (buttons.get(i).contains(x, y)) {
s = i;
break;
}
}
return s;
}
private class EventHandler implements MouseInputListener,WindowListener, ActionListener {
private int selectionIndex, startX, startY, lastX, lastY;
private MyJButton selected;
private boolean moving=false;
....
....
public void mouseReleased(MouseEvent e) {
if (moving){
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
moving=false;
int dropX=e.getX();
int dropY=e.getY();
int row = dropY/selected.height;
int col = dropX/selected.width;
int idx=(row*3+col)-1;
int oldIdx=buttons.indexOf(selected);
insertIntoList(idx,oldIdx);
}
if (selected!=null){
selected.unHighLight();
selected=null;
}
Collections.sort(buttons);
for (int i=0;i<buttons.size();i++){
//modButtonList.get(buttons.get(i).idx).menuModifier.setViewOrderValue(buttons.get(i).orderIndex);
}
editPanel.validate();
repaint();
}
private void insertIntoList(int idx, int oldIdx) {
MyJButton temp = buttons.get(idx);
int tempid=buttons.get(idx).idx;
buttons.set(idx, new MyJButton(selected.text,selected.x,selected.y,selected.width,selected.height,selected.idx));
buttons.get(idx).orderIndex=temp.orderIndex;
if (idx<oldIdx){
int id;
for (int i=oldIdx;i>idx+1;i--){
id=buttons.get(i).orderIndex;
buttons.set(i, new MyJButton(buttons.get(i-1).text,buttons.get(i-1).x,buttons.get(i-1).y,buttons.get(i-1).width,buttons.get(i-1).height,buttons.get(i-1).idx));
buttons.get(i).orderIndex=id;
}
id = buttons.get(idx+1).orderIndex;
buttons.set(idx+1,new MyJButton(temp.text,temp.x,temp.y,temp.width,temp.height,temp.idx));
buttons.get(idx+1).orderIndex=id;
} else if (idx>oldIdx) {
int id;
for (int i=oldIdx;i<idx-1;i++){
id=buttons.get(i).orderIndex;
buttons.set(i, new MyJButton(buttons.get(i+1).text,buttons.get(i+1).x,buttons.get(i+1).y,buttons.get(i+1).width,buttons.get(i+1).height,buttons.get(i+1).idx));
buttons.get(i).orderIndex=id;
}
id = buttons.get(idx-1).orderIndex;
buttons.set(idx-1,new MyJButton(temp.text,temp.x,temp.y,temp.width,temp.height,temp.idx));
buttons.get(idx-1).orderIndex=id;;
} else {
buttons.get(idx).x=buttons.get(idx).originx;
buttons.get(idx).y=buttons.get(idx).originy;
}
repaint();
}
public void mouseDragged(MouseEvent e) {
if (moving) {
Graphics g = editPanel.getGraphics();
g.setColor(Color.black);
g.drawLine(selected.getXPos(), 0, selected.getXPos(),editPanel.getWidth());
g.drawLine(0, selected.getYPos(), editPanel.getHeight(), selected.getYPos());
selected.moveBy(e.getX()-lastX, e.getY()-lastY);
g.setXORMode(Color.black);
g.drawLine(selected.getXPos(), 0, selected.getXPos(), editPanel.getWidth());
g.drawLine(0, selected.getYPos(), editPanel.getHeight(), selected.getYPos());
lastX=e.getX();
lastY=e.getY();
repaint();
}
}
....
}
private class MyJButton extends JButton implements Comparable {
private int orderIndex,idx;
private int x, y, width, height,originx,originy;
private String text;
public Border DEFAULT_BORDER;// new SoftBevelBorder(BevelBorder.RAISED);
public Border SELECT_BORDER = BorderFactory.createLineBorder(Color.RED, 3, true);
public MyJButton(String text, int x, int y, int width, int height) {
....
setFocusable(false);
}
public MyJButton(String text, int x, int y, int width, int height,int idx) {....}
public void addTo(JPanel p) {
setBounds(x, y, width, height);
p.add(this);
}
#Override
public boolean contains(int x, int y) {
int x1 = x, y1 = y;
if (x1 >= this.x && y1 >= this.y && x1 <= this.x + width && y1 <= this.y + height) {
return true;
} else {
return false;
}
}
#Override
public void setSize(int w, int h) {
width = w;
height = h;
}
....
public void moveBy(int dx, int dy) {
x += dx;
y += dy;
}
#Override
public void resize(int newWidth, int newHeight) {
this.width = newWidth;
this.height = newHeight;
setBounds(x, y, width, height);
}
public int compareTo(Object o) {
MyJButton mjb = (MyJButton)o;
return this.idx-mjb.idx;
}
}
}
+1 to GagandeepBalis comment.
Okay so I found this very cool and decided to look into it more.
I came up with some logic which will be needed for it to work, maybe not the best but...:
1) We need to make our JButtons draggable ( thank you #camickr and his DragLayout) :)
2) When a JButtons is dragged and than dropped i.e on mouseReleased(..) we should check if a our dragged buttons collide with any others
3) we check whether a JButton collides with another by getting the JButton image and counting how many opaque pixels of the JButton, we are dragging, are covering another button.
4) sort the number of collisions and find the highest, this will be used so we can see where to insert the JButton we dragged. i.e it will be inserted by component with the most collisions.
5) sort the ArrayList which holds the buttons to match the changes
6) remove all buttons and re-add them using the Array (and thus they will be re-ordered).
Here is an example (Most code takes place in overridden ComponentMover mouseReleased(..) method):
Before dragging anything:
after dragging button 4 over button 1 and letting go of mouse button:
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DragButtons {
ArrayList<JButton> buttons = new ArrayList<>();
public DragButtons() {
initComponents();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new DragButtons();
}
});
}
private void initComponents() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new GridLayout(2, 2));
ComponentMover cm = new ComponentMover() {
#Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
HashMap<Integer, JButton> collisions = new HashMap<>();
JButton draggedButton = (JButton) e.getSource();
for (JButton btn : buttons) {//iterate through all buttons and get the number of collsions of each
if (btn != draggedButton) {//dont chck button we were dragging
int col = checkPerPixelCollision(draggedButton, btn);
System.out.println("Button " + btn.getText());
System.out.println(col);
collisions.put(col, btn);
}
}
//lets get the button which had most collisions
int maxCollisions = 0;
JButton intersectingButton = null;
for (Map.Entry<Integer, JButton> entry : collisions.entrySet()) {
Integer collisionCount = entry.getKey();
JButton button = entry.getValue();
if (collisionCount > maxCollisions) {
maxCollisions = collisionCount;
intersectingButton = button;
}
}
boolean reLayout = false;
if (maxCollisions > 0) {//check if there was any
System.out.println("Button " + draggedButton.getText() + " is intersecting more of Button " + intersectingButton.getText());
System.out.println("Collisions: " + maxCollisions);
reLayout = true;
} else {
System.out.println("No change made");
reLayout = false;
}
ArrayList<JButton> tmpButtons = (ArrayList<JButton>) buttons.clone();//create clone of buttons
if (reLayout) {//a button as moved and panel needs to be layed out
buttons.clear();//clear old buttons
for (JButton b : tmpButtons) {//re-order jbuttons
if (b == intersectingButton) {
buttons.add(draggedButton);
} else if (b == draggedButton) {
buttons.add(intersectingButton);
} else {
buttons.add(b);
}
}
panel.removeAll();//remove all buttons
for (JButton btn : buttons) {//iterate through all buttons and get the number of collsions of each
panel.add(btn);//re-add buttons according to arraylist
}
panel.revalidate();
panel.repaint();
//re-order the Array of buttons to fit
//remove all button and re add them using sorted array
}
}
};
for (int i = 0; i < 4; i++) {
JButton b = new JButton(String.valueOf(i + 1));
buttons.add(b);
panel.add(b);
cm.registerComponent(b);
}
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public HashSet<String> getMask(JButton e) {
HashSet<String> mask = new HashSet<>();
int pixel, a;
BufferedImage bi = null;
try {
bi = componentToImage(e, e.getBounds()); //gets the current image being shown
} catch (IOException ex) {
Logger.getLogger(DragButtons.class.getName()).log(Level.SEVERE, null, ex);
}
for (int i = 0; i < bi.getWidth(); i++) { // for every (x,y) component in the given box,
for (int j = 0; j < bi.getHeight(); j++) {
pixel = bi.getRGB(i, j); // get the RGB value of the pixel
a = (pixel >> 24) & 0xff;
if (a != 0) { // if the alpha is not 0, it must be something other than transparent
mask.add((e.getX() + i) + "," + (e.getY() - j)); // add the absolute x and absolute y coordinates to our set
}
}
}
return mask; //return our set
}
public static BufferedImage componentToImage(Component component, Rectangle region) throws IOException {
BufferedImage img = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
Graphics g = img.getGraphics();
g.setColor(component.getForeground());
g.setFont(component.getFont());
component.paintAll(g);
ImageIO.write(img, "png", new File("c:/saved.png"));
return img;
}
// Returns true if there is a collision between object a and object b
public int checkPerPixelCollision(JButton b, JButton b2) {
// This method detects to see if the images overlap at all. If they do, collision is possible
int ax1 = (int) b2.getX();
int ay1 = (int) b2.getY();
int ax2 = ax1 + (int) b2.getWidth();
int ay2 = ay1 + (int) b2.getHeight();
int bx1 = (int) b.getX();
int by1 = (int) b.getY();
int bx2 = bx1 + (int) b.getWidth();
int by2 = by1 + (int) b.getHeight();
if (by2 < ay1 || ay2 < by1 || bx2 < ax1 || ax2 < bx1) {
return 0; // Collision is impossible.
} else { // Collision is possible.
// get the masks for both images
HashSet<String> maskPlayer1 = getMask(b2);
HashSet<String> maskPlayer2 = getMask(b);
maskPlayer1.retainAll(maskPlayer2); // Check to see if any pixels in maskPlayer2 are the same as those in maskPlayer1
if (maskPlayer1.size() > 0) { // if so, than there exists at least one pixel that is the same in both images, thus
return maskPlayer1.size();
}
}
return 0;
}
}
class ComponentMover extends MouseAdapter {
private Insets dragInsets = new Insets(0, 0, 0, 0);
private Dimension snapSize = new Dimension(1, 1);
private Insets edgeInsets = new Insets(0, 0, 0, 0);
private boolean changeCursor = true;
private boolean autoLayout = false;
private Class destinationClass;
private Component destinationComponent;
private Component destination;
private Component source;
private Point pressed;
private Point location;
private Cursor originalCursor;
private boolean autoscrolls;
private boolean potentialDrag;
/**
* Constructor for moving individual components. The components must be
* regisetered using the registerComponent() method.
*/
public ComponentMover() {
}
/**
* Constructor to specify a Class of Component that will be moved when drag
* events are generated on a registered child component. The events will be
* passed to the first ancestor of this specified class.
*
* #param destinationClass the Class of the ancestor component
* #param component the Components to be registered for forwarding drag
* events to the ancestor Component.
*/
public ComponentMover(Class destinationClass, Component... components) {
this.destinationClass = destinationClass;
registerComponent(components);
}
/**
* Constructor to specify a parent component that will be moved when drag
* events are generated on a registered child component.
*
* #param destinationComponent the component drage events should be
* forwareded to
* #param components the Components to be registered for forwarding drag
* events to the parent component to be moved
*/
public ComponentMover(Component destinationComponent, Component... components) {
this.destinationComponent = destinationComponent;
registerComponent(components);
}
/**
* Get the auto layout property
*
* #return the auto layout property
*/
public boolean isAutoLayout() {
return autoLayout;
}
/**
* Set the auto layout property
*
* #param autoLayout when true layout will be invoked on the parent
* container
*/
public void setAutoLayout(boolean autoLayout) {
this.autoLayout = autoLayout;
}
/**
* Get the change cursor property
*
* #return the change cursor property
*/
public boolean isChangeCursor() {
return changeCursor;
}
/**
* Set the change cursor property
*
* #param changeCursor when true the cursor will be changed to the
* Cursor.MOVE_CURSOR while the mouse is pressed
*/
public void setChangeCursor(boolean changeCursor) {
this.changeCursor = changeCursor;
}
/**
* Get the drag insets
*
* #return the drag insets
*/
public Insets getDragInsets() {
return dragInsets;
}
/**
* Set the drag insets. The insets specify an area where mouseDragged events
* should be ignored and therefore the component will not be moved. This
* will prevent these events from being confused with a MouseMotionListener
* that supports component resizing.
*
* #param dragInsets
*/
public void setDragInsets(Insets dragInsets) {
this.dragInsets = dragInsets;
}
/**
* Get the bounds insets
*
* #return the bounds insets
*/
public Insets getEdgeInsets() {
return edgeInsets;
}
/**
* Set the edge insets. The insets specify how close to each edge of the
* parent component that the child component can be moved. Positive values
* means the component must be contained within the parent. Negative values
* means the component can be moved outside the parent.
*
* #param edgeInsets
*/
public void setEdgeInsets(Insets edgeInsets) {
this.edgeInsets = edgeInsets;
}
/**
* Remove listeners from the specified component
*
* #param component the component the listeners are removed from
*/
public void deregisterComponent(Component... components) {
for (Component component : components) {
component.removeMouseListener(this);
}
}
/**
* Add the required listeners to the specified component
*
* #param component the component the listeners are added to
*/
public void registerComponent(Component... components) {
for (Component component : components) {
component.addMouseListener(this);
}
}
/**
* Get the snap size
*
* #return the snap size
*/
public Dimension getSnapSize() {
return snapSize;
}
/**
* Set the snap size. Forces the component to be snapped to the closest grid
* position. Snapping will occur when the mouse is dragged half way.
*/
public void setSnapSize(Dimension snapSize) {
if (snapSize.width < 1
|| snapSize.height < 1) {
throw new IllegalArgumentException("Snap sizes must be greater than 0");
}
this.snapSize = snapSize;
}
/**
* Setup the variables used to control the moving of the component:
*
* source - the source component of the mouse event destination - the
* component that will ultimately be moved pressed - the Point where the
* mouse was pressed in the destination component coordinates.
*/
#Override
public void mousePressed(MouseEvent e) {
source = e.getComponent();
int width = source.getSize().width - dragInsets.left - dragInsets.right;
int height = source.getSize().height - dragInsets.top - dragInsets.bottom;
Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width, height);
if (r.contains(e.getPoint())) {
setupForDragging(e);
}
}
private void setupForDragging(MouseEvent e) {
source.addMouseMotionListener(this);
potentialDrag = true;
// Determine the component that will ultimately be moved
if (destinationComponent != null) {
destination = destinationComponent;
} else if (destinationClass == null) {
destination = source;
} else // forward events to destination component
{
destination = SwingUtilities.getAncestorOfClass(destinationClass, source);
}
pressed = e.getLocationOnScreen();
location = destination.getLocation();
if (changeCursor) {
originalCursor = source.getCursor();
source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
// Making sure autoscrolls is false will allow for smoother dragging of
// individual components
if (destination instanceof JComponent) {
JComponent jc = (JComponent) destination;
autoscrolls = jc.getAutoscrolls();
jc.setAutoscrolls(false);
}
}
/**
* Move the component to its new location. The dragged Point must be in the
* destination coordinates.
*/
#Override
public void mouseDragged(MouseEvent e) {
Point dragged = e.getLocationOnScreen();
int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width);
int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height);
int locationX = location.x + dragX;
int locationY = location.y + dragY;
// Mouse dragged events are not generated for every pixel the mouse
// is moved. Adjust the location to make sure we are still on a
// snap value.
while (locationX < edgeInsets.left) {
locationX += snapSize.width;
}
while (locationY < edgeInsets.top) {
locationY += snapSize.height;
}
Dimension d = getBoundingSize(destination);
while (locationX + destination.getSize().width + edgeInsets.right > d.width) {
locationX -= snapSize.width;
}
while (locationY + destination.getSize().height + edgeInsets.bottom > d.height) {
locationY -= snapSize.height;
}
// Adjustments are finished, move the component
destination.setLocation(locationX, locationY);
}
/*
* Determine how far the mouse has moved from where dragging started
* (Assume drag direction is down and right for positive drag distance)
*/
private int getDragDistance(int larger, int smaller, int snapSize) {
int halfway = snapSize / 2;
int drag = larger - smaller;
drag += (drag < 0) ? -halfway : halfway;
drag = (drag / snapSize) * snapSize;
return drag;
}
/*
* Get the bounds of the parent of the dragged component.
*/
private Dimension getBoundingSize(Component source) {
if (source instanceof Window) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
Rectangle bounds = env.getMaximumWindowBounds();
return new Dimension(bounds.width, bounds.height);
} else {
return source.getParent().getSize();
}
}
/**
* Restore the original state of the Component
*/
#Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
if (!potentialDrag) {
return;
}
source.removeMouseMotionListener(this);
potentialDrag = false;
if (changeCursor) {
source.setCursor(originalCursor);
}
if (destination instanceof JComponent) {
((JComponent) destination).setAutoscrolls(autoscrolls);
}
// Layout the components on the parent container
if (autoLayout) {
if (destination instanceof JComponent) {
((JComponent) destination).revalidate();
} else {
destination.revalidate();
}
destination.repaint();
}
}
}

Categories