【问题标题】:Draw on top of an Applet inside a Frame在框架内的小程序顶部绘制
【发布时间】:2017-01-19 10:31:30
【问题描述】:

我正在尝试创建一个 Applet 加载程序,我需要在显示的 Applet 上进行绘制,但是我似乎找不到这样做的方法。

我最初的理解是,Applet 通过扩展 Component 就像任何可以添加到 Container 中的常规 java.awt.Component 一样,只是具有覆盖的paint方法,但它似乎不起作用。

在我的初始化代码中,我创建了一个 java.awt.Frame,在该框架上添加了我的 java.awt.Container 的自定义实现,该实现覆盖了所有绘制方法,以便它们在 x: 5, y:5 处填充 rect 的大小w:10, h:10 调用父方法后

但是,当添加小程序时,它总是,无论在所有内容之上绘制什么

public class AppletTest {

    public static void main(String[] args) {
        Frame frame = new Frame("Applet Test!");

        Container container = new Container() {

            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void paintAll(Graphics g) {
                super.paintAll(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void paintComponents(Graphics g) {
                super.paintComponents(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void print(Graphics g) {
                super.print(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void printComponents(Graphics g) {
                super.printComponents(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void update(Graphics g) {
                super.update(g);
                g.fillRect(5, 5, 10, 10);
            }

        };

        Dimension dimension = new Dimension(50, 50);
        container.setPreferredSize(dimension);

        Applet applet = new Applet() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.fillRect(0, 0, 10, 10);
            }
        };

        container.add(applet);

        applet.setBounds(0, 0, 50, 50);


        frame.add(container);
        frame.pack();
        frame.setVisible(true);

        applet.init();
        applet.start();


    }

}

需要采取哪些步骤才能在其父 ContainerApplet 之上进行绘制?

这也是上面代码运行结果的截图

如果我将applet 的类型更改为Component,例如

Component applet = new Component() {
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0, 0, 10, 10);
    }
};

结果正确

所需解决方案的局限性在于我无法修改 Applet 本身,因为它是一个仅以二进制形式提供的遗留组件。我知道通过修改字节码可以解决问题,但是由于 Applet 的风格很多,所以这是不可能的。

【问题讨论】:

  • 1) “我正在尝试创建一个 Applet 加载器..” 为什么? “..我需要借鉴显示的小程序顶部” 考虑使用JLayeredPane。 2) 如需更好的帮助,请发帖minimal reproducible example
  • 1) 我正在尝试创建一个层来扩展遗留的 Java 二进制小程序,并希望尽可能地保持类似于 swing 的“API”,我的意思是......这就是它应该如何工作的?您将一个组件添加到一个容器并且容器绘制了孩子,但是对于 Applets,它似乎并非如此.. 2)我发布的代码是最小的、完整的和可验证的,我不知道我能做的任何其他事情撕掉代码..它创建一个框架,向其中添加一个绘制矩形的容器,在该容器上添加一个小程序也绘制一个矩形..我还发布了当前和预期的运行结果..
  • @AndrewThompson MCVE...!?。这个目标可能是有问题的,但不管怎样,这是一个有趣的小故障。我尝试了不同的组件,并添加了打印语句(在super 调用之后)。如果组件是Container,那么它会打印"Paint component, paint container",并且它可以工作。如果组件是Panel,那么它会打印"Paint container, paint component",并且工作。但是Panel 类中不应该有任何会导致 such 不同行为的内容。我没有投入太多时间,但很想知道发生了什么
  • 确实非常奇怪的行为!我很肯定这与 Applet 沙盒有关。我将调试器附加到 Applet 的 onPaint 方法,堆栈跟踪显示它不是由父组件绘制的,而是由一些接收到的 RepaintArea 绘制的重绘活动.. 感谢您抽出宝贵时间!
  • 您是否可以接受 Sergey 的当前答案,或者您是否正在寻找替代方案(以及对奇怪行为的可能解释)?

标签: java swing applet awt


【解决方案1】:

为什么容器正方形不可见

这是因为applet 重叠了容器的drawaple 区域。如果设置小程序的背景颜色并更改大小,您可以看到:

applet.setBackground(Color.RED);
applet.setBounds(0, 0, 12, 12);

结果我们可以看到小程序上绘制的黑色方块下方的红色边框(小程序的背景):

与您的小程序大小和小程序的红色背景完全重叠容器可绘制区域:

如果您将Applet 更改为Component,您可以从容器中看到黑色方块,因为Component 类没有背景。 IE。改变小程序变量的类型:

Component applet = new Component() {
//...
};
applet.setBackground(Color.RED);

您可以在实验中看到图像:

在小程序之上绘图

需要采取哪些步骤才能从其父 Container 在 Applet 之上进行绘制?

不能在小程序上绘制,除非直接在小程序上绘制。

使用GlassPane 不能解决小程序的这个问题。我从文档中尝试了example

并替换代码:

contentPane.add(new JButton("Button 1"));
contentPane.add(new JButton("Button 2"));

到:

Applet applet = new Applet() {
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0, 0, 10, 10);
    }
};
applet.setPreferredSize(new Dimension(100, 25));
applet.setBackground(Color.GREEN);
contentPane.add(applet);

在结果中我们可以看到applet,它重叠了一个圆圈:

如果我们将小程序变量的类型更改为JLanel,则圆已完全绘制。

【讨论】:

  • 感谢您提供图解示例,但即使这抽象地回答了这个问题——“不,你做不到”,它并没有真正告诉我为什么?我查看了 applet.java 的 Grepcode,但仍然无法弄清楚那里发生了什么。Applet 是如何赋予其绘制所有内容的特殊能力的?
  • 我明白你的要求。明天我会研究这个问题
  • Sergey,据我所知,OP 不想更改 applet 的类型,因为他们使用的是预制小程序,只是从二进制文件中读取它。如果我错了,请纠正我@Ruuhkis
  • @Dan,我感觉到了这一点,但我研究了不同的方法来提出适当的解决方案并在示例中展示最有趣的结果。我明白,改变类型 - 这不是解决方案
  • 这重复了问题的陈述。有更多丰富多彩的例子
【解决方案2】:

正如Sergey 所说,您的Applet 与您的Container 重叠,并且由于它有背景,您无法在Container 上看到绘制的矩形。但是,据我所知,不可能将此背景设置为透明颜色。您可以查看这些帖子,herehere,讨论主题和可能的解决方案。

当您可以更改applet 的代码但不能更改applet 的类型时的最佳方法

import java.applet.Applet;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

@SuppressWarnings("serial")
public class AppletTest {
    public static void main(String[] args) {
        Frame frame = new Frame("Applet Test");
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                frame.dispose();
             }
         });

        int apHeight = 50;
        int apWidth = 50;

        Container container = new Container() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                Graphics2D graphs = (Graphics2D) g;
                graphs.setBackground(Color.WHITE);
                graphs.clearRect(0, 0, apWidth, apHeight);
                g.setColor(Color.RED);
                g.fillRect(5, 5, 10, 10);
            }
        };

        Dimension dimension = new Dimension(50, 50);
        container.setPreferredSize(dimension);

        frame.add(container);
        frame.pack();
        frame.setVisible(true);

        BufferedImage bufImage = new BufferedImage(dimension.width, dimension.height, BufferedImage.TYPE_INT_RGB);
        container.paint(bufImage.createGraphics());

        Applet applet = new Applet() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.drawImage(bufImage, 0, 0, this); 
                g.fillRect(0, 0, 10, 10);
            }
        };

        container.add(applet);
        applet.setBounds(0, 0, apWidth, apHeight);
        applet.init();
    }
}

它的输出如下所示。

编辑澄清问题
如果您从文件中读取,最好的方法是声明您的小程序,创建一个图像,然后将图像绘制到小程序上。我做了一个例子,制作二进制文件,然后从中读取对象。之后,它会在其上绘制背景。

import java.applet.Applet;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

@SuppressWarnings("serial")
public class AppletTest {
    private static void createBinaryApplet() throws IOException {
        Applet applet = new Applet() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.fillRect(0, 0, 10, 10);
            }
        };

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("applet.dat"));
        oos.writeObject(applet);
        oos.close();
    }

    public static void main(String[] args) {
        Frame frame = new Frame("Applet Test");
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                frame.dispose();
             }
         });

        final int prefHeight = 50;
        final int prefWidth = 50;

        Container container = new Container() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                Graphics2D graphs = (Graphics2D) g;
                graphs.setBackground(Color.WHITE);
                graphs.clearRect(0, 0, prefWidth, prefHeight);
                g.setColor(Color.RED);
                g.fillRect(5, 5, 10, 10);
            }
        };

        container.setPreferredSize(new Dimension(prefWidth, prefHeight));

        frame.add(container);
        frame.pack();
        frame.setVisible(true);

        BufferedImage bufImage = new BufferedImage(prefWidth, prefHeight, BufferedImage.TYPE_INT_RGB);
        container.paint(bufImage.createGraphics());

        try {
            createBinaryApplet();

            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("applet.dat"));
            Applet applet = (Applet) ois.readObject();
            ois.close();

            container.add(applet);
            applet.setBounds(0, 0, prefWidth, prefHeight);
            applet.init();

            Graphics g = applet.getGraphics();
            g.drawImage(bufImage, 0, 0, applet);
            applet.paint(g);
        } catch(ClassNotFoundException | IOException e) {
            System.out.println("Whoops");
        }
    }
}

这会产生与以前相同的输出。

【讨论】:

  • 对不起,如果我没有说得太清楚。我无法修改 Java 小程序,因为它们是作为二进制文件提供的,并且通过修改字节码对每个小程序执行此操作是做太多的家务。搞乱JNA是什么意思?我怎样才能通过访问原生的东西来实现这一点?
  • @Ruuhkis 好吧。我懂了。让我做更多的研究,我会用一种新的方法来编辑我的答案以达到结果并解释问题,或者至少使用 JNA 更好地解决它
  • @Ruuhkis 请问您从哪里获取二进制小程序或获取它们的副本以进行测试?
  • @Ruuhkis 我也误会了 JNA。那不能用来使小程序透明,只有根窗口
  • @Ruuhkis 我编辑了关于如何更新“预制”小程序背景的答案。如果我假设有问题,请告诉我,我会尝试修改我的答案。此外,如果您使用其他类型的文件,这不起作用,我是否有可能获得类似的示例文件,以便我可以找到针对该文件类型执行此操作的方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多