【问题标题】:How to apply affine transform to component's childs?如何将仿射变换应用于组件的孩子?
【发布时间】:2013-02-22 22:00:27
【问题描述】:

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

注意问题是关于如何从控件的父级应用转换,而不是关于如何使用转换。 IE。转换必须由父母发出,而孩子应该服从它。所以,请提示如何转换标准的 Swing 控件或如何编写遵循 PARENT 转换的自定义控件。

在绘制子元素之前将变换应用于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);
        }
    });
}
}

绘制以下内容:

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

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

修改后的 O'Reilly hack 51

以下是基于@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);
}
}

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

【问题讨论】:

  • 如果你想用这种方式转换组件,你应该看看JXLayer
  • 你想达到什么目的?
  • 我想实现我所写的。
  • 你必须有一个更高的理由来释放地狱中的所有恶魔 ;-) 特别是:从控制的父级应用转换 闻起来像对感知解决方案的症状进行篡改(而不是说明基本目标并从那里开始) - 它正在与 Swing 的风作斗争,而不是漂浮在上面。
  • 我想要自定义图形编辑器之类的东西,但不完全是。我需要一些可重用的组件(例如扩展的JPanel),我可以在上面放置我自己的几何图形,例如绘图或形状。我在想所有这一切都可以通过扩展面板和组件来实现,但是 Swing 的风会抵抗。实际上,他们总是倾向于我创建自己的单个组件,该组件手动完成所有操作,即 Swing 风试图将我完全吹出 Swing。不幸的是,我不能在这里要求 Swing 替代品,因为我可以想象会发生仇恨的火山:D

标签: java swing affinetransform


【解决方案1】:

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

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

我认为对预制组件的绘画进行改造不是一个好主意,因为即使你在图形上成功了,鼠标点击也会在原来的地方出现。

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

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

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

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);
    }
}

【讨论】:

  • 是的,我知道我应该将转换恢复到原始状态。但是如果想使用转换呢?
  • 我已经按照您的建议写了代码,以使代码更“礼貌”。但问题仍然存在。
  • 现在g2.setTransform(savedTransform); 我只看到一个(小)按钮,这是合乎逻辑的,因为按钮绘画没有任何转换。以前,按钮被缩放(它使用相同的 Graphics 对象)只是一个意外。请注意,您没有包含 JFrameEx 的源代码,所以我使用了一个简单的 JFrame。
  • 好吧,如果你重写了paint,你改变了组件的绘制方式,但这不会改变它的大小,因为大小完全取决于其他变量,你仍然不能可靠地在它的范围之外绘制。顺便说一句,您不需要 center() 方法,f.setLocationRelativeTo(null); 也像我的示例一样将框架居中。
  • 好的,我改变了我的例子,包括一个 (0.5, 1) 缩放,通过覆盖paintBorder 以及清除paintComponent 中的背景。当然,按钮会在其整个范围内响应鼠标点击,正如我在一开始就警告过的那样。我认为是时候接受这个答案了,并在单独的问题中提出其他问题:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
相关资源
最近更新 更多