【问题标题】:Scaling shape with frame size随帧大小缩放形状
【发布时间】:2020-05-02 15:02:01
【问题描述】:

我是 Java 新手,我正在玩一个简单的 GUI 示例。在收到对我的last question 的良好答复后,我尝试更进一步,并根据帧大小(固定纵横比)缩放形状。事实上,当我改变窗口大小时,形状会随之缩放。但是,形状永远不会最大程度地填充框架,右侧和底部边缘总是有相当多的空间。代码如下:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class DrawTest2 {

    class DrawingPanel extends JPanel {

        private Rectangle2D shape;
        private JFrame frame;

        public DrawingPanel(Rectangle2D shape, JFrame frame) {
            this.shape = shape;
            this.frame = frame;
        }

        public void paintComponent(Graphics g) {

            Graphics2D g2D = (Graphics2D) g;                
            double scaleFactor = calcScaleFactor();

            AffineTransform oldAt = g2D.getTransform();
            AffineTransform tx1 = new AffineTransform();
            tx1.scale(scaleFactor, scaleFactor);

            super.paintComponent(g2D);  

            g2D.setTransform(tx1);
            g2D.transform(AffineTransform.getTranslateInstance(3, 3)); 

            g2D.setColor(new Color(31, 21, 1));
            g2D.fill(shape);
            g2D.draw(shape);
            g2D.setTransform(oldAt);

        }

        @Override
        public Dimension getPreferredSize() {
            Dimension dimension = new Dimension((int)shape.getWidth(), (int)shape.getHeight());
            System.out.println("Dimension: " + dimension); // 2000 x 500
            return dimension;
        }

        private double calcScaleFactor() {

            double maxX = shape.getMaxX();
            double maxY = shape.getMaxY();

            double widthPanel = frame.getContentPane().getSize().getWidth();
            double heightPanel = frame.getContentPane().getSize().getHeight();
            double minFactor;

            if (widthPanel < maxX || heightPanel < maxY) {
                double factorX = widthPanel/maxX;
                double factorY = heightPanel/maxY;
                minFactor = factorX < factorY ? factorX : factorY;
            } else {
                minFactor = 1.0;
            }

            System.out.println("widthPanel: " + widthPanel); // 1283
            System.out.println("heightPanel: " + heightPanel); // 500
            System.out.println("widthFrame: " + frame.getSize().getWidth()); // 1297
            System.out.println("heightFrame: " + frame.getSize().getHeight()); // 537
            System.out.println("maxX: " + maxX); // 2000
            System.out.println("maxY: " + maxY); // 500
            System.out.println("minFactor:" + minFactor); // 0.6415

            return minFactor; 
        }
    }


    public void draw() {
        JFrame frame = new JFrame();
        Rectangle2D shape = new Rectangle2D.Float();
        shape.setRect(0, 0, 2000, 500);
        final DrawingPanel drawing = new DrawingPanel(shape, frame);
        frame.getContentPane().add(drawing, BorderLayout.CENTER);
        frame.pack();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);  

    }
}

public class DrawMain {
    public static void main(String[] args) {
        DrawTest2 test = new DrawTest2();
        test.draw();

    }
}

比例因子似乎没问题 (2000 * 0.6415 = 1283)。我的猜测是,虽然计算本身是正确的,但我在错误的地方应用了比例因子。对此的任何帮助将不胜感激。

编辑:这是我让问题更清楚的窗口截图:

EDIT2:这是更新后的代码(稍微重命名):

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

public class DrawTest2 {

class DrawingPanel extends JPanel {

        private Rectangle2D shape;

        public DrawingPanel(Rectangle2D shape) {
            this.shape = shape;
        }

        public void paintComponent(Graphics g) {

            Graphics2D g2D = (Graphics2D) g;                
            double scaleFactor = calcScaleFactor();

            AffineTransform oldAt = g2D.getTransform();
            AffineTransform tx1 = new AffineTransform();
            tx1.scale(scaleFactor, scaleFactor);

            super.paintComponent(g2D);  

            g2D.setTransform(tx1);

            g2D.setColor(new Color(31, 21, 1));
            g2D.fill(shape);
            g2D.draw(shape);
            g2D.setTransform(oldAt);

        }

        @Override
        public Dimension getPreferredSize() {
            Dimension dimension = new Dimension((int)shape.getWidth(), (int)shape.getHeight());
            return dimension;
        }

        private double calcScaleFactor() {

            double maxX = shape.getMaxX();
            double maxY = shape.getMaxY();

            double widthPanel = getWidth();
            double heightPanel = getHeight();
            double minFactor;

            double factorX = widthPanel/maxX;
            double factorY = heightPanel/maxY;
            minFactor = factorX < factorY ? factorX : factorY;

            return minFactor; 
        }
    }


    public void draw() {
        JFrame frame = new JFrame();
        Rectangle2D shape = new Rectangle2D.Float();
        shape.setRect(0, 0, 800, 500);
        final DrawingPanel drawing = new DrawingPanel(shape);
        frame.getContentPane().add(drawing, BorderLayout.CENTER);
        frame.pack();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);

    }

    public static void main(String[] args) {
        DrawTest2 test = new DrawTest2();
        test.draw();

    }
}

这是shape.setRect(0, 0, 2000, 500) 的结果:

这是shape.setRect(0, 0, 800, 500) 的结果:

如果我将return minFactor 替换为return 1.0(并保留shape.setRect(0, 0, 2000, 500)),我会得到以下结果:

如果我将return minFactor 替换为return 0.9(并保留shape.setRect(0, 0, 2000, 500)),我会得到以下结果:

如果我将return minFactor 替换为return 0.9 并更改为shape.setRect(0, 0, 3000, 500),我会得到与return 1.0 和shape.setRect(0, 0, 2000, 500) 相同的可见结果。

【问题讨论】:

    标签: java swing


    【解决方案1】:

    在右边距和下边距总是有相当大的空间。

    对我来说很好用。

    也就是说,只要框架的纵横比与矩形的纵横比相同,就没有多余的空间。

    如果您希望矩形始终填充可用空间,那么您需要一个单独的宽度和高度缩放因子。

    其他cmets:

    g2D.transform(AffineTransform.getTranslateInstance(3, 3)); 
    

    我不确定您为什么要使用翻译,但如果您这样做了,那么您的首选尺寸计算是错误的。您需要在宽度和高度上都加 3,否则当您以首选尺寸绘制组件时,将切断 3 个像素。

    double widthPanel = frame.getContentPane().getSize().getWidth();
    double heightPanel = frame.getContentPane().getSize().getHeight();
    

    不要引用内容窗格或框架。面板的自定义绘画基于面板的大小。此外,面板可能并不总是直接添加到内容窗格中。

    所以代码应该是:

    double widthPanel = getWidth();
    double heightPanel = getHeight();
    

    此外,由于您不需要框架,因此面板的构造函数应该是:

    //public DrawingPanel(Rectangle2D shape, JFrame frame) {
    public DrawingPanel(Rectangle2D shape) {
    

    在您的应用程序中,如果您确实需要访问可以使用的框架:

    Window window = SwingUtilities.windowForComponent(…);
    

    编辑:

    您有条件地设置“minFactor”:

    if (widthPanel < maxX || heightPanel < maxY) {
        double factorX = widthPanel/maxX;
        double factorY = heightPanel/maxY;
        minFactor = factorX < factorY ? factorX : factorY;
    } else {
        minFactor = 1.0;
    

    如果您总是希望保留纵横比,那么请无条件地这样做:

    double factorX = widthPanel/maxX;
    double factorY = heightPanel/maxY;
    minFactor = factorX < factorY ? factorX : factorY;
    

    编辑2:

    Graphics2D g2D = (Graphics2D) g;
    double scaleFactor = calcScaleFactor();
    AffineTransform oldAt = g2D.getTransform();
    AffineTransform tx1 = new AffineTransform();
    tx1.scale(scaleFactor, scaleFactor);
    super.paintComponent(g2D);
    

    调用 super.paintComponent() 的目的是在进行自定义绘画之前清除背景,以确保您没有绘画伪影。通常这是作为方法中的第一条语句完成的。我不确定您为什么在调用该方法之前对 Graphics 应用转换。

    所以我建议代码应该是:

        public void paintComponent(Graphics g) {
    
            super.paintComponent(g);
    
            Graphics2D g2D = (Graphics2D) g;
            double scaleFactor = calcScaleFactor();
    
            AffineTransform oldAt = g2D.getTransform();
            AffineTransform tx1 = new AffineTransform();
            tx1.scale(scaleFactor, scaleFactor);
    

    编辑 3:

    然后我尝试通过命令提示符编译和执行你的代码,差距就消失了。

    我还使用命令行编译/测试,这让我想到了我最近评论的另一个问题:How to make a picture not blurry in Java

    果然这是一个相关的问题。

    当我使用时:

    java DrawTest2
    

    我看到了想要的行为。但是当我使用时:

    java -Dsun.java2d.uiScale=1.5 DrawTest2
    

    我看到了你描述的行为。所以这会让我相信 Eclipse 正在做类似的事情。

    但是,我不知道如何解决这个问题,以便无论是否使用缩放都能正确完成绘画。

    编辑 4:

    刚刚找到解决方案。 Graphics 对象已包含应用程序的缩放因子,您正在重置该因子。相反,您需要调整因子:

        tx1.concatenate( g2D.getTransform() );
        tx1.scale(scaleFactor, scaleFactor);
    

    【讨论】:

    • 感谢您的回复和您的 cmets。我添加了一个屏幕截图作为我在屏幕上看到的示例。由于形状的缩放是通过保持固定的纵横比完成的,我希望矩形始终填充窗口的整个宽度或整个高度(或者如果框架的纵横比与纵横比相同,则两者都填满)矩形)。但是,矩形的右侧和底部总是有很多可用空间。
    • 很遗憾,去掉条件并不能解决问题,右边和底部的空闲空间还在。
    • @AnjaM,好吧,我已经使用您的原始代码尝试了它,使用的尺寸为 (2000, 500),它比我的显示器更宽,尺寸为 (800, 500) 适合我的显示器。在所有情况下,黑色矩形总是被绘制到其中一个边缘,并且两个边缘都具有适当的纵横比。确保在编译之前已删除所有旧的类文件。发布您的更新代码,展示您如何实施我的建议。
    • 在我的原始帖子中添加了代码和屏幕截图。我从头开始创建了一个新项目并将此代码复制到其中以生成屏幕截图。
    • 我对代码所做的唯一区别是我将 main() 方法移到了 DrawTest 类中,因此我只有一个源文件。我编辑了您的代码以发布我编译和测试的代码。新代码对我来说仍然可以正常工作。那是无论我如何调整框架的大小,矩形的至少一个边缘将被绘制到框架的边缘。我从来没有同时在右侧和底部边缘看到间隙。我不确定你为什么会看到不同的东西。请参阅我对您可以添加的代码的另一个小改动的编辑。
    猜你喜欢
    • 2014-08-03
    • 2022-01-26
    • 2021-01-25
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-27
    相关资源
    最近更新 更多