在Windows上快速调整JFrame大小的黑色背景

h6my8fg2  于 2023-08-07  发布在  Windows
关注(0)|答案(2)|浏览(107)

bounty已结束.回答此问题可获得+150声望奖励。赏金宽限期10小时后结束。zoldxk希望引起更多关注此问题。

我用netbeans启动了一个新的java项目,我创建了一个JFrame,然后启动了我的应用程序,然后我注意到在调整窗口大小时出现了一个黑色背景。

import javax.swing.JFrame;

public class SimpleJFrameExample {
    public static void main(String[] args) {
        // Create a new JFrame
        JFrame frame = new JFrame();

        // Set the size of the JFrame
        frame.setSize(400, 300);

        // Make the JFrame visible
        frame.setVisible(true);

        // Set the default close operation when the user clicks the close button
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

字符串


的数据
正如你所看到的,有一个黑色的背景显示(在这种情况下,我',调整大小的权利)。
我发现:
这显然与承载JFrame的窗口的本机窗口装饰有关
从这个answer和它建议使用JFrame.setDefaultLookAndFeelDecorated(boolean)。我试了一下,得到了:



它的作品,并没有显示任何背景时,resuzing,但不是我想我的窗口看起来像,所以我想知道是否有一种方法来阻止这种影响,并保持正常的窗口装饰。


gzszwxb4

gzszwxb41#

我通过设置一个计时器来减少这个延迟,该计时器每隔100 ms重新验证和重绘一次组件:

resizeTimer = new Timer(100, (e) -> {
       imagePanel.updateScaledImage();
       revalidate();
       repaint();
});

字符串
还有更好的办法吗?
啊,我不会一直这样做,如果你是这样做的,那很贵。
很可能是updateScaledImage阻塞了事件调度线程,阻止了UI的绘制,直到它完成,或者是您的绘制例程在绘制过程中阻塞了事件调度线程,但这都是猜测,因为您没有提供可运行的示例。
如前所述,可能的原因是在代码中的某个地方,识别它是另一个问题。
我在过去所做的是使用“合并回调”与快速/低质量缩放方法相结合。
这意味着,在调整大小事件之间有足够的延迟之前,我将使用快速/低质量缩放方法将“某些内容”渲染到屏幕上。一旦在调整大小事件之间出现“足够的延迟”,我就会在后台线程中触发一个慢/高质量的缩放通道,最终用这个版本更新UI。
现在请记住,Swing是单线程的,不是线程安全的。这意味着您不应该在事件调度线程内执行阻塞或长时间运行的操作,也不应该从事件调度线程外更新UI(或UI所依赖的任何状态)。
为此,我创建了Swing Timer(用作我的“合并回调”工作流)和SwingWorker来执行高质量的缩放。
x1c 0d1x的数据

  • 备注:原始图像为1920 x1080。当组件使用“快速缩放”时,我也会应用AlphaComposite,这样您就可以看到“高质量”图像何时可用。*
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    BufferedImage image = ImageIO.read(getClass().getResource("/images/Mando01.jpeg"));
                    JFrame frame = new JFrame();
                    frame.add(new ImagePane(image));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }

    public class ImagePane extends JPanel {
        private Timer resizeTimer;
        private BufferedImage masterImage;
        private BufferedImage scaledImage;
        private Scaler scaler;

        public ImagePane(BufferedImage masterImage) {
            this.masterImage = masterImage;
            this.scaledImage = masterImage;

            scaler = new Scaler(masterImage);
            // This timer is "reset" each time the component is resized
            // This is an example of a "coalescing callback".  This means, rather
            // then responding to each resize event, which could cause a degradation
            // of the performance, we wait until no new events have occured with
            // a 250 millisecond period before we perform our action
            resizeTimer = new Timer(250, new ActionListener() {
                private Object currentToken;
                // Adapter so we can extract the scaled image from the worker
                private SwingWorkerAdapter<ResizeWorker> workerAdapter = new SwingWorkerAdapter<>() {
                    @Override
                    protected void workerDone(ResizeWorker worker) {
                        try {
                            // The important part is the concept of the token
                            // Because we can't "stop" the worker, we need some
                            // to determine if we're interested in the results or
                            // not.  This uses a token based approach, but you
                            // could simply remove the adapter from the previous
                            // worker and apply it to the new worker
                            if (!worker.getToken().equals(currentToken)) {
                                return;
                            }
                            scaledImage = worker.get();
                            repaint();
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                        } catch (ExecutionException ex) {
                            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                };

                @Override
                public void actionPerformed(ActionEvent e) {
                    // Just create something that is equatable and is relative
                    // unique within your context
                    currentToken = Instant.now();
                    // Offload the work to a background thread, so we can keep
                    // the UI responsive
                    ResizeWorker worker = new ResizeWorker(currentToken, scaler, getSize());
                    worker.addPropertyChangeListener(workerAdapter);
                    worker.execute();
                    repaint();
                }
            });
            resizeTimer.setRepeats(false);
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    scaledImage = null;
                    resizeTimer.restart();
                }
            });
            setLayout(new BorderLayout());
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(scaledImage.getWidth(), scaledImage.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            if (scaledImage != null) {
                int x = (getWidth() - scaledImage.getWidth()) / 2;
                int y = (getHeight() - scaledImage.getHeight()) / 2;
                g2d.drawImage(scaledImage, x, y, this);
            } else {
                Dimension scaleSize = scaler.getScaledSizeToFill(getSize());
                int x = (getWidth() - scaleSize.width) / 2;
                int y = (getHeight() - scaleSize.height) / 2;
                // I've just applied a alpha composite so I can see when we're
                // do a "quick" render
                g2d.setComposite(AlphaComposite.SrcOver.derive(0.1f));
                // This might not be the fastest method, but it is eaisly 
                // available
                g2d.drawImage(masterImage, x, y, scaleSize.width, scaleSize.height, this);
            }
            g2d.dispose();
        }
    }

    // This makes it easier to deal with the PropertyChangeListener support of
    // SwingWorker and allows you to focus on only the states you are interested
    // in
    public class SwingWorkerAdapter<W extends SwingWorker> implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            W worker = (W) evt.getSource();
            switch (evt.getPropertyName()) {
                case "state":
                    workerStateChanged(worker);
                    switch (worker.getState()) {
                        case STARTED:
                            workerStarted(worker);
                            break;
                        case DONE:
                            workerDone(worker);
                            break;
                    }
                    break;
                case "progress":
                    workerProgressUpdated(worker);
                    break;
            }
        }

        protected void workerProgressUpdated(W worker) {
        }

        protected void workerStateChanged(W worker) {
        }

        protected void workerStarted(W worker) {
        }

        protected void workerDone(W worker) {
        }

    }

    // This SwingWorker performs the "quality" scaling workflow, which might
    // take some time to execute, so we do it in the background
    public class ResizeWorker extends SwingWorker<BufferedImage, BufferedImage> {
        private Object token;
        private Scaler scaler;
        private Dimension targetSize;

        public ResizeWorker(Object token, Scaler scaler, Dimension targetSize) {
            this.token = token;
            this.scaler = scaler;
            this.targetSize = targetSize;
        }

        public Object getToken() {
            return token;
        }

        @Override
        protected BufferedImage doInBackground() throws Exception {
            BufferedImage scaledImage = scaler.getScaledInstanceToFill(targetSize);
            publish(scaledImage);
            return scaledImage;
        }
    }

    // Utility class that uses a "divide and conqure" approach to the scaling
    // of images, this generally produces a higher quality image over using
    // Image#getScaledInstance in a single step
    public class Scaler {
        private BufferedImage master;
        private Dimension masterSize;

        private Map<RenderingHints.Key, Object> renderingHints = new HashMap<>();

        public Scaler(BufferedImage master) {
            this.master = master;
            masterSize = new Dimension(master.getWidth(), master.getHeight());
            renderingHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            renderingHints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            renderingHints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            renderingHints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            renderingHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            renderingHints.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        }

        public BufferedImage getScaledInstanceToFit(Dimension size) {
            return getScaledInstance(getScaleFactorToFit(size));
        }

        public BufferedImage getScaledInstanceToFill(Dimension size) {
            return getScaledInstance(getScaleFactorToFill(size));
        }

        public Dimension getScaledSizeToFit(Dimension targetSize) {
            return getScaledSizeForScaleFactor(getScaleFactorToFit(targetSize));
        }

        public Dimension getScaledSizeToFill(Dimension targetSize) {
            return getScaledSizeForScaleFactor(getScaleFactorToFill(targetSize));
        }

        protected double getScaleFactor(int masterSize, int targetSize) {
            return (double) targetSize / (double) masterSize;
        }

        protected double getScaleFactorToFit(Dimension toFit) {
            double dScaleWidth = getScaleFactor(masterSize.width, toFit.width);
            double dScaleHeight = getScaleFactor(masterSize.height, toFit.height);
            return Math.min(dScaleHeight, dScaleWidth);
        }

        protected double getScaleFactorToFill(Dimension targetSize) {
            double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
            double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
            return Math.max(dScaleHeight, dScaleWidth);
        }

        protected Dimension getScaledSizeForScaleFactor(double scaleFactor) {
            int targetWidth = (int) Math.round(masterSize.getWidth() * scaleFactor);
            int targetHeight = (int) Math.round(masterSize.getHeight() * scaleFactor);
            return new Dimension(targetWidth, targetHeight);
        }

        protected BufferedImage getScaledInstance(double dScaleFactor) {
            BufferedImage imgScale = master;
            int targetWidth = (int) Math.round(masterSize.getWidth() * dScaleFactor);
            int targetHeight = (int) Math.round(masterSize.getHeight() * dScaleFactor);

            if (dScaleFactor <= 1.0d) {
                imgScale = getScaledDownInstance(targetWidth, targetHeight);
            } else {
                imgScale = getScaledUpInstance(targetWidth, targetHeight);
            }

            return imgScale;
        }

        protected BufferedImage getScaledDownInstance(
                int targetWidth,
                int targetHeight) {

            int type = (master.getTransparency() == Transparency.OPAQUE)
                    ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

            BufferedImage scaled = master;

            if (targetHeight > 0 || targetWidth > 0) {
                int width = master.getWidth();
                int height = master.getHeight();

                do {
                    if (width > targetWidth) {
                        width /= 2;
                        if (width < targetWidth) {
                            width = targetWidth;
                        }
                    }
                    if (height > targetHeight) {
                        height /= 2;
                        if (height < targetHeight) {
                            height = targetHeight;
                        }
                    }

                    BufferedImage tmp = new BufferedImage(Math.max(width, 1), Math.max(height, 1), type);
                    Graphics2D g2 = tmp.createGraphics();
                    g2.setRenderingHints(renderingHints);
                    g2.drawImage(scaled, 0, 0, width, height, null);
                    g2.dispose();

                    scaled = tmp;
                } while (width != targetWidth || height != targetHeight);
            } else {
                scaled = new BufferedImage(1, 1, type);
            }

            return scaled;
        }

        protected BufferedImage getScaledUpInstance(
                int targetWidth,
                int targetHeight) {

            int type = BufferedImage.TYPE_INT_ARGB;

            BufferedImage ret = (BufferedImage) master;
            int width = master.getWidth();
            int height = master.getHeight();

            do {
                if (width < targetWidth) {
                    width *= 2;
                    if (width > targetWidth) {
                        width = targetWidth;
                    }
                }

                if (height < targetHeight) {
                    height *= 2;
                    if (height > targetHeight) {
                        height = targetHeight;
                    }
                }

                BufferedImage tmp = new BufferedImage(width, height, type);
                Graphics2D g2 = tmp.createGraphics();
                g2.setRenderingHints(renderingHints);
                g2.drawImage(ret, 0, 0, width, height, null);
                g2.dispose();

                ret = tmp;
            } while (width != targetWidth || height != targetHeight);
            return ret;
        }
    }
}


请注意,这只是所描述方法的一种实现,您可能会找到更适合您自己需要的不同实现方式。

ujv3wf0j

ujv3wf0j2#

我确实找到了一个导致您所描述的行为的Toolkit属性:

import java.awt.*;
import javax.swing.*;

public class SimpleJFrameExample {
    public static void main(String[] args) {

        Toolkit.getDefaultToolkit().setDynamicLayout(false);

        // Create a new JFrame
        JFrame frame = new JFrame();

        // Set the size of the JFrame
        frame.setSize(400, 300);

        // Make the JFrame visible
        frame.setVisible(true);

        // Set the default close operation when the user clicks the close button
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

字符串
如果我把它改回“true”,问题就消失了:

//Toolkit.getDefaultToolkit().setDynamicLayout(false);
Toolkit.getDefaultToolkit().setDynamicLayout(true);


不知道在默认情况下如何或在何处将此属性设置为“false”。

相关问题