I have a list of N JSliders (N does not change procedurally, only as I add more features. Currently N equals 4). The sum of all the sliders values must equal to 100. As one slider moves the rest of the sliders shall adjust. Each slider has values that range from 0 to 100.
Currently I am using this logic when a slider is changed (pseudo-code):
newValue = currentSlider.getValue
otherSliders = allSliders sans currentSlider
othersValue = summation of otherSliders values
properOthersValue = 100 - newValue
ratio = properOthersValue / othersValue
for slider in otherSlider
slider.value = slider.getValue * ratio
The problem with this setup is slider's values are stored as ints. So as I adjust the sliders I get precision problems: sliders will twitch or not move at all depending on the ratio value. Also the total value does not always add up to 100.
Does anyone have a solution to this problem without creating an entirely new JSlider class that supports floats or doubles?
If you want an example of the behavior I want, visit: Humble Indie Bundle and scroll to the bottom of the page.
thank you
p.s. Multiplying the values by the ratio allows for the user to 'lock' values at 0. However, I am not sure what to do when 3 of the 4 sliders are at 0 and the 4th slider is at 100 and I move the 4th slider down. Using the logic above, the 3 sliders with 0 as their value stay put and the 4th slider moves to where the user puts it, which makes the total less than 100, which is improper behavior.
EDIT
Here is the SSCCE:
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.LinkedList;
public class SliderDemo
{
static LinkedList<JSlider> sliders = new LinkedList<JSlider>();
static class SliderListener implements ChangeListener
{
boolean updating = false;
public void stateChanged(ChangeEvent e)
{
if (updating) return;
updating = true;
JSlider source = (JSlider)e.getSource();
int newValue = source.getValue();
LinkedList<JSlider> otherSliders = new LinkedList<JSlider>(sliders);
otherSliders.remove(source);
int otherValue = 0;
for (JSlider slider : otherSliders)
{
otherValue += slider.getValue();
}
int properValue = 100 - newValue;
double ratio = properValue / (double)otherValue;
for (JSlider slider : otherSliders)
{
int currentValue = slider.getValue();
int updatedValue = (int) (currentValue * ratio);
slider.setValue(updatedValue);
}
int total = 0;
for (JSlider slider : sliders)
{
total += slider.getValue();
}
System.out.println("Total = " + total);
updating = false;
}
}
public static void main(String[] args)
{
JFrame frame = new JFrame("SliderDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = frame.getContentPane();
JPanel sliderPanel = new JPanel(new GridBagLayout());
container.add(sliderPanel);
SliderListener listener = new SliderListener();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
int sliderCount = 4;
int initial = 100 / sliderCount;
for (int i = 0; i < sliderCount; i++)
{
gbc.gridy = i;
JSlider slider = new JSlider(0, 100, initial);
slider.addChangeListener(listener);
slider.setMajorTickSpacing(50);
slider.setPaintTicks(true);
sliders.add(slider);
sliderPanel.add(slider, gbc);
}
frame.pack();
frame.setVisible(true);
}
}
Why not making the granularity of the JSlider models finer by say having them go from 0 to 1000000, and having the sum be 1000000? With the proper Dictionary for the LabelTable, the user will probably not know that it doesn't go from 0 to 100.
For example:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial")
public class LinkedSliders2 extends JPanel {
private static final int SLIDER_COUNT = 5;
public static final int SLIDER_MAX_VALUE = 1000;
private static final int MAJOR_TICK_DIVISIONS = 5;
private static final int MINOR_TICK_DIVISIONS = 20;
private static final int LS_WIDTH = 700;
private static final int LS_HEIGHT = 500;
private JSlider[] sliders = new JSlider[SLIDER_COUNT];
private SliderGroup2 sliderGroup = new SliderGroup2(SLIDER_MAX_VALUE);
public LinkedSliders2() {
Dictionary<Integer, JComponent> myDictionary = new Hashtable<Integer, JComponent>();
for (int i = 0; i <= MAJOR_TICK_DIVISIONS; i++) {
Integer key = i * SLIDER_MAX_VALUE / MAJOR_TICK_DIVISIONS;
JLabel value = new JLabel(String.valueOf(i * 100 / MAJOR_TICK_DIVISIONS));
myDictionary.put(key, value);
}
setLayout(new GridLayout(0, 1));
for (int i = 0; i < sliders.length; i++) {
sliders[i] = new JSlider(0, SLIDER_MAX_VALUE, SLIDER_MAX_VALUE
/ SLIDER_COUNT);
sliders[i].setLabelTable(myDictionary );
sliders[i].setMajorTickSpacing(SLIDER_MAX_VALUE / MAJOR_TICK_DIVISIONS);
sliders[i].setMinorTickSpacing(SLIDER_MAX_VALUE / MINOR_TICK_DIVISIONS);
sliders[i].setPaintLabels(true);
sliders[i].setPaintTicks(true);
sliders[i].setPaintTrack(true);
sliderGroup.addSlider(sliders[i]);
add(sliders[i]);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(LS_WIDTH, LS_HEIGHT);
}
private static void createAndShowGui() {
LinkedSliders2 mainPanel = new LinkedSliders2();
JFrame frame = new JFrame("LinkedSliders");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class SliderGroup2 {
private List<BoundedRangeModel> sliderModelList = new ArrayList<BoundedRangeModel>();
private ChangeListener changeListener = new SliderModelListener();
private int maxValueSum;
public SliderGroup2(int maxValueSum) {
this.maxValueSum = maxValueSum;
}
public void addSlider(JSlider slider) {
BoundedRangeModel model = slider.getModel();
sliderModelList.add(model);
model.addChangeListener(changeListener);
}
private class SliderModelListener implements ChangeListener {
private boolean internalChange = false;
#Override
public void stateChanged(ChangeEvent cEvt) {
if (!internalChange) {
internalChange = true;
BoundedRangeModel sourceModel = (BoundedRangeModel) cEvt.getSource();
int sourceValue = sourceModel.getValue();
int oldSumOfOtherSliders = 0;
for (BoundedRangeModel model : sliderModelList) {
if (model != sourceModel) {
oldSumOfOtherSliders += model.getValue();
}
}
if (oldSumOfOtherSliders == 0) {
for (BoundedRangeModel model : sliderModelList) {
if (model != sourceModel) {
model.setValue(1);
}
}
internalChange = false;
return;
}
int newSumOfOtherSliders = maxValueSum - sourceValue;
for (BoundedRangeModel model : sliderModelList) {
if (model != sourceModel) {
long newValue = ((long) newSumOfOtherSliders * model
.getValue()) / oldSumOfOtherSliders;
model.setValue((int) newValue);
}
}
int total = 0;
for (BoundedRangeModel model : sliderModelList) {
total += model.getValue();
}
//!! System.out.printf("Total = %.0f%n", (double)total * 100 / LinkedSliders2.SLIDER_MAX_VALUE);
internalChange = false;
}
}
}
}
Edited to have SliderGroup2 use a List of BoundedRangeModels rather than JSliders.
sliders will twitch or not move at all depending on the ratio value.
HumbleBundle has the same problem. If you move the slider by the keyboard then the change is only 1, which means it will only ever go to the first slider. So you ratios will eventually get out of sync.
Also the total value does not always add up to 100.
So you need to do a rounding check. If it doesn't add to 100, then you need to decide where the error goes. Maybe the last slider given the above problem?
I am not sure what to do when 3 of the 4 sliders are at 0 and the 4th slider is at 100 and I move the 4th slider down.
The way HumbleBundle handles it is to move all the slicers. However it only allows you to move the slider down increments of 3, so that you can increase each of the 3 sliders by 1.
Even the implementation at HumbleBundle isn't perfect.
Borrowing from some of Hovercrafts solution I came up with a different approach. The basis of this approach is that the "other sliders" values are tracked at the time a slider is moved. As long as you continue to slide the same slider the frozen values are used to calculate the new values. Any rounding differences are then applied sequentially to each slider until the difference is used up. Using this approach you can have incremental changes in the slider applied evenly to all of the other sliders.
The values in the model are the actual values of the slider and you can also use the keyboard to adjust the sliders:
import java.awt.*;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class SliderGroup implements ChangeListener
{
private List<JSlider> sliders = new ArrayList<JSlider>();
private int groupSum;
private boolean internalChange = false;
private JSlider previousSlider;
private List<SliderInfo> otherSliders = new ArrayList<SliderInfo>();
public SliderGroup(int groupSum)
{
this.groupSum = groupSum;
}
public void addSlider(JSlider slider)
{
sliders.add(slider);
slider.addChangeListener(this);
}
#Override
public void stateChanged(ChangeEvent e)
{
if (internalChange) return;
internalChange = true;
JSlider sourceSlider = (JSlider)e.getSource();
if (previousSlider != sourceSlider)
{
setupForSliding(sourceSlider);
previousSlider = sourceSlider;
}
int newSumOfOtherSliders = groupSum - sourceSlider.getValue();
int oldSumOfOtherSliders = 0;
for (SliderInfo info : otherSliders)
{
JSlider slider = info.getSlider();
if (slider != sourceSlider)
{
oldSumOfOtherSliders += info.getValue();
}
}
int difference = newSumOfOtherSliders - oldSumOfOtherSliders;
if (oldSumOfOtherSliders == 0)
{
resetOtherSliders( difference / otherSliders.size() );
allocateDifference(difference % otherSliders.size(), true);
internalChange = false;
return;
}
double ratio = (double)newSumOfOtherSliders / oldSumOfOtherSliders;
for (SliderInfo info : otherSliders)
{
JSlider slider = info.getSlider();
int oldValue = info.getValue();
int newValue = (int)Math.round(oldValue * ratio);
difference += oldValue - newValue;
slider.getModel().setValue( newValue );
}
if (difference != 0)
{
allocateDifference(difference, false);
}
internalChange = false;
}
private void allocateDifference(int difference, boolean adjustZeroValue)
{
while (difference != 0)
{
for (SliderInfo info : otherSliders)
{
if (info.getValue() != 0 || adjustZeroValue)
{
JSlider slider = info.getSlider();
if (difference > 0)
{
slider.getModel().setValue( slider.getValue() + 1 );
difference--;
}
if (difference < 0)
{
slider.getModel().setValue( slider.getValue() - 1 );
difference++;
}
}
}
}
}
private void resetOtherSliders(int resetValue)
{
for (SliderInfo info : otherSliders)
{
JSlider slider = info.getSlider();
slider.getModel().setValue( resetValue );
}
}
private void setupForSliding(JSlider sourceSlider)
{
otherSliders.clear();
for (JSlider slider: sliders)
{
if (slider != sourceSlider)
{
otherSliders.add( new SliderInfo(slider, slider.getValue() ) );
}
}
}
class SliderInfo
{
private JSlider slider;
private int value;
public SliderInfo(JSlider slider, int value)
{
this.slider = slider;
this.value = value;
}
public JSlider getSlider()
{
return slider;
}
public int getValue()
{
return value;
}
}
private static JPanel createSliderPanel(int groupSum, int sliderCount)
{
int sliderValue = groupSum / sliderCount;
SliderGroup sg = new SliderGroup(groupSum);
JPanel panel = new JPanel( new BorderLayout() );
JPanel sliderPanel = new JPanel( new GridLayout(0, 1) );
panel.add(sliderPanel, BorderLayout.CENTER);
JPanel labelPanel = new JPanel( new GridLayout(0, 1) );
panel.add(labelPanel, BorderLayout.EAST);
for (int i = 0; i < sliderCount; i++)
{
JLabel label = new JLabel();
label.setText( Integer.toString(sliderValue) );
labelPanel.add( label );
JSlider slider = new JSlider(0, groupSum, sliderValue);
slider.setMajorTickSpacing(25);
slider.setMinorTickSpacing(5);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setPaintTrack(true);
slider.addChangeListener( new LabelChangeListener(label) );
sliderPanel.add( slider );
sg.addSlider( slider );
}
return panel;
}
static class LabelChangeListener implements ChangeListener
{
private JLabel label;
public LabelChangeListener(JLabel label)
{
this.label = label;
}
#Override
public void stateChanged(ChangeEvent e)
{
JSlider slider = (JSlider)e.getSource();
label.setText( Integer.toString(slider.getValue()) );
}
}
private static void createAndShowGui()
{
JPanel panel = createSliderPanel(100, 5);
JFrame frame = new JFrame("SliderGroup");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Related
I am very new to this community. I have tried to get up to speed on all rules before posting this question (as well as researching solutions). I apologize if I offended or broke any rules through ignorance. Please also excuse my awful code, I am still learning. Thank you for understanding!
EDIT: I have added additional information and tried different approaches to this issue I'm having. I have reworked part of the code below.
I am building a simple football game with a game field panel and control panel. The game field displays all of the player and tackles on the GUI. The control panel sets the difficulty of the game, starts the timer, and the type of quarterback. I ran into a road block where I have all of my code to compile correctly, but when calling set methods on the GameField class to update the score, it updates the variable but not the actual score through my JTextArea Score keeper.
I have instantiated the ControlPanel within the GameField class. I've also tested with a System.out.println() and it shows that it is indeed updating the variable. Is updating JTextArea allowed between classes?
GameField.java
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class GameField extends JPanel implements KeyListener {
ControlPanel cp = new ControlPanel();
// Game pieces
private JButton playerIcon = new JButton("RB");
private JButton tackleIcon1 = new JButton("LB");
private JButton tackleIcon2 = new JButton("LB");
private JButton fieldGoal = new JButton("FG");
// Player and Tackle locations
private int playerPositionX = 100;
private int playerPositionY = 500;
private int tackle1PositionX = 1200;
private int tackle1PositionY = 400;
private int tackle2PositionX = 1200;
private int tackle2PositionY = 600;
// Player variable speeds
private int playerSpeed = 20;
public GameField() {
setLayout(null);
setBackground(Color.green);
add(playerIcon);
playerIcon.setBounds(new Rectangle(getPlayerPositionX(), getPlayerPositionY(), 80, 30));
add(tackleIcon1);
tackleIcon1.setBounds(new Rectangle(getTackle1PositionX(), getTackle1PositionY(), 100, 50));
add(tackleIcon2);
tackleIcon2.setBounds(new Rectangle(getTackle2PositionX(), getTackle2PositionY(), 100, 50));
add(fieldGoal);
fieldGoal.setBounds(new Rectangle(1600, 100, 100, 800));
playerIsTackled();
setFocusable(true);
addKeyListener(this);
}
public void playerIsTackled() {
Rectangle playerRect = playerIcon.getBounds();
Rectangle tackle1Rect = tackleIcon1.getBounds();
Rectangle tackle2Rect = tackleIcon2.getBounds();
if (playerRect.intersects(tackle1Rect) || playerRect.intersects(tackle2Rect)) {
setPlayerPositionX(100);
setPlayerPositionY(500);
setTackle1PositionX(1200);
setTackle1PositionY(400);
setTackle2PositionX(1200);
setTackle2PositionY(600);
playerIcon.setBounds(getPlayerPositionX(), getPlayerPositionY(), 80, 30);
tackleIcon1.setBounds(getTackle1PositionX(), getTackle1PositionY(), 100, 50);
tackleIcon2.setBounds(getTackle2PositionX(), getTackle2PositionY(), 100, 50);
cp.setCurrentTackles(cp.getCurrentTackles() + 1);
System.out.println(cp.getCurrentTackles());
}
}
public void playerScored() {
Rectangle playerRect = playerIcon.getBounds();
Rectangle fieldGoalRect = fieldGoal.getBounds();
if (playerRect.intersects(fieldGoalRect)) {
setPlayerPositionX(100);
setPlayerPositionY(500);
setTackle1PositionX(1200);
setTackle1PositionY(400);
setTackle2PositionX(1200);
setTackle2PositionY(600);
playerIcon.setBounds(getPlayerPositionX(), getPlayerPositionY(), 80, 30);
tackleIcon1.setBounds(getTackle1PositionX(), getTackle1PositionY(), 100, 50);
tackleIcon2.setBounds(getTackle2PositionX(), getTackle2PositionY(), 100, 50);
cp.setCurrentScore(cp.getCurrentScore() + 1);
System.out.println(cp.getCurrentScore());
}
}
public void moveToPlayer() {
if (getTackle1PositionX() > getPlayerPositionX()) {
setTackle1PositionX(getTackle1PositionX() - 1);
} else {
setTackle1PositionX(getTackle1PositionX() + 1);
}
if (getTackle1PositionY() > getPlayerPositionY()) {
setTackle1PositionY(getTackle1PositionY() - 1);
} else {
setTackle1PositionY(getTackle1PositionY() + 1);
}
getTackleIcon1().setBounds(getTackle1PositionX(), getTackle1PositionY(), 100, 50);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
requestFocusInWindow();
playerIsTackled();
playerScored();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
int k = e.getKeyCode();
if (k == e.VK_LEFT && getPlayerPositionX() > 0) {
setPlayerPositionX(getPlayerPositionX() - getPlayerSpeed());
}
if (k == e.VK_RIGHT && getPlayerPositionX() < 1703) {
setPlayerPositionX(getPlayerPositionX() + getPlayerSpeed());
}
if (k == e.VK_UP && getPlayerPositionY() > 0) {
setPlayerPositionY(getPlayerPositionY() - getPlayerSpeed());
}
if (k == e.VK_DOWN && getPlayerPositionY() < 1089) {
setPlayerPositionY(getPlayerPositionY() + getPlayerSpeed());
}
getPlayerIcon().setBounds(getPlayerPositionX(), getPlayerPositionY(), 80, 30);
}
Below is the ControlPanel.java. In the actionPerformed method, you can see I added a getScore().setText() method for updating the score. It correctly updates the score there if I replace the getCurrentScore() method with any integer.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Hashtable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ControlPanel extends JPanel implements ActionListener, ChangeListener {
private JButton start;
private JButton stop;
private JSlider speed;
private JComboBox playerList;
private Timer tim;
private int delay = 1000;
private int i = 0;
private int currentScore = 0;
private int currentTackles = 0;
private JTextArea timer = new JTextArea("Timer: " + 0, 1, 6);
private JTextArea score = new JTextArea("Field Goals: " + currentScore + " Tackles: " + currentTackles, 1, 16);
private String[] playerStyle = {"Slow Runner", "Running Back", "All Star"};
public ControlPanel() {
super();
setBackground(Color.darkGray);
// Game controls
start = new JButton("Start");
stop = new JButton("Stop");
speed = new JSlider(JSlider.HORIZONTAL, 0, 2, 1);
playerList = new JComboBox(getPlayerStyle());
// Slider label
Hashtable labelTable = new Hashtable();
labelTable.put(new Integer(0), new JLabel("Slow"));
labelTable.put(new Integer(1), new JLabel("Normal"));
labelTable.put(new Integer(2), new JLabel("Fast"));
speed.setLabelTable(labelTable);
speed.setPaintLabels(true);
// Combo box dropdown
playerList.setSelectedIndex(1);
// Timer
tim = new Timer(getDelay(), this);
// Add methods
add(start);
add(stop);
add(timer);
add(speed);
add(score);
add(playerList);
// Event listeners
start.addActionListener(this);
stop.addActionListener(this);
speed.addChangeListener(this);
playerList.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox) e.getSource();
String playerChoice = (String) cb.getSelectedItem();
playerList.setSelectedItem(playerChoice);
}
});
// Set focus to false on all game controls
start.setFocusable(false);
stop.setFocusable(false);
speed.setFocusable(false);
playerList.setFocusable(false);
}
#Override
public void actionPerformed(ActionEvent event) {
Object obj = event.getSource();
if (obj == getTim()) {
setI(getI() + 1);
getTimer().setText("Timer: " + getI());
getScore().setText("Field Goals: " + getCurrentScore() + " Tackles: " + getCurrentTackles());
}
if (obj == getStop()) {
getTim().stop();
}
if (obj == getStart()) {
getTim().start();
}
}
#Override
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
int currentSpeed = (int) source.getValue();
if (currentSpeed == 0) {
int delaySpeed = getTim().getDelay();
delaySpeed = (int) 2000;
getTim().setDelay(delaySpeed);
}
if (currentSpeed == 1) {
int delaySpeed = getTim().getDelay();
delaySpeed = (int) 1000;
getTim().setDelay(delaySpeed);
}
if (currentSpeed == 2) {
int delaySpeed = getTim().getDelay();
delaySpeed = (int) 500;
getTim().setDelay(delaySpeed);
}
}
MyJPanel.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyJPanel extends JPanel {
public MyJPanel() {
super();
setBackground(Color.gray);
setLayout(new BorderLayout());
ControlPanel gm = new ControlPanel();
GameField gf = new GameField();
add(gm, "North");
add(gf, "Center");
}
}
Thanks to #D.B. I have gotten my code to run as intended. Because I only intend to have two JPanels (Control Panel and Game Panel), I added the panels to pass as arguments to each other. After this, the code started working as intended. Thank you for pushing me into the right step! It was such a simple mistake. Special thanks to #DaveyDaveDave for motivating me to push harder!
Changes I made to my main JPanel object:
public class MyJPanel extends JPanel {
ControlPanel gm = new ControlPanel();
GameField gf = new GameField();
public MyJPanel() {
super();
setBackground(Color.gray);
setLayout(new BorderLayout());
add(gm, "North");
add(gf, "Center");
gf.setCp(gm);
gm.setGf(gf);
}
}
When i setSize of a jLabel, normally it grows towards bottom. How can i increase the height in positive y direction ?
After pressing init
Current Result
Expected result
My source code
private void initActionPerformed(java.awt.event.ActionEvent evt) {
int p = Integer.parseInt(abc[0].getText());
int q = Integer.parseInt(abc[1].getText());
int r = Integer.parseInt(abc[2].getText());
int s = Integer.parseInt(abc[3].getText());
int t = Integer.parseInt(abc[4].getText());
one.setSize(20, p*10 );
one.setBackground(Color.decode("#03A9F4"));
two.setSize(20, q*10 );
two.setBackground(Color.decode("#03A9F4"));
three.setSize(20, r*10 );
three.setBackground(Color.decode("#03A9F4"));
four.setSize(20, s*10 );
four.setBackground(Color.decode("#03A9F4"));
five.setSize(20, t*10 );
five.setBackground(Color.decode("#03A9F4"));
}
one,two,three are the label names.
abc is an array containing all the labels
You're finding out that Java considers positive y direction graphics to be down, which is in keeping with how computer monitors see y direction. Solutions include:
Figure out a maximum size, and subtract your y height from it to figure out where to start your JLabel.
Even better, don't use JLabels but draw within a JPanel's paintComponent, using the same calculations as above.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial")
public class GraphicsEg extends JPanel {
private static final int DATA_COLUMNS = 5;
private List<JSlider> sliders = new ArrayList<>();
private DrawPanel drawPanel = new DrawPanel(DATA_COLUMNS);
public GraphicsEg() {
JPanel sliderPanel = new JPanel(new GridLayout(1, 0, 5, 5));
SliderListener sliderListener = new SliderListener();
for (int i = 0; i < DATA_COLUMNS; i++) {
JSlider slider = new JSlider(0, 100, 50);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setPaintTrack(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setOrientation(SwingConstants.VERTICAL);
slider.addChangeListener(sliderListener);
sliders.add(slider);
sliderPanel.add(slider);
}
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
setLayout(new BorderLayout(5, 5));
add(drawPanel, BorderLayout.CENTER);
add(sliderPanel, BorderLayout.PAGE_END);
sliderValuesIntoDrawPanel();
}
private void sliderValuesIntoDrawPanel() {
int[] data = new int[DATA_COLUMNS];
for (int i = 0; i < data.length; i++) {
data[i] = sliders.get(i).getValue();
}
drawPanel.setData(data);
}
private class SliderListener implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
sliderValuesIntoDrawPanel();
}
}
private static void createAndShowGui() {
GraphicsEg mainPanel = new GraphicsEg();
JFrame frame = new JFrame("GraphicsEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final int PAD = 20;
private static final Color BORDER_COLOR = Color.BLUE;
private static final Color COLUMN_COLOR = Color.RED;
private static final double RELATIVE_COL_WIDTH = 2.0 / 3.0;
private int dataColumns = 0;
private int[] data;
public DrawPanel(int dataColumns) {
this.dataColumns = dataColumns;
data = new int[dataColumns];
setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
}
public void setData(int[] data) {
this.data = data;
repaint();
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < data.length; i++) {
drawColumn(g, i, data[i]);
}
}
private void drawColumn(Graphics g, int index, int columnHeight) {
g.setColor(COLUMN_COLOR);
int width = (int) (RELATIVE_COL_WIDTH * (PREF_W - 2 * PAD) / dataColumns);
int x = PAD + (index * (PREF_W - 2 * PAD)) / dataColumns;
int height = (columnHeight * (PREF_H - 2 * PAD)) / 100;
int y = PREF_H - PAD - height;
g.fillRect(x, y, width, height);
}
}
I'm trying to code a simple Pong and I have the background panel which contains a Bar panel. So of course I need to be able to place the bar on the size and move it vertically at request. Now I'm just trying to put it in a starting position. If I don't disable the layout the bar gets placed in the top center regardless of location setting, but if I disable the layout and set location it just doesn't show up. I'm not sure what I missing. Here is a code snippet if it can be relevant:
public PongPanel() {
setLayout(null);
setPreferredSize(SIZE);
setBackground(Color.BLACK);
player_one_bar = new Bar();
add(player_one_bar);
player_one_bar.setLocation(10, getSize().height/2-3);
}
If you set the layout manager as null you'll have to specify the exact coordinates of the panel, meaning something like -
setBounds(10, 10, 20, 100);
Will put the panel at location (10,10) with Width of 20 and Height of 100.
If by "bar" you mean Pong game paddle, then it shouldn't be a component at all but rather a logical entity that represents a position, which is visually represented by a sprite that gets drawn in the JPanel's paintComponent method.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class PongPaddle extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 500;
private static final int RECT_X = 20;
private static final int RECT_W = 10;
private static final int RECT_H = 60;
private static final int STARTING_Y = (PREF_H - RECT_H) / 2;
private static final int TIMER_DELAY = 15;
private static final int DELTA_PADDLE = 3;
private boolean paddle1GoingDown = true;
private boolean paddle2GoingDown = false;
private Rectangle paddle1 = new Rectangle(RECT_X, STARTING_Y, RECT_W, RECT_H);
private Rectangle paddle2 = new Rectangle(PREF_W - RECT_X - RECT_W,
STARTING_Y, RECT_W, RECT_H);
public PongPaddle() {
setBackground(Color.black);
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int deltaPaddle1 = paddle1GoingDown ? 1 : -1;
deltaPaddle1 *= DELTA_PADDLE;
int x = paddle1.getLocation().x;
int y = paddle1.getLocation().y + deltaPaddle1;
if (y + RECT_H >= PREF_H) {
paddle1GoingDown = false;
}
if (y <= 0) {
paddle1GoingDown = true;
}
paddle1.setLocation(x, y);
int deltaPaddle2 = paddle2GoingDown ? 1 : -1;
deltaPaddle2 *= DELTA_PADDLE;
x = paddle2.getLocation().x;
y = paddle2.getLocation().y + deltaPaddle2;
if (y + RECT_H >= PREF_H) {
paddle2GoingDown = false;
}
if (y <= 0) {
paddle2GoingDown = true;
}
paddle2.setLocation(x, y);
repaint();
if (!PongPaddle.this.isShowing()) {
((Timer) e.getSource()).stop();
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
if (paddle1 != null) {
g2.fill(paddle1);
}
if (paddle2 != null) {
g2.fill(paddle2);
}
}
private static void createAndShowGui() {
PongPaddle mainPanel = new PongPaddle();
JFrame frame = new JFrame("PongPaddle");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
The program is basically to drag and drop JPanel. I wanted to implement long press to select a JPanel and usage of timer is suggested in few tutorials. Therefore, I tried using timer for 1000ms to select a JPanel, but it works in the preferred way only once but with some flickering of a JPanel which I don't understand why. It fails to recognize mousepressed function later. Another problem is, when clicked on a JPanel, panels start to get removed unintentionally. Actually nothing should happen when a JPanel is clicked as I haven't written anything for Clicked function.
Please provide some suggestions to remove the above mentioned problems
Thanks in advance.
package swappaneleg;
import java.awt.Color;
import java.awt.Component;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.TimerTask;
import javax.swing.*;
public class SwapPanelEg extends JPanel{
private static final long serialVersionUID = 1594039652438249918L;
private static final int PREF_W = 400;
private static final int PREF_H = 400;
private static final int MAX_COLUMN_PANELS = 8;
private JPanel columnPanelsHolder = new JPanel();
public SwapPanelEg(){
columnPanelsHolder.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
for (int i = 0; i < MAX_COLUMN_PANELS; i++) {
int number = i + 1;
int width = 20 + i * 3;
int height = PREF_H - 30;
columnPanelsHolder.add(new ColumnPanel(number, width, height));
}
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
columnPanelsHolder.addMouseListener(myMouseAdapter);
columnPanelsHolder.addMouseMotionListener(myMouseAdapter);
setLayout(new GridBagLayout());
add(columnPanelsHolder);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
private JComponent selectedPanel;
private Point deltaLocation;
private JPanel placeHolder = new JPanel();
private JComponent glassPane;
java.util.Timer t;
#Override
public void mousePressed(final MouseEvent evt) {
if (evt.getButton() != MouseEvent.BUTTON1) {
return;
}
if(t == null) {
t = new java.util.Timer();
}
t.schedule(new TimerTask() {
public void run() {
JPanel source = (JPanel) evt.getSource();
selectedPanel = (JComponent) source.getComponentAt(evt.getPoint());
if (selectedPanel == null) {
return;
}
if (selectedPanel == source) {
selectedPanel = null;
return;
}
glassPane = (JComponent) SwingUtilities.getRootPane(source).getGlassPane();
glassPane.setVisible(true);
Point glassPaneOnScreen = glassPane.getLocationOnScreen();
glassPane.setLayout(null);
Point ptOnScreen = evt.getLocationOnScreen();
Point panelLocOnScreen = selectedPanel.getLocationOnScreen();
int deltaX = ptOnScreen.x + glassPaneOnScreen.x - panelLocOnScreen.x;
int deltaY = ptOnScreen.y + glassPaneOnScreen.y - panelLocOnScreen.y;
deltaLocation = new Point(deltaX, deltaY);
Component[] allComps = source.getComponents();
for (Component component : allComps) {
if (component == selectedPanel) {
placeHolder.setPreferredSize(selectedPanel.getPreferredSize());
source.add(placeHolder);
selectedPanel.setSize(selectedPanel.getPreferredSize());
int x = ptOnScreen.x - deltaLocation.x;
int y = ptOnScreen.y - deltaLocation.y;
selectedPanel.setLocation(x, y);
glassPane.add(selectedPanel);
repaint();
}
else {
source.add(component);
repaint();
}
}
}
},1000,500);
revalidate();
repaint();
}
#Override
public void mouseDragged(MouseEvent evt) {
if (selectedPanel != null) {
Point ptOnScreen = evt.getLocationOnScreen();
int x = ptOnScreen.x - deltaLocation.x;
int y = ptOnScreen.y - deltaLocation.y;
selectedPanel.setLocation(x, y);
selectedPanel.setBorder(BorderFactory.createLineBorder(Color.black));
selectedPanel.setOpaque(false);
repaint();
if(t != null)
{
t.cancel();
t = null;
}
}
}
#Override
public void mouseReleased(MouseEvent evt) {
if (evt.getButton() != MouseEvent.BUTTON1) {
return;
}
if (selectedPanel == null) {
return;
}
JComponent source = (JComponent) evt.getSource();
Component[] allComps = source.getComponents();
JPanel overComponent = (JPanel) source.getComponentAt(evt
.getPoint());
if (overComponent != null && overComponent != placeHolder
&& overComponent != source) {
for (Component component : allComps) {
if (component == overComponent) {
source.add(overComponent);
source.add(selectedPanel);
source.remove(placeHolder);
selectedPanel.setOpaque(true);
selectedPanel.setBorder(BorderFactory.createLineBorder(new Color(0,0,0,0)));
}
else {
source.add(component);
source.remove(placeHolder);
selectedPanel.setOpaque(true);
selectedPanel.setBorder(BorderFactory.createLineBorder(new Color(0,0,0,0)));
}
}
}
else {
for (Component component : allComps) {
if (component == placeHolder) {
source.add(selectedPanel);
source.remove(placeHolder);
}
else {
source.remove(placeHolder);
source.add(component);
selectedPanel.setOpaque(true);
selectedPanel.setBorder(BorderFactory.createLineBorder(new Color(0,0,0,0)));
}
}
}
revalidate();
repaint();
selectedPanel = null;
if(t != null)
{
t.cancel();
t = null;
}
}
}
private static void createAndShowGui() {
SwapPanelEg mainPanel = new SwapPanelEg();
JFrame frame = new JFrame("SwapPanelEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ColumnPanel extends JPanel {
private static final long serialVersionUID = 5366233209639059032L;
private int number;
private int prefWidth;
private int prefHeight;
public ColumnPanel(int number, int prefWidth, int prefHeight) {
setName("ColumnPanel " + number);
this.number = number;
this.prefWidth = prefWidth;
this.prefHeight = prefHeight;
add(new JLabel(String.valueOf(number)));
setBackground(Color.cyan);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefWidth, prefHeight);
}
public int getNumber() {
return number;
}
}
I think a better solution would be:
Create a SwingTimer when a mouse is first pressed.
Have it wait a second and track if the mouse was released or leaves the panel.
If by the time the SwingTimer ends there has been no release and the mouse didn't leave the panel then process with your task.
SwingTimer tutorial: http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
Proposed timer code:
private class InnerTimer implements Runnable {
public boolean expired;
public boolean cancelled;
private long end;
public void run() {
expired = cancelled = false;
end = System.currentTimeMillis() + 1000;
while ( ! cancelled && ! expired) {
if (System.currentTimeMillis() >= end) {
expired = true;
} else {
try { Thread.sleep(100); }
catch(InterruptedException e) {/* no big deal */}
}
}
}
}
Keep an InnerTimer innerTimer = new InnerTimer(); in each panel that needs to listen for long-presses. When the mouse is pressed on such a panel, start this timer in a separate thread: (new Thread(innerTimer)).start(). If the mouse leaves the panel, or is released, cancel the timer: innerTimer.cancelled = true;. If the mouse is moved, check if the timer expired naturally: if (innerTimer.expired); if it did, then the user has successfully long-clicked on this panel. If it did not expire, then just cancel the timer.
Note that this code is somewhat quick&dirty. However, it is short and relatively efficient (except in that it creates threads, but these mostly sleep a lot and are discarded anyway after 1s). Using Swing Timers would avoid the thread-creation overhead, but be considerably more verbose.
I am trying to make a JPanel slide in from the side using this class i made:
public class AnimationClass {
private int i;
private int y;
private JPanel panel;
private int xTo;
private Timer timer;
private int xFrom;
synchronized void slidePanelInFromRight(JPanel panelInput, int xFromInput, int xToInput, int yInput, int width, int height) {
this.panel = panelInput;
this.xFrom = xFromInput;
this.xTo = xToInput;
this.y = yInput;
panel.setSize(width, height);
timer = new Timer(0, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
for (int i = xFrom; i > xTo; i--) {
panel.setLocation(i, y);
panel.repaint();
i--;
timer.stop();
timer.setDelay(100);
if (i >= xTo) {
timer.stop();
}
}
timer.stop();
}
});
timer.start();
}
}
Well, i dont know what the problem is. I've tried a lot of different things, but i doesn't seem like I can get it to work.
The timer should be changing the location on each tick, until it is in place, instead, on each tick, you're running through a for-next loop, which is blocking the EDT until the loop finishes, preventing from updating the UI
Update with example
For example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestAnimatedPane {
public static void main(String[] args) {
new TestAnimatedPane();
}
public TestAnimatedPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel panel;
public TestPane() {
setLayout(null);
panel = new JPanel();
panel.setBackground(Color.RED);
add(panel);
Dimension size = getPreferredSize();
Rectangle from = new Rectangle(size.width, (size.height - 50) / 2, 50, 50);
Rectangle to = new Rectangle((size.width - 50) / 2, (size.height - 50) / 2, 50, 50);
Animate animate = new Animate(panel, from, to);
animate.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class Animate {
public static final int RUN_TIME = 2000;
private JPanel panel;
private Rectangle from;
private Rectangle to;
private long startTime;
public Animate(JPanel panel, Rectangle from, Rectangle to) {
this.panel = panel;
this.from = from;
this.to = to;
}
public void start() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long duration = System.currentTimeMillis() - startTime;
double progress = (double)duration / (double)RUN_TIME;
if (progress > 1f) {
progress = 1f;
((Timer)e.getSource()).stop();
}
Rectangle target = calculateProgress(from, to, progress);
panel.setBounds(target);
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.setInitialDelay(0);
startTime = System.currentTimeMillis();
timer.start();
}
}
public static Rectangle calculateProgress(Rectangle startBounds, Rectangle targetBounds, double progress) {
Rectangle bounds = new Rectangle();
if (startBounds != null && targetBounds != null) {
bounds.setLocation(calculateProgress(startBounds.getLocation(), targetBounds.getLocation(), progress));
bounds.setSize(calculateProgress(startBounds.getSize(), targetBounds.getSize(), progress));
}
return bounds;
}
public static Point calculateProgress(Point startPoint, Point targetPoint, double progress) {
Point point = new Point();
if (startPoint != null && targetPoint != null) {
point.x = calculateProgress(startPoint.x, targetPoint.x, progress);
point.y = calculateProgress(startPoint.y, targetPoint.y, progress);
}
return point;
}
public static int calculateProgress(int startValue, int endValue, double fraction) {
int value = 0;
int distance = endValue - startValue;
value = (int)Math.round((double)distance * fraction);
value += startValue;
return value;
}
public static Dimension calculateProgress(Dimension startSize, Dimension targetSize, double progress) {
Dimension size = new Dimension();
if (startSize != null && targetSize != null) {
size.width = calculateProgress(startSize.width, targetSize.width, progress);
size.height = calculateProgress(startSize.height, targetSize.height, progress);
}
return size;
}
}
Update
I should have added this in last night (1 year who didn't want to go to bed, 2 parents that did, say no more...)
Animation is complex topic, especially when you start looking at variable speed (the example is static).
Instead of reinventing the wheel, you should seriously consider taking a look at...
Timing Framework - This is base animation framework, that makes no assumptions about how you might like to use it.
Trident - Similar to the Timing Framework, but also has support for Swing based components (via reflection) build in
Universal Tween Engine
This well-factored example easily admits the variation below. It leverages the enclosed panel's preferred size in a JLayeredPane.
/**
* #see https://stackoverflow.com/a/16322007/230513
* #see https://stackoverflow.com/a/16316345/230513
*/
public class TestPane extends JLayeredPane {
private static final int WIDE = 200;
private static final int HIGH = 5 * WIDE / 8; // ~1/phi
private JPanel panel;
public TestPane() {
panel = new JPanel();
panel.setBackground(Color.RED);
panel.add(new JButton("Test"));
add(panel);
Dimension size = panel.getPreferredSize();
int half = HIGH / 2 - size.height / 2;
Rectangle from = new Rectangle(size);
from.translate(WIDE, half);
Rectangle to = new Rectangle(size);
to.translate(0, half);
panel.setBounds(from);
Animate animate = new Animate(panel, from, to);
animate.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDE, HIGH);
}
}
There are a number of problems in the OP's code. As MadProrammer points, should only be moving one step per timer tick. Here is a simple,tested correction to the OPs code which moves the JPanel one pixel at a time, 25 times a second. Note the comments:
synchronized void slidePanelInFromRight(JPanel panelInput, int xFromInput, int xToInput, int yInput, int width, int height) {
this.panel = panelInput;
this.xFrom = xFromInput;
this.xTo = xToInput;
this.y = yInput;
panel.setSize(width, height);
// timer runs 25 times per second
timer = new Timer(40, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
// Must 'remember' where we have slid panel to by using instance variable rather than automatic variable
// Only move one step at a time.
// No need to restart timer, it continues to run until stopped
if (xFrom > xTo){
xFrom = xFrom - 1;
panel.setLocation(xFrom, y);
panel.repaint();
} else {
timer.stop();
}
panel.setLocation(xFrom, y);
panel.repaint();
}
});
timer.start();
}
example to slid Anything
package TestingPackage;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class ToggleBtn extends JPanel {
JFrame frame;
JPanel panelOut;
JLabel labelOn;
JLabel labelOff;
JButton btn;
int count = 1;
public ToggleBtn() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(500, 300, 300, 300);
frame.setLayout(null);
panelOut = new JPanel(null);
panelOut.setBounds(50, 100, 120, 30);
panelOut.setBackground(Color.gray);
frame.add(panelOut);
btn = new JButton("::");
btn.setBounds(0, 0, 60, 30);
panelOut.add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
startThread();
}
});
labelOn = new JLabel("ON");
labelOn.setBounds(0, 0, 60, 30);
panelOut.add(labelOn);
labelOff = new JLabel("OFF");
labelOff.setBounds(60, 0, 60, 30);
panelOut.add(labelOff);
frame.setVisible(true);
}
public void startThread() {
count++;
new Move().start();
}
public static void main(String[] args) {
new ToggleBtn();
}
class Move extends Thread {
#Override
public void run() {
if (count % 2 == 0) {
System.out.println("if");
for (int i = 0; i <= 60; i++) {
try {
Thread.sleep(3);
} catch (InterruptedException ex) {
Logger.getLogger(ToggleBtn.class.getName()).log(Level.SEVERE, null, ex);
}
btn.setBounds(i, 0, 60, 30);
}
} else {
System.out.println("else");
for (int i = 60; i >= 0; i--) {
try {
Thread.sleep(3);
} catch (InterruptedException ex) {
Logger.getLogger(ToggleBtn.class.getName()).log(Level.SEVERE, null, ex);
}
btn.setBounds(i, 0, 60, 30);
}
}
}
}
}