【问题标题】:Using a canvas object in a thread to do simple animations - Java在线程中使用画布对象做简单的动画 - Java
【发布时间】:2012-10-19 09:48:32
【问题描述】:

我有一个小程序,正如现在所写的那样,它应该在按下“运行”按钮后绘制并在顶部弹跳一个球(代码中的drawingpanel)。在线查看其他示例并使用来自不使用 Canvas 的不同程序的代码我无法弄清楚为什么我的 Ball 对象没有出现。我已经测试了程序的流程,并且所有的方法都按照它们应该的方式被调用,所以不是代码没有进入paint 方法的情况。任何关于使用 Canvas/Thread 的好资源都会很棒,或者关于以下小程序的任何建议都会非常有用。谢谢!

import java.awt.*;
import java.applet.*;
import java.awt.event.*;

public class Bounce2 extends Applet implements ActionListener, AdjustmentListener, Runnable
{
    private final static long serialVersionUID = 1L;

    //runtime variables
    boolean running = false;
    boolean currentlyCircle = true;
    boolean showtails = false;
    boolean kill = false;

    //buttons
    Button runbutton = new Button("Run"); 
    Button pausebutton = new Button("Pause");
    Button quitbutton = new Button("Quit");

    //text
    Label speedlabel = new Label("Speed");
    Label sizelabel = new Label("Size");

    //scrollbars
    private final int barHeight = 20;
    private final int SLIDER_WIDTH = 10;
    private final int MAXSPEED = 110;
    private final int MINSPEED = 0;
    private final int MAX_SIZE = 110;
    private final int MIN_SIZE = 10;
    Scrollbar speedbar = new Scrollbar(Scrollbar.HORIZONTAL, MAXSPEED/2, SLIDER_WIDTH, MINSPEED, MAXSPEED);
    Scrollbar sizebar = new Scrollbar(Scrollbar.HORIZONTAL, MAX_SIZE/2, SLIDER_WIDTH, MIN_SIZE, MAX_SIZE);

    //drawn objs
    Ball ball;
    Image buffer;
    int size = 50;  
    private Graphics obj;
    Point currentlocation = new Point(100,100);
    Point previouslocation;
    Point nextlocation;

    Rectangle circle;
    Rectangle screen;
    private Thread ballThread;

    //boundaries
    int bound_x;
    int bound_y;

    //directions
    int dx = 1; //1 = left, -1 = right
    int dy = 1; //1 = up, -1 = down

    //speed
    int speed = speedbar.getValue();
    int delay;

    //initialize the applet and draw everything
    public void init()
    {
        double colWeight[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//15 cols
        double rowWeight[] = {1,1,1,1,1,1,1,1,1,1}; //10 rows
        int colWidth[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};//15 cols
        int rowHeight[] = {1,1,1,1,1,1,1,1,1,1}; //10 rows
        GridBagConstraints c = new GridBagConstraints();
        GridBagLayout gbl = new GridBagLayout();
        gbl.rowHeights = rowHeight;
        gbl.rowWeights = rowWeight;
        gbl.columnWeights = colWeight;
        gbl.columnWidths = colWidth;
        c.anchor = GridBagConstraints.CENTER;

        setBounds(0,0,480,640);
        setLayout(new BorderLayout());
        Panel controlpanel = new Panel();
        controlpanel.setLayout(gbl);
        controlpanel.setSize(640,80);

        Panel drawingpanel = new Panel(null);
        drawingpanel.setSize(640,400);
        ball = new Ball();
        drawingpanel.add("Center", ball);
        Rectangle circle = new Rectangle(size, size);
        Rectangle screen = new Rectangle(0,0,640,400);
        drawingpanel.setVisible(true);

        //speed scrollbar
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 3;
        c.gridheight = 1;
        c.gridx = 1;
        c.gridy = 7;
        c.fill= GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(this.speedbar,c);

        //run button
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 2;
        c.gridheight = 1;
        c.gridx = 5;
        c.gridy = 7;
        c.fill= GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(this.runbutton,c);

        //pause button
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 2;
        c.gridheight = 1;
        c.gridx = 8;
        c.gridy = 7;
        c.fill= GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(this.pausebutton,c);

        //size scrollbar
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 3;
        c.gridheight = 1;
        c.gridx = 11;
        c.gridy = 7;
        c.fill= GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(this.sizebar,c);

        //speed text label
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 3;
        c.gridheight = 1;
        c.gridx = 1;
        c.gridy = 8;
        c.fill= GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(this.speedlabel,c);

        //size text label
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 3;
        c.gridheight = 1;
        c.gridx = 11;
        c.gridy = 8;
        c.fill= GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(this.sizelabel,c);

        //quit button
        c.weightx = 1;
        c.weighty = 1;
        c.gridwidth = 3;
        c.gridheight = 1;
        c.gridx = 6;
        c.gridy = 9;
        c.fill= GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(this.quitbutton,c);

        //add to the screen
        controlpanel.add(this.speedbar);
        controlpanel.add(this.runbutton);
        controlpanel.add(this.pausebutton);
        controlpanel.add(this.sizebar);
        controlpanel.add(this.speedlabel);
        controlpanel.add(this.sizelabel);
        controlpanel.add(this.quitbutton);

        //add listners
        speedbar.addAdjustmentListener(this);
        runbutton.addActionListener(this);
        pausebutton.addActionListener(this);
        sizebar.addAdjustmentListener(this);
        quitbutton.addActionListener(this);

        //add the panels
        add("South", controlpanel);
        add("Center", drawingpanel);

        //drawing paramaters, draw the first object
        System.err.println(obj);
        obj = drawingpanel.getGraphics();
        nextlocation = new Point(currentlocation.x+dx, currentlocation.y+dy);

        setVisible(true);
        validate();
    }

    public void start()
    {
        if (ballThread == null)
        {
            ballThread = new Thread(this);
            ballThread.start();
            repaint();
        }
    }

        public void run()
{
    Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
    while (!kill)
    {
        if (running)
        {
            ball.move();
            repaint();
        }
        try
        {
            Thread.sleep(delay);
        }
        catch(InterruptedException e){System.err.println("Interrupted.");}
    }
    stop();
}

public void update(Graphics g)
{   
    Graphics buffer;
    Image offscreen = null;

    offscreen = createImage(bound_x, bound_y);
    buffer = offscreen.getGraphics();
    buffer.setColor(getBackground());
    buffer.fillRect(0,0,bound_x, bound_y);
    //update

    previouslocation = new Point(currentlocation);
    currentlocation = nextlocation;

    //draw
    buffer.setColor(Color.black);
    buffer.drawOval(nextlocation.x, nextlocation.y, size, size);
    buffer.fillOval(nextlocation.x, nextlocation.y, size, size);

    //draw rectangles out of vector     
    g.drawImage(offscreen, 0,0, null);
    paint(buffer);
}   

//class to handle animations
class Ball extends Canvas 
{
    public void move()
    {
        nextlocation = new Point(currentlocation.x+dx, currentlocation.y+dy);
        //if it will hit the right or left, flip the x direction and set it 
        if (nextlocation.x+size >= bound_x || nextlocation.x <= 0)
        { dx *= -1; }
        nextlocation.x += dx;
        //if it will hit the top or bottom, flip the y direction and set it
        if (nextlocation.y+size >= bound_y + 100 || nextlocation.y <= 0)
        { dy *= -1; }
        nextlocation.y += dy;
        setBounds(dx,dy,size,size);
        System.out.println(dx + "," + dy);
    }   

    public void paint(Graphics g)
    {
        super.paint(g);
        g.setColor(Color.black);
        g.drawOval(0, 0, size, size);
    }
}

    public void actionPerformed(ActionEvent e)
    {
        Object source = e.getSource();
        if (source == this.runbutton)
        {
            running = true;
        }
        else if (source == this.pausebutton)
        {
            running = false;
        }
        else if (source == this.quitbutton)
        {
            //kill processes
            kill = true;
            //remove listeners
            stop();
        }
    }

    public void adjustmentValueChanged(AdjustmentEvent e)
    {
        Object source = e.getSource();
        //set the new size. 
        if (source == sizebar)
        {
            //check for clipping
            int newsize = sizebar.getValue();

            // x
            if (currentlocation.x+newsize >= bound_x)
            { 
                newsize = bound_x - currentlocation.x - 1;
                sizebar.setValue(newsize);
            }

            // y
            if (currentlocation.y+newsize >= bound_y + 100)
            {
                newsize = bound_y+100 - currentlocation.y - 1;
                sizebar.setValue(newsize);
            }
            size = newsize;
        }
        if (source == speedbar)
        {
            speed = speedbar.getValue();
            delay = MAXSPEED - speedbar.getValue();
        }
    }

    public void stop()
    {
        this.speedbar.removeAdjustmentListener(this);
        this.runbutton.removeActionListener(this);
        this.pausebutton.removeActionListener(this);
        this.sizebar.removeAdjustmentListener(this);
        this.quitbutton.removeActionListener(this);
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    }
}

【问题讨论】:

    标签: java animation applet awt java-canvas


    【解决方案1】:

    说实话,我不知道从哪里开始。

    这是个坏主意。

    private Graphics obj;
    .
    .
    .
    obj = drawingpanel.getGraphics();
    

    任何组件图形都只是时间的快照。在下一次重绘时,它将失效(我实际上是一个不同的对象)

    你的绘画方法是错误的。无论如何,这实际上是错误的地方,但有很多事情是错误的。除了你应该很少覆盖顶级容器paint 方法的事实

    public void paint(Graphics obj)
    {
        // I can see you're trying to double buffer the graphics, but I would have a
        // buffer already set up...
        buffer = createImage(640,400);
        // You should NEVER dispose of Graphics context you didn't created...
        if (obj != null)
            obj.dispose();
    
        // Now we're really stuffed.  You've just overridden the screen graphics context
        obj = buffer.getGraphics();
        obj.setColor(getBackground());
        //update
        previouslocation = new Point(currentlocation);
        currentlocation = nextlocation;
    
        //draw
        obj.fillRect(currentlocation.x, currentlocation.y, size, size);
        obj.setColor(Color.black);
        obj.drawOval(nextlocation.x, nextlocation.y, size, size);
        obj.fillOval(nextlocation.x, nextlocation.y, size, size);
        /*
         *draw rectangles out of vector
         */
        // Now you drawing the buffer onto itself...???
        obj.drawImage(buffer, 0,0, null);
    }   
    

    回顾您的代码,我发现您正在向主绘制区域添加一个 Ball 组件,并且您正在为此绘制区域使用 null 布局。

    如果你放弃布局管理器,你需要接管布局子组件的控制权...

    您需要绘制Ball...所以在您的Ball 类中,您需要覆盖paint 方法...

    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(Color.black);
        g.drawOval(0, 0, size, size);
    }
    

    在您的Ball#move 方法中,您需要更新球的位置...

    public void move() {
        // You've previous move code
        setBounds(dx, dy, size, size);
    }
    

    这会让你画画……或多或少……

    不过,我建议您放弃 AWT 并改用 Swing 组件,如果没有其他原因,它们是双缓冲的。

    你看看

    你也可以看看

    举几个例子;)

    【讨论】:

    • 好吧哈哈,我发现有很多错误,感谢您逐点介绍。我对 Ball 类进行了更改并进行了更新(以前是paint),并在 Ball 中添加了您推荐的 paint 方法。它确实有效,有点;球不是实心的,它会在一个只有一个像素的可用空间的小矩形中反弹。
    • 是的,这就是我要去的地方,我认为您用于计算球可以移动的范围是错误的,但我想我会把它留给您;)。您可以将paint方法中的g.drawOval(0, 0, size, size);调用更改为g.fillOval(0, 0, size, size);来填充它
    • 哈哈好吧,这是一个非常简单的修复。奇怪的是,移动球的计算与另一个不使用画布的类似程序相同。显然我将不得不重写...
    猜你喜欢
    • 2014-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-06
    • 2013-06-14
    • 2012-09-01
    相关资源
    最近更新 更多