Mercenary
Mercenary

Reputation: 2176

Repositioning GridLayout in Swing

I've created a board game with 8x8 GridLayout where in each grid, there is a button which is opaque. I've created to show in the form of a 8x8 table and when user clicks on any cell, it should do some action. Right now, the grid looks like :

enter image description here

I want it to re-position this through animation at an angle to look like this:

enter image description here

Is it possible to do this in Swing using repaint? If not, is there a better toolkit to use other than Swing so that I can animate this?

Upvotes: 1

Views: 394

Answers (3)

MadProgrammer
MadProgrammer

Reputation: 347332

Changing the state of a live component requires more then just painting...

For example, based on Boann's example...

enter image description here

Painting in Swing is a complex and optimised process, which isn't always done in a linear fashion.

Having said that, there is a way, but you are going to need to work for it.

enter image description here

This is based on the JXLayer API and pbjar examples

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;

public class Twister02 {

    public static void main(String[] args) {
        new Twister02();
    }

    public Twister02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new ExamplePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ExamplePane extends JPanel {

        private JSlider slider;
        private FieldPane fieldPane;
        private DefaultTransformModel transformModel;

        public ExamplePane() {

            setLayout(new BorderLayout());

            slider = new JSlider(0, 360);
            slider.setValue(0);
            slider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {

                    transformModel.setRotation(Math.toRadians(slider.getValue()));

                }
            });

            fieldPane = new FieldPane();

            transformModel = new DefaultTransformModel();
            transformModel.setRotation(Math.toRadians(0));
            transformModel.setScaleToPreferredSize(true);
            JXLayer<JComponent> rotatePane = TransformUtils.createTransformJXLayer(fieldPane, transformModel);

            add(slider, BorderLayout.NORTH);
            add(rotatePane);

        }
    }

    public class FieldPane extends JPanel {

        public FieldPane() {
            setLayout(new GridLayout(8, 8));
            for (int i = 0; i < 64; i++) {
                add(new JButton("" + i));
            }
        }

    }
}

Now, the problem is, the pbjar examples seem to have vanished off the face of the internet, to our great disappointment.

However, you can grab a copy from here

Add because it's fun...

enter image description here

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;

public class Twister02 {

    public static void main(String[] args) {
        new Twister02();
    }

    public Twister02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new ExamplePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ExamplePane extends JPanel {

        private JSlider rotateSlider;
        private JSlider shearXSlider;
        private JSlider shearYSlider;
        private FieldPane fieldPane;
        private DefaultTransformModel transformModel;

        public ExamplePane() {

            setLayout(new BorderLayout());

            rotateSlider = new JSlider(0, 180);
            rotateSlider.setValue(0);
            rotateSlider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {

                    transformModel.setRotation(Math.toRadians(rotateSlider.getValue()));

                }
            });
            shearXSlider = new JSlider(-100, 100);
            shearXSlider.setValue(0);
            shearXSlider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {

                    double value = shearXSlider.getValue() / 200d;
                    transformModel.setShearX(value);

                }
            });
            shearYSlider = new JSlider(-100, 100);
            shearYSlider.setValue(0);
            shearYSlider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {

                    double value = shearYSlider.getValue() / 200d;
                    transformModel.setShearY(value);

                }
            });

            fieldPane = new FieldPane();

            transformModel = new DefaultTransformModel();
            transformModel.setRotation(Math.toRadians(0));
            transformModel.setScaleToPreferredSize(true);
            JXLayer<JComponent> rotatePane = TransformUtils.createTransformJXLayer(fieldPane, transformModel);

            JPanel sliders = new JPanel(new GridLayout(3, 0));
            sliders.add(rotateSlider);
            sliders.add(shearXSlider);
            sliders.add(shearYSlider);

            add(sliders, BorderLayout.NORTH);
            add(rotatePane);

        }
    }

    public class FieldPane extends JPanel {

        public FieldPane() {
            setLayout(new GridLayout(8, 8));
            for (int i = 0; i < 64; i++) {
                add(new JButton("" + i));
            }
        }

    }
}

Upvotes: 3

Boann
Boann

Reputation: 50061

Override the container's paintChildren method to apply a transform to the Graphics object, then call super.paintChildren with it. Simple example that rotates a button grid a bit about its center:

JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(8, 8)) {
    @Override
    public void paintChildren(Graphics g1) {
        Graphics2D g = (Graphics2D)g1;
        g.rotate(0.3, getWidth() / 2, getHeight() / 2);
        super.paintChildren(g);
    }
};
frame.add(panel);
for (int i = 0; i < 64; i++) panel.add(new JButton("" + i));

frame.pack();
frame.setVisible(true);

What it looks like (before/after):

enter image description here

Edit: Although it looks clever, you can't actually click these buttons in their rotated positions; it's probably possible to use an AffineTransform object to transform mouse events and redirect them to the correct child correct component, but I haven't tried it. There might be issues with the repaint region too. ... Maybe drawing your own grid (@Masud's answer) would avoid trying to hack this functionality on top of Swing.

Upvotes: 1

Masudul
Masudul

Reputation: 21981

Is it possible to do this in Swing using repaint? If not, is there a better toolkit to use other than Swing so that I can animate this?

No, It is not possible with GridLayout. Use graphics on a JPanel with drawLine and animate it with Timer.

Upvotes: 0

Related Questions