两年前,我编写了一个名为“LayoutAnimator”的工具,可以在任意布局之间执行过渡效果。也许你会觉得它很有用:
import java.awt.Component;
import java.awt.Container;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import javax.swing.Timer;
class LayoutAnimator
{
private static final Map<Container, LayoutAnimation> running =
new IdentityHashMap<Container, LayoutAnimation>();
public static synchronized void execute(
Container container, LayoutManager newLayout, double durationS)
{
LayoutAnimation runningLayoutAnimtion = running.get(container);
if (runningLayoutAnimtion != null)
{
runningLayoutAnimtion.cancel();
running.remove(container);
}
final LayoutAnimation layoutAnimtion =
new LayoutAnimation(container, newLayout);
running.put(container, layoutAnimtion);
layoutAnimtion.execute(durationS, new LayoutAnimationCallback()
{
@Override
public void animationFinished()
{
running.remove(layoutAnimtion);
}
});
}
private static interface LayoutAnimationCallback
{
void animationFinished();
}
private static class LayoutAnimation
{
private final Container container;
private final LayoutManager newLayout;
private final Timer timer;
private final int delayMS = 20;
LayoutAnimation(Container container, LayoutManager newLayout)
{
this.container = container;
this.newLayout = newLayout;
this.timer = new Timer(delayMS, null);
}
void execute(final double durationS,
final LayoutAnimationCallback layoutAnimatorCallback)
{
final Map<Component, Rectangle> oldBounds =
getAllBounds(container.getComponents());
container.setLayout(newLayout);
newLayout.layoutContainer(container);
final Map<Component, Rectangle> newBounds =
getAllBounds(container.getComponents());
container.setLayout(null);
setAllBounds(container.getComponents(), oldBounds);
final Map<Component, Rectangle> currentBounds =
getAllBounds(container.getComponents());
timer.addActionListener(new ActionListener()
{
double alpha = 0;
double alphaStep = 1.0 / (durationS * (1000.0 / delayMS));
@Override
public void actionPerformed(ActionEvent e)
{
if (alpha == 1.0)
{
timer.stop();
container.setLayout(newLayout);
layoutAnimatorCallback.animationFinished();
}
alpha += alphaStep;
alpha = Math.min(1.0, alpha);
interpolate(oldBounds, newBounds, currentBounds, alpha);
setAllBounds(container.getComponents(), currentBounds);
}
});
timer.setCoalesce(true);
timer.start();
}
void cancel()
{
timer.stop();
}
}
private static Map<Component, Rectangle> getAllBounds(
Component components[])
{
Map<Component, Rectangle> currentBounds =
new HashMap<Component, Rectangle>();
for (Component component : components)
{
Rectangle bounds = component.getBounds();
currentBounds.put(component, bounds);
}
return currentBounds;
}
private static void setAllBounds(
Component components[], Map<Component, Rectangle> newBounds)
{
for (Component component : components)
{
Rectangle bounds = newBounds.get(component);
component.setBounds(bounds);
component.validate();
}
}
private static void interpolate(
Map<Component, Rectangle> b0, Map<Component, Rectangle> b1,
Map<Component, Rectangle> b, double alpha)
{
for (Component component : b0.keySet())
{
Rectangle r0 = b0.get(component);
Rectangle r1 = b1.get(component);
Rectangle r = b.get(component);
interpolate(r0, r1, r, alpha);
}
}
private static void interpolate(
Rectangle r0, Rectangle r1, Rectangle r, double alpha)
{
r.x = (int)(r0.x + alpha * (r1.x - r0.x));
r.y = (int)(r0.y + alpha * (r1.y - r0.y));
r.width = (int)(r0.width + alpha * (r1.width - r0.width));
r.height = (int)(r0.height + alpha * (r1.height - r0.height));
}
private LayoutAnimator()
{
}
}
一个小样例:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoundedRangeModel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class LayoutAnimatorDemo
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent c = new JComponent()
{
private static final long serialVersionUID =
-8793865141504880212L;
@Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
};
f.setContentPane(c);
Container container = f.getContentPane();
container.setLayout(new FlowLayout());
JButton c0 = new JButton("FlowLayout");
JButton c1 = new JButton("GridLayout");
JButton c2 = new JButton("BorderLayout");
JButton c3 = new JButton("GridBagLayout");
JComponent c4 = new JPanel(new BorderLayout());
c4.add(new JLabel("Duration (ms) :"), BorderLayout.WEST);
JSlider slider = new JSlider(0, 2000);
slider.setMinimumSize(new Dimension(100, 100));
slider.setPaintTicks(true);
slider.setMajorTickSpacing(500);
slider.setPaintLabels(true);
c4.add(slider, BorderLayout.CENTER);
BoundedRangeModel b = slider.getModel();
connect(c0, container, new FlowLayout(), b);
connect(c1, container, new GridLayout(2,3), b);
connect(c2, container, createBorderLayout(c0, c1, c2, c3, c4), b);
connect(c3, container, createGridBagLayout(c0, c1, c2, c3, c4), b);
container.add(c0);
container.add(c1);
container.add(c2);
container.add(c3);
container.add(c4);
f.setSize(800, 600);
f.setVisible(true);
}
private static void connect(
JButton button, final Container container,
final LayoutManager layoutManager,
final BoundedRangeModel boundedRangeModel)
{
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
double durationS = boundedRangeModel.getValue() / 1000.0;
LayoutAnimator.execute(container, layoutManager, durationS);
}
});
}
private static LayoutManager createBorderLayout(
Component c0, Component c1, Component c2, Component c3, Component c4)
{
BorderLayout borderLayout = new BorderLayout();
borderLayout.addLayoutComponent(c0, BorderLayout.NORTH);
borderLayout.addLayoutComponent(c1, BorderLayout.CENTER);
borderLayout.addLayoutComponent(c2, BorderLayout.SOUTH);
borderLayout.addLayoutComponent(c3, BorderLayout.WEST);
borderLayout.addLayoutComponent(c4, BorderLayout.EAST);
return borderLayout;
}
private static LayoutManager createGridBagLayout(
Component c0, Component c1, Component c2, Component c3, Component c4)
{
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints c = null;
c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.weightx = 0.5;
c.weighty = 0.5;
c.gridwidth = 2;
c.fill = GridBagConstraints.BOTH;
gridBagLayout.addLayoutComponent(c0, c);
c = new GridBagConstraints();
c.gridx = 2;
c.gridy = 0;
c.weightx = 0.5;
c.weighty = 0.5;
c.fill = GridBagConstraints.BOTH;
gridBagLayout.addLayoutComponent(c1, c);
c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 1;
c.weightx = 0.25;
c.weighty = 0.5;
c.fill = GridBagConstraints.BOTH;
gridBagLayout.addLayoutComponent(c2, c);
c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 1;
c.weightx = 0.75;
c.weighty = 0.5;
c.fill = GridBagConstraints.BOTH;
gridBagLayout.addLayoutComponent(c3, c);
c = new GridBagConstraints();
c.gridx = 2;
c.gridy = 1;
c.weightx = 0.5;
c.weighty = 0.5;
c.fill = GridBagConstraints.BOTH;
gridBagLayout.addLayoutComponent(c4, c);
return gridBagLayout;
}
}