Moving JLabel to other JLabels - GUI

前端 未结 1 364
无人及你
无人及你 2020-11-29 13:57

I\'m trying to make a JLabel move across other JLabels, only 1 timer works right now. It is supposed to operate like a train moving across a track, going all the way around

相关标签:
1条回答
  • 2020-11-29 14:58

    nb: I don't like null layouts, I don't condone null layouts, I would prefer to have used custom painting, but that's a lot of work not related to the question. This example is intended to focus on the implementation of a Timeline and KeyFrame animation

    Because you have to move the object through both the x/y positions, but in different directions over the same time period, this becomes a very complex problem...

    You could try and set up four, chained Timers, which trigger the next Timer when they complete, and which all do a different part of the animation...but frankly, that becomes a mess real quick...

    A better idea is to use a concept of a time line and key frames. The idea is, that over the time line (0-1), certain events occur at prescribed times (key frames). The time line will then blend between these key frames...

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Point;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.TreeMap;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class MoveLabel {
    
        public static void main(String[] args) {
            new MoveLabel();
        }
    
        public MoveLabel() {
            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.getContentPane().setLayout(new BorderLayout());
                    TestPane testPane = new TestPane();
                    testPane.setBackground(Color.WHITE);
                    frame.getContentPane().add(testPane);
    
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public static class TestPane extends JPanel {
    
            private JTextField tf;
            private List<JTextField> tracks;
    
            protected static final int PLAY_TIME = 4000;
    
            private Timeline timeline;
            private long startTime;
    
            public TestPane() {
                setLayout(null);
    
                tracks = new ArrayList<JTextField>(20);
    
                int x = 20;
                int y = 20;
    
                for (int index = 0; index < 6; index++) {
                    x += 20;
                    tracks.add(createTrack(x, y, 20, 20));
                }
                for (int index = 0; index < 6; index++) {
                    y += 20;
                    tracks.add(createTrack(x, y, 20, 20));
                }
                for (int index = 0; index < 6; index++) {
                    x -= 20;
                    tracks.add(createTrack(x, y, 20, 20));
                }
                for (int index = 0; index < 6; index++) {
                    y -= 20;
                    tracks.add(createTrack(x, y, 20, 20));
                }
    
                for (JTextField track : tracks) {
                    add(track);
                }
    
                tf = new JTextField("");
                tf.setSize(20, 20);
                tf.setBackground(Color.red);
                add(tf);
                setComponentZOrder(tf, 0);
    
                timeline = new Timeline();
                timeline.add(0, new Point(20, 20));
                timeline.add(0.25f, new Point(20 * 7, 20));
                timeline.add(0.5f, new Point(20 * 7, 20 * 7));
                timeline.add(0.75f, new Point(20, 20 * 7));
                timeline.add(1f, new Point(20, 20));
    
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        long duration = System.currentTimeMillis() - startTime;
                        float progress = (float) duration / (float) PLAY_TIME;
                        if (progress > 1f) {
                            startTime = System.currentTimeMillis();
                            progress = 0;
    //                      ((Timer) (e.getSource())).stop();
                        }
    
                        Point p = timeline.getPointAt(progress);
                        tf.setLocation(p);
    
                    }
                });
                startTime = System.currentTimeMillis();
                timer.start();
    
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(600, 500);
    
            }
    
            protected JTextField createTrack(int x, int y, int width, int height) {
                JTextField field = new JTextField();
                field.setBounds(x, y, width, height);
                field.setEditable(false);
                field.setFocusable(false);
                return field;
            }
    
        }
    
        public static class Timeline {
    
            private Map<Float, KeyFrame> mapEvents;
    
            public Timeline() {
                mapEvents = new TreeMap<>();
            }
    
            public void add(float progress, Point p) {
                mapEvents.put(progress, new KeyFrame(progress, p));
            }
    
            public Point getPointAt(float progress) {
    
                if (progress < 0) {
                    progress = 0;
                } else if (progress > 1) {
                    progress = 1;
                }
    
                KeyFrame[] keyFrames = getKeyFramesBetween(progress);
    
                float max = keyFrames[1].progress - keyFrames[0].progress;
                float value = progress - keyFrames[0].progress;
                float weight = value / max;
    
                return blend(keyFrames[0].getPoint(), keyFrames[1].getPoint(), 1f - weight);
    
            }
    
            public KeyFrame[] getKeyFramesBetween(float progress) {
    
                KeyFrame[] frames = new KeyFrame[2];
                int startAt = 0;
                Float[] keyFrames = mapEvents.keySet().toArray(new Float[mapEvents.size()]);
                while (startAt < keyFrames.length && keyFrames[startAt] <= progress) {
                    startAt++;
                }
    
                if (startAt >= keyFrames.length) {
                    startAt = keyFrames.length - 1;
                }
    
                frames[0] = mapEvents.get(keyFrames[startAt - 1]);
                frames[1] = mapEvents.get(keyFrames[startAt]);
    
                return frames;
    
            }
    
            protected  Point blend(Point start, Point end, float ratio) {
                Point blend = new Point();
    
                float ir = (float) 1.0 - ratio;
    
                blend.x = (int)(start.x * ratio + end.x * ir);
                blend.y = (int)(start.y * ratio + end.y * ir);
    
                return blend;
            }
    
            public class KeyFrame {
    
                private float progress;
                private Point point;
    
                public KeyFrame(float progress, Point point) {
                    this.progress = progress;
                    this.point = point;
                }
    
                public float getProgress() {
                    return progress;
                }
    
                public Point getPoint() {
                    return point;
                }
    
            }
    
        }
    }
    

    You could then do silly things like vary the speed between sections...

    timeline = new Timeline();
    timeline.add(0, new Point(20, 20));
    timeline.add(0.1f, new Point(20 * 7, 20));
    timeline.add(0.5f, new Point(20 * 7, 20 * 7));
    timeline.add(0.6f, new Point(20, 20 * 7));
    timeline.add(1f, new Point(20, 20));
    

    enter image description here

    0 讨论(0)
提交回复
热议问题