【问题标题】:Java Applet - Stop other threads from changing Graphics colorJava Applet - 阻止其他线程更改图形颜色
【发布时间】:2018-11-25 18:08:45
【问题描述】:

作为学校项目的一部分,我们必须使用 Applets 创建一个小游戏。我现在正在做一些测试,但有一件事我不太明白: 我想在我的 Applet 屏幕上同时在我的屏幕上飞行多个对象。动画效果是通过绘制对象,删除它然后在一段时间后移动它来创建的。

这是我的代码: 机器人世界级 封装核心;

import items.Obstacle;

import java.applet.Applet;
import java.awt.*;
import java.util.ArrayList;

public class Roboterwelt extends Applet {

    private ArrayList<Obstacle> obstacles = new ArrayList<>();

    @Override
    public void init() {
        setSize(600, 600);
        Graphics g = getGraphics();
        g.setColor(Color.BLACK);
        for(int x = 0; x < 5; x++) {
            Obstacle h = new Obstacle((x+1)*100, 100, g, this);
            obstacles.add(h);
            Thread t = new Thread(h);
            t.start();
        }
    }

    @Override
    public void paint(Graphics g) {
        for(Obstacle o : obstacles) {
            o.draw();
        }
    }

}

障碍类 包装物品;

import java.applet.Applet;
import java.awt.*;

public class Obstacle implements Runnable {

    private int x;
    private int y;

    private Graphics g;

    public Hindernis(int x, int y, Graphics g) {
        this.x = x;
        this.y = y;
        this.g = g;
    }    

    public void draw() {
        g.drawOval(x, y, 50, 50);   //Draw obstacle
    }

    //Deleting the obstacle by covering it with a white circle
    public void delete() {
        g.setColor(Color.WHITE); //Change the color to white
        g.fillOval(x-5,y-5,60,60); //Making it a bit bigger than the obstacle to fully cover it
        g.setColor(Color.BLACK); //Reset the color to black
    }

    @Override
    public void run() {
        try {
            while(y < 600) {
                delete();
                y += 10;
                draw();
                Thread.sleep(1000);
            }
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

}

问题是我更改图形对象的颜色以覆盖白色圆圈的部分。当我有多个线程运行以表示屏幕上的多个障碍并且同时发生重绘和删除时,线程在将颜色更改为白色后被中断,并使用 Graphics 对象绘制一个填充的椭圆,该对象的颜色被另一个线程设置为黑色将 delete() 方法运行到最后。

如何强制程序在颜色变为白色和绘制填充的椭圆形之间不中断 delete() 方法?

【问题讨论】:

  • 1.不要使用getGraphics,这不是自定义绘画的方式; 2.不要覆盖顶级容器的paint,在你的情况下,Applet不是双缓冲的,所以你最终会闪烁; 3. 您不需要多个线程,您只需要一个(更多线程!= 完成更多工作);
  • @MadProgrammer 那我应该如何同时移动多个障碍物?
  • 使用单线程,循环实体列表。使用Thread.sleep控制帧率
  • 但它们不会同时移动,对吧?当我得到正确的时机时,它们看起来就像它们一样。
  • “更新”和您绘画的时间之间的时间意味着它们似乎同时移动 - 假设您正在正确执行绘画

标签: java applet awt


【解决方案1】:

免责声明

Applet 已弃用,浏览器、Oracle 或社区不再支持它。如果我试图鼓励你继续使用它们是不专业的。

我很欣赏这是一项“学校”作业,但也许是时候让您的老师跟上世界其他地方的步伐并开始使用实际上不会导致更多问题然后解决的问题(提示 JavaFX)-恕我直言

回答...

  • 不要使用getGraphics,这不是自定义绘画的方式。绘画应该在paint 方法的范围内完成。详情请查看Painting in AWT and Swing。除了解决您当前的问题之外,当小程序重新绘制自身时,您当前的方法可能会被“清除”干净。
  • 覆盖像Applet 这样的顶级容器的paint 是个坏主意。除了将您锁定在单个用例中之外,它们不是双缓冲的,这会在绘制时导致闪烁。最简单的解决方案是从 JPanel 开始,它是双缓冲的,可以添加到您想要使用的任何容器中。
  • 您不需要多个线程。线程是一种艺术形式。更多的线程并不总是意味着完成更多的工作,实际上会降低系统的性能。在您的情况下,您希望在一次通过中“更新”状态,然后安排一次绘制通过,以便操作在一个步骤中同步,并且您不会以“脏”更新结束

以下示例简单地使用了基于 AWT 的 Swing。它使用JFrame 而不是Applet,但这个概念很容易转移,因为核心功能是基于JPanel,所以你可以随意添加它。

它使用了 Swing Timer,它基本上会定期安排回调,但它会在外面进行,这样可以安全地更新 UI 的状态(这会替换您的 Thread)。

通过使用paintComponent 绘制Obstacles,我们可以免费获得两件东西。

  1. 双缓冲,不再闪烁
  2. Graphics 上下文是自动为我们准备的,我们不需要先“删除”对象,我们只需绘制当前状态

该示例还会在 Obstacle 通过面板边缘后将其删除,因此当它不再可见时,您不会浪费时间尝试移动/绘制它。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<Obstacle> obstacles;

        public TestPane() {
            Color[] colors = new Color[]{Color.RED, Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW};
            obstacles = new ArrayList<>(10);
            int y = 0;
            for (int index = 0; index < 5; index++) {
                y += 55;
                Obstacle obstacle = new Obstacle(y, 0, colors[index]);
                obstacles.add(obstacle);
            }
            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Iterator<Obstacle> it = obstacles.iterator();
                    while (it.hasNext()) {
                        Obstacle ob = it.next();
                        if (ob.move(getSize())) {
                            it.remove();
                        }
                    }
                    repaint();
                }
            });
            timer.start();
        }

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

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Iterator<Obstacle> it = obstacles.iterator();
            while (it.hasNext()) {
                Obstacle ob = it.next();
                ob.paint(g2d);
            }
            g2d.dispose();
        }

    }

    public class Obstacle {

        private int x, y;
        private Color color;

        public Obstacle(int x, int y, Color color) {
            this.x = x;
            this.y = y;
            this.color = color;
        }

        public void paint(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.fillRect(x, y, 50, 50);
        }

        public boolean move(Dimension size) {
            y += 1;
            return y > size.height;
        }
    }

}

但是所有Obstacles 都以相同的速度移动!

是的,那是因为您使用了单个 delta。如果您希望Obstacles 以不同的速率移动,请更改增量,例如...

public static class Obstacle {

    private static Random RND = new Random();

    private int x, y;
    private Color color;

    private int yDelta;

    public Obstacle(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;

        yDelta = RND.nextInt(5) + 1;
    }

    public void paint(Graphics2D g2d) {
        g2d.setColor(color);
        g2d.fillRect(x, y, 50, 50);
    }

    public boolean move(Dimension size) {
        y += yDelta;
        return y > size.height;
    }
}

【讨论】:

    猜你喜欢
    • 2023-01-26
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 2017-01-06
    • 2014-12-24
    相关资源
    最近更新 更多