Using a canvas object in a thread to do simple animations - Java

别来无恙 提交于 2019-12-11 04:06:59

问题


I have an applet that, as written now, should be drawing and bouncing a ball around the top (drawingpanel in the code) after the "Run" button is pressed. Looking at other examples online and using code from a different program that did not use Canvas I am not able to figure out why my Ball object is not showing up. I have tested the flow of the program, and all the methods are being called as they should be, so it isn't the case where the code doesn't make it to the paint method. Any good resources on using Canvas/Thread would be great, or any advice on the following applet would be much appriciated. Thank you!

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);
    }
}

回答1:


To be honest, I don't know where to start with this one.

This is a bad idea.

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

Any components graphics is just a snap shot in time. On the next repaint, it will invalidated (and my actually be a different object)

You paint method is wrong. It's actually the wrong place to do this any way, but there are just to many things wrong. Apart from the fact you should very rarely override top level containers paint methods

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);
}   

Going back through you code, I found that you are adding a Ball component to you main paint area and you're using a null layout for this paint area.

If you discard the layout manager, you need to take over control of laying out the child components...

You need away to draw the Ball...so in your Ball class, you need to override the paint method...

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

In your Ball#move method, you need to update the location of the ball...

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

That will get you painting working...more or less...

I would, however, suggest you discard AWT and use Swing components instead, if for no other reason, they're double buffered.

You take a look through

  • Creating a GUI With JFC/Swing
  • Performing Custom Painting
  • 2D Graphics

You could also have a look at

  • Java Bouncing Ball
  • the images are not loading

For some examples ;)



来源:https://stackoverflow.com/questions/13132491/using-a-canvas-object-in-a-thread-to-do-simple-animations-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!