Масштабирование формы с размером кадра - PullRequest
0 голосов
/ 02 мая 2020

Я новичок в Java и играю с простым примером GUI. Получив хороший ответ на мой последний вопрос , я попытался сделать еще один шаг вперед и масштабировать фигуру в соответствии с размером кадра (с фиксированным соотношением сторон). Действительно, когда я изменяю размер окна, форма масштабируется вместе с ним. Тем не менее, форма никогда не заполняет рамку до максимума, всегда есть много места справа и снизу. Вот код:

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). Я предполагаю, что, хотя сам расчет правильный, я применяю масштабный коэффициент в неправильном месте. Любая помощь с этим будет оценена.

РЕДАКТИРОВАТЬ: Вот скриншот окна, которое я получаю, чтобы прояснить проблему:

shape with large margins at the right and at the bottom

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 size 2000x500

Вот как выглядит результат с shape.setRect(0, 0, 800, 500):

Shape size 2000x800

Если я заменим return minFactor на return 1.0 (и остаться с shape.setRect(0, 0, 2000, 500)), я получаю этот результат:

Shape size 2000x500, scaling factor 1.0

Если я заменю return minFactor на return 0.9 (и останусь с shape.setRect(0, 0, 2000, 500)), я получаю этот результат:

enter image description here

Если я заменю return minFactor на return 0.9 и изменится на shape.setRect(0, 0, 3000, 500), я получу тот же видимый результат, что и с return 1.0 и shape.setRect(0, 0, 2000, 500).

1 Ответ

2 голосов
/ 02 мая 2020

всегда есть много места справа и снизу.

У меня отлично работает.

То есть дополнительного пространства нет, если соотношение сторон кадра такое же, как у прямоугольника.

Если вы хотите, чтобы прямоугольник всегда заполнял доступное пространство, то вам понадобится отдельный коэффициент масштабирования для ширины и высоты.

Другие комментарии:

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;

Edit2:

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

Цель вызова super.paintComponent () - очистить фон перед выполнением пользовательской рисования, чтобы сделать уверен, что у вас нет артефактов живописи. Обычно это делается как первое утверждение в методе. Я не уверен, почему вы применяете преобразование к графике перед вызовом метода.

Поэтому я хотел бы предложить следующий код:

    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:

Затем я попытался скомпилировать и выполнить ваш код через командную строку, и пробел исчез.

Я также использую командную строку для компиляции / тестирования, что заставило меня задуматься над другим вопросом Я недавно прокомментировал: Как сделать изображение не размытым в Java

Конечно, это связанная проблема.

Когда я использую:

java DrawTest2

Я вижу желаемое поведение. Однако когда я использую:

java -Dsun.java2d.uiScale=1.5 DrawTest2

, я вижу поведение, которое вы описываете. Так что это заставит меня поверить, что Eclipse делает нечто подобное.

Однако я не знаю, как решить проблему, чтобы рисование выполнялось правильно, независимо от того, используется ли масштабирование.

Редактировать 4:

Только что нашел решение. Графический объект уже содержит масштабный коэффициент для приложения, и вы сбрасываете коэффициент. Вместо этого вам нужно отрегулировать коэффициент:

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