如何将仿射变换应用于组件的孩子?

时间:2021-12-27 11:16:47

Is it possible to apply transformations to custom or premade controls in Swing? By one hand transformations are allowed, by other hand there are probably some gaps in implementation with this.

是否可以在Swing中将转换应用于自定义或预制控件?一方面允许转换,另一方面,在实现中可能存在一些差距。

ATTENTION the question is about how to apply transfomations from control's parent, not about how to use transformations at all. I.e. transformation must be issued by the parent, while child should just obey it. So, please hint how to transform standard Swing controls or how to write custom controls which obey PARENT transformation.

注意问题是关于如何应用来自对照父母的transfomations,而不是如何使用转换。即转变必须由父母发布,而孩子应该遵守它。因此,请提示如何转换标准Swing控件或如何编写遵循PARENT转换的自定义控件。

Simple example which applies transform to Graphics before drawing childs and which doesn't work:

在绘制子项之前将变换应用于Graphics并且不起作用的简单示例:

public class Tester_TransformDuringPaint_01 {

private static Logger log = LoggerFactory.getLogger(Tester_TransformDuringPaint_01.class);  
private static class JPanelEx extends JPanel {
private AffineTransform transform = new AffineTransform();

    public AffineTransform getTransform() {
        return transform;
    }

    public void setTransform(AffineTransform transform) {
        this.transform = transform;
    }       

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        AffineTransform savedTransform = g2.getTransform();
        g2.transform(transform);
        super.paintComponent(g);            
        g2.drawOval(0, 0, 100, 100);            
        g2.setTransform(savedTransform);

    }

    @Override
    protected void paintChildren(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        AffineTransform savedTransform = g2.getTransform();
        g2.transform(transform);
        super.paintChildren(g);
        g2.setTransform(savedTransform);
    }
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {             
            JButton button = new JButton("Button");
            button.setBounds(0,20,100,60);              
            JPanelEx panel = new JPanelEx();
            panel.setLayout(null);
            panel.setBounds(10, 10, 640, 480);
            panel.setBackground(Color.PINK);
            panel.setTransform(AffineTransform.getScaleInstance(2, 1));
            panel.add(button);
            JFrameEx frame = new JFrameEx();
            frame.setLayout(null);
            frame.add(panel);               
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);               
            frame.setSize(0.5);
            frame.center();
            frame.setVisible(true);
        }
    });
}
}

draws the following:

得出以下结论:

如何将仿射变换应用于组件的孩子?

while left half of the button looks alive, and bigger part looks dead.

而按钮的左半部分看起来很活跃,而更大的部分看起来已经死了。

This is because different pars of API draws button with different approach.

这是因为API的不同部分使用不同的方法绘制按钮。

Modified O'Reilly hack 51

修改了O'Reilly黑客51

Below is the code based on @lbalazscs's example, which shows that transformations don't work even if they are "in bounds"

下面是基于@ lbalazscs示例的代码,该示例表明即使转换是“在边界内”,转换也不起作用

public class BackwardsJButton extends JButton {

public BackwardsJButton(String text) {
    super(text);
}

public void paint(Graphics g) {
    if (g instanceof Graphics2D) {
        Graphics2D g2 = (Graphics2D) g;
        AffineTransform savedTransform = g2.getTransform();
        AffineTransform flipTrans = new AffineTransform();
        double widthD = (double) getWidth();
        //flipTrans.setToTranslation(widthD, 0);
        //flipTrans.scale(-2.0, 1);
        flipTrans.scale(0.5, 1);
        g2.transform(flipTrans);
        super.paint(g);
        g2.setTransform(savedTransform);
    } else {
        super.paint(g);
    }
}

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            buildFrame();
        }
    });
}

private static void buildFrame() {
    JFrame f = new JFrame("Test");
    f.setLayout(new FlowLayout());
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    f.add(new BackwardsJButton("BackwardsJLabel"));

    f.pack();
    f.setLocationRelativeTo(null);
    f.setVisible(true);
}
}

output follows (you may need to resize window and move mouse to see it, because Swing bug is located inside mouse hover code:

输出如下(您可能需要调整窗口大小并移动鼠标才能看到它,因为Swing bug位于鼠标悬停代码中:

如何将仿射变换应用于组件的孩子?

1 个解决方案

#1


3  

You have some innovative ideas how to abuse Swing :)

你有一些创新的想法如何滥用Swing :)

It is possible to apply affine transform while drawing components, but only if you are happy within the bounds of the component(for example you could mirror the text). If you override paint, you change how the component is drawn, but this will not change its size because the size depends on completely other variables, and you still cannot draw reliably outside its bounds.

在绘制组件时可以应用仿射变换,但前提是您在组件的范围内感到满意(例如,您可以镜像文本)。如果覆盖绘制,则更改组件的绘制方式,但这不会更改其大小,因为大小完全取决于其他变量,并且仍然无法在其边界外可靠绘制。

I think it is not a good idea to transform the painting of premade components, because even if you succeed graphically, mouse clicks will be expected in the original places.

我认为改造预制组件的绘画并不是一个好主意,因为即使你以图形方式成功,也会在原始位置点击鼠标。

Note that you need to reset the transformation after you are done, because the same Graphics object will be used to paint other components.

请注意,完成后需要重置转换,因为相同的Graphics对象将用于绘制其他组件。

AffineTransform savedTransform = g.getTransform();
g.setTransform(specialTransform);
... your drawing here
g.setTransform(savedTransform);

EDIT: here is a complete running example of a transformed component

编辑:这是一个转换组件的完整运行示例

import javax.swing.*;
import javax.swing.plaf.metal.MetalButtonUI;
import java.awt.*;
import java.awt.geom.AffineTransform;

public class ScaledButton extends JButton {

    public ScaledButton(String text) {
        super(text);
    }

    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        Color savedColor = g2.getColor();
        g2.setColor(getBackground());
        g2.fill(new Rectangle(0, 0, getWidth(), getHeight()));
        g2.setColor(savedColor);

        AffineTransform backup = g2.getTransform();
        g2.scale(0.5, 1);
        super.paintComponent(g);
        g2.setTransform(backup);
    }

    @Override
    protected void paintBorder(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        AffineTransform backup = g2.getTransform();
        g2.scale(0.5, 1);
        super.paintBorder(g);
        g2.setTransform(backup);
    }



    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                buildFrame();
            }
        });
    }

    private static void buildFrame() {
        JFrame f = new JFrame("Test");
        f.setLayout(new FlowLayout());
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        f.add(new ScaledButton("ScaledButton"));

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

#1


3  

You have some innovative ideas how to abuse Swing :)

你有一些创新的想法如何滥用Swing :)

It is possible to apply affine transform while drawing components, but only if you are happy within the bounds of the component(for example you could mirror the text). If you override paint, you change how the component is drawn, but this will not change its size because the size depends on completely other variables, and you still cannot draw reliably outside its bounds.

在绘制组件时可以应用仿射变换,但前提是您在组件的范围内感到满意(例如,您可以镜像文本)。如果覆盖绘制,则更改组件的绘制方式,但这不会更改其大小,因为大小完全取决于其他变量,并且仍然无法在其边界外可靠绘制。

I think it is not a good idea to transform the painting of premade components, because even if you succeed graphically, mouse clicks will be expected in the original places.

我认为改造预制组件的绘画并不是一个好主意,因为即使你以图形方式成功,也会在原始位置点击鼠标。

Note that you need to reset the transformation after you are done, because the same Graphics object will be used to paint other components.

请注意,完成后需要重置转换,因为相同的Graphics对象将用于绘制其他组件。

AffineTransform savedTransform = g.getTransform();
g.setTransform(specialTransform);
... your drawing here
g.setTransform(savedTransform);

EDIT: here is a complete running example of a transformed component

编辑:这是一个转换组件的完整运行示例

import javax.swing.*;
import javax.swing.plaf.metal.MetalButtonUI;
import java.awt.*;
import java.awt.geom.AffineTransform;

public class ScaledButton extends JButton {

    public ScaledButton(String text) {
        super(text);
    }

    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        Color savedColor = g2.getColor();
        g2.setColor(getBackground());
        g2.fill(new Rectangle(0, 0, getWidth(), getHeight()));
        g2.setColor(savedColor);

        AffineTransform backup = g2.getTransform();
        g2.scale(0.5, 1);
        super.paintComponent(g);
        g2.setTransform(backup);
    }

    @Override
    protected void paintBorder(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        AffineTransform backup = g2.getTransform();
        g2.scale(0.5, 1);
        super.paintBorder(g);
        g2.setTransform(backup);
    }



    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                buildFrame();
            }
        });
    }

    private static void buildFrame() {
        JFrame f = new JFrame("Test");
        f.setLayout(new FlowLayout());
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        f.add(new ScaledButton("ScaledButton"));

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}