3

直径 512untis の 2 つの回転ホイールを異なる速度で表示しようとしていますが、以前に描画されたイメージ グラフィックを削除して、回転したグラフィックを正しい位置に設定することができません。今のところ、任意の角度で回転しています。私は affineTransform を試して回転を得ましたが、すべてのピクセルが広がっているように奇妙でした。thread.sleep() で while ループを使用しています。コードは次のとおりです。 // drawSmallCircle と drawBigCircle は 2 つの画像を返します。

class MyFramePart2 extends JFrame

{ 

    String name;
    JPanel big_obj_panel,small_obj_panel;    
    JLabel bigLabel,smallLabel;BufferedImage imgRet,imgRetSmall;
    static double radians,angle,rev,fps,smallAngle,smallRadians;               
    int numLines,i=0;    

    MyFramePart2(String frameName,int numStrokes,double revolutions,double frameps)
    {       
        numLines=numStrokes;
        smallAngle=smallRadians=angle=radians=Math.toRadians(360/numLines);        
        rev=revolutions;
        fps=frameps; 

        setSize(1240,720);       
        setLocation(0,0);
        setLayout(null); 
        getContentPane().setBackground(Color.WHITE);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

        big_obj_panel=new JPanel();
        big_obj_panel.setLayout(null);
        big_obj_panel.setSize(512,512);
        big_obj_panel.setLocation(100,100); 
        big_obj_panel.setBackground(Color.white);
        add(big_obj_panel);   


        imgRet=drawBigCircle();
        bigLabel = new JLabel(new ImageIcon(imgRet));
        bigLabel.setLayout(null);        
        bigLabel.setLocation(0,0);
        bigLabel.setSize(512,512);
        bigLabel.setOpaque(true);
        bigLabel.setBackground(Color.white);  
        big_obj_panel.add(bigLabel);

        small_obj_panel=new JPanel();
        small_obj_panel.setLayout(null);
        small_obj_panel.setSize(512,512);
        small_obj_panel.setLocation(700,100); 
        small_obj_panel.setBackground(Color.white);
        add(small_obj_panel);   

        imgRetSmall=drawSmallCircle();

        smallLabel = new JLabel(new ImageIcon(imgRetSmall));
        smallLabel.setLayout(null);        
        smallLabel.setLocation(0,0);
        smallLabel.setSize(512,512);
        smallLabel.setOpaque(true);
        smallLabel.setBackground(Color.white);  
        small_obj_panel.add(smallLabel);

        setVisible(true);               

        while(i!=5) // suppose to be while true, just checking 
      {                 
        setVisible(true);          
        bigLabel.setIcon(new ImageIcon(imgRet));         
        smallLabel.setIcon(new ImageIcon(imgRetSmall));
        try{
        Thread.sleep(10);        
        }
        catch(Exception e)
        {}
        i++;               
     }                         
 }

    @Override
    public void paint(Graphics g)
    {        
        super.paint(g);
        Graphics2D g2d=(Graphics2D)g;           
        g2d.translate(256,256);
        g2d.rotate(Math.toRadians(20));                       
        g2d.translate(-256,-256); 
        g2d.drawImage(imgRet,0,0,null);
        g2d.dispose();        

        super.paint(g);
        g2d=(Graphics2D)g;
        g2d.translate(256,256);
        g2d.rotate(Math.toRadians(30));
        g2d.translate(-256,-256);        
        g2d.drawImage(imgRetSmall,0,0,null);        
        g2d.dispose();
    }

    public static BufferedImage drawBigCircle()
    {
        BufferedImage img=new BufferedImage(512,512,BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d=img.createGraphics();                
        g2d.setColor(Color.black);
        g2d.drawOval(0,0,511,511);

        Line2D l2d;
        while(angle <= 2*Math.PI)
        {
            l2d=new Line2D.Double(256,256,256+256*Math.cos(angle),256+256*Math.sin(angle));
            g2d.draw(l2d);
            angle=angle+radians;
        }               
        return img;
    }        
}
4

1 に答える 1

2

スイングの最初のルール。イベントディスパッチスレッドをブロックしないでください。これを行うと、アプリケーションがハングしているように見え、EDT が再描画要求を処理できなくなります。

つまり、EDT をブロックしない更新をスケジュールする何らかの方法が必要です。

Swing の 2 番目のルール。EDT 以外のスレッドから UI コンポーネントを作成または変更しないでください。

paint一般に、のようなトップ レベル コンテナのメソッドをオーバーライドすることは避けJFrameてください。それらはダブル バッファリングされていません。つまり、ペイントが更新されるとちらつきます。代わりに、次のSwingようなコンテナーの 1 つを使用する必要があります。JPanel

これを実現するには、さまざまな方法があります。基本的に、ここでは 3 つのリストを使用しましたが、本気なら、必要なすべての情報 (画像、角度、デルタ) を維持できるオブジェクトを作成します。

実際のアニメーションを実現するために、javax.swing.Timer. これにより、少なくともn期間ごとにイベントがトリガーされますが、さらに重要なことは、イベント ディスパッチ スレッドのコンテキスト内でトリガーされることです。これにより、角度に加えられたすべての変更が、値の更新中にペイントが発生する可能性を防ぐ方法で行われることが保証されます...

この例では、3 つの画像を異なる (ランダムな) 速度で回転させます...

ここに画像の説明を入力

public class TestRotation {

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

    public TestRotation() {
        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 AnimationPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class AnimationPane extends JPanel {

        private List<BufferedImage> images;
        private List<Double> angles;
        private List<Double> speed;

        public AnimationPane() {
            images = new ArrayList<>(5);
            images.add(createWheel(50, 4));
            images.add(createWheel(50, 3));
            images.add(createWheel(50, 6));
            angles = new ArrayList<>();
            speed = new ArrayList<>();
            for (int index = 0; index < images.size(); index++) {
                angles.add(0d);
                speed.add(Math.random() * 5d);
            }

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (int index = 0; index < angles.size(); index++) {
                        double angle = angles.get(index);
                        double delta = speed.get(index);
                        angle += delta;
                        angles.set(index, angle);
                    }
                    repaint();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int x = 0;
            int y = 0;
            for (int index = 0; index < images.size(); index++) {
                BufferedImage image = images.get(index);
                double angle = angles.get(index);

                // This is important.  Basically we going to grab a isolated snap shot
                // of the current graphics context.  This means any changes we make
                // will not affect the original graphics context (other then painting)
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                AffineTransform at = new AffineTransform();
                at.translate(x, y);
                at.rotate(Math.toRadians(angle), image.getWidth() / 2, image.getHeight() / 2);
                g2d.setTransform(at);
                g2d.drawImage(image, 0, 0, this);
                g2d.dispose();

                x += image.getWidth();

            }
        }

    }

    protected Point2D calculateOutterPoint(int radius, double angel) {

        int x = Math.round(radius / 2);
        int y = Math.round(radius / 2);

        double rads = Math.toRadians((angel + 90));

        // This determins the length of tick as calculate from the center of
        // the circle.  The original code from which this derived allowed
        // for a varible length line from the center of the cirlce, we
        // actually want the opposite, so we calculate the outter limit first
        double fullLength = (radius / 2d);

        // Calculate the outter point of the line
        double xPosy = (x + Math.cos(rads) * fullLength);
        double yPosy = (y - Math.sin(rads) * fullLength);

        return new Point2D.Double(xPosy, yPosy);

    }

    public BufferedImage createWheel(int radius, int spokes) {
        BufferedImage img = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.black);
        g2d.drawOval(0, 0, radius - 1, radius - 1);

        Point2D center = new Point2D.Double(radius / 2d, radius / 2d);
        double angle = 360d / spokes;
        for (int index = 0; index < spokes; index++) {
            Point2D p = calculateOutterPoint(radius, index * angle);
            g2d.draw(new Line2D.Double(center, p));
        }

        g2d.dispose();
        return img;

    }

}
于 2013-02-13T10:18:21.240 に答える