首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用AffineTransform缩放图形

用AffineTransform缩放图形
EN

Stack Overflow用户
提问于 2015-08-26 02:13:16
回答 2查看 3.3K关注 0票数 2

我正在用Swing制作一个GUI,它使用AffineTransform来缩放绘制在JInternalFrame上的Graphics2D对象。问题是,在目前的状态下,它是有问题的,我不知道为什么。

为什么我的代码没有正确地缩放?为什么图形“跳转”到面板顶部的调整尺寸?

下面是我自己的例子:

代码语言:javascript
复制
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.util.*;

public class MainPanel extends JFrame implements ActionListener{

    private static final double version = 1.0;
    private JDesktopPane desktop;
    public static RFInternalFrame frame;

    private java.util.List<Point> POINT_LIST = Arrays.asList(
            //Top Row
            new Point(50, 30),
            new Point(70, 30),
            new Point(90, 30),
            new Point(110, 30),
            new Point(130, 30),
            new Point(150, 30),
            new Point(170, 30),
            new Point(190, 30),
            new Point(210, 30),
            new Point(230, 30),

            //Circle of Radios
            new Point(140, 60),
            new Point(120, 80),
            new Point(100, 100),
            new Point(100, 120),
            new Point(120, 140),
            new Point(140, 160),
            new Point(160, 140),
            new Point(180, 120),
            new Point(180, 100),
            new Point(160, 80));

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        JFrame frame = new MainPanel();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLocationByPlatform(false);
        frame.setVisible(true);
    }

    public MainPanel() {
        super("MainPanel " + version);

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset,
                screenSize.width - inset * 7,
                screenSize.height - inset * 4);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        desktop.setBackground(Color.DARK_GRAY);

        createRFFrame(); //create first RFFrame
        createScenarioFrame(); //create ScenarioFrame

        setContentPane(desktop);
        setJMenuBar(createMenuBar());

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
    }

    protected JMenuBar createMenuBar() {
        JMenuBar menuBar = new JMenuBar();

        //Set up the lone menu.
        JMenu menu = new JMenu("File");
        menu.setMnemonic(KeyEvent.VK_D);
        menuBar.add(menu);

        //Set up the first menu item.
        JMenuItem menuItem = new JMenuItem("Add Panel");
        menuItem.setMnemonic(KeyEvent.VK_N);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_N, ActionEvent.ALT_MASK));
        menuItem.setActionCommand("new");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        //Set up the second menu item.
        menuItem = new JMenuItem("Quit");
        menuItem.setMnemonic(KeyEvent.VK_Q);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_Q, ActionEvent.ALT_MASK));
        menuItem.setActionCommand("quit");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        return menuBar;
    }

    //React to menu selections.
    public void actionPerformed(ActionEvent e) {
        if ("new".equals(e.getActionCommand())) { //new
            createRFFrame();
        } else {
            //quit
            quit();
        }
    }

    /*
     * ActivateAllAction activates all radios on the panel, essentially changes the color
     * of each ellipse from INACTIVE to ACTIVE
     */
    private class ActivateAllAction extends AbstractAction {
        public ActivateAllAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(1);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        /*
         * This will find the selected tab and extract the DrawEllipses instance from it
         * Then for the actionPerformed it will call activateAll() from DrawEllipses
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            Component comp = desktop.getSelectedFrame();
            if (comp instanceof DrawEllipses){
                DrawEllipses desktop = (DrawEllipses) comp;
                desktop.activateAll();
            }
        }
    }

    /*
     * DeactivateAllAction deactivates all radios on the panel, essentially changes the color
     * of each ellipse from ACTIVE to INACTIVE
     */
    private class DeactivateAllAction extends AbstractAction {
        public DeactivateAllAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        /*
         * This will find the selected tab and extract the DrawPanel2 instance from it
         * Then for the actionPerformed it will call activateAll() from DrawEllipses
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            Component comp = desktop.getSelectedFrame();
            if (comp instanceof DrawEllipses){
                DrawEllipses desktop = (DrawEllipses) comp;
                desktop.deactivateAll();
            }
        }
    }

    /*
     * Define a JPanel that will hold the activate and deactivate all JButtons
     */
    protected JPanel btnPanel() {
        JPanel btnPanel = new JPanel();

        btnPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());

        //Set the layout of the frame to a grid bag layout
        btnPanel.setLayout(new GridBagLayout());

        //Creates constraints variable to hold values to be applied to each aspect of the layout
        GridBagConstraints c = new GridBagConstraints();

        //Column 1
        c.gridx = 0;
        btnPanel.add(new JButton(new ActivateAllAction("Activate All")));

        //Column 2
        c.gridx = 1;
        btnPanel.add(new JButton(new DeactivateAllAction("Deactivate All")));
        return btnPanel;
    }

    //not used currently
    protected JPanel drawPanel() {
        JPanel drawPanel = new JPanel();
        drawPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());
        DrawEllipses drawEllipses = new DrawEllipses(POINT_LIST);
        drawPanel.add(drawEllipses);

        return drawPanel;

    }

    //Create a new internal frame.
    protected void createRFFrame() {
        RFInternalFrame iframe = new RFInternalFrame();
        iframe.setLayout(new BorderLayout());

        DrawEllipses drawEllipses = new DrawEllipses(POINT_LIST);
        iframe.add(drawEllipses);
        iframe.add(btnPanel(), BorderLayout.SOUTH);

        iframe.setVisible(true);
        desktop.add(iframe);

        try {
            iframe.setSelected(true);
        } catch (java.beans.PropertyVetoException e) {}
    }

    protected void createScenarioFrame() {
        ScenarioInternalFrame frame = new ScenarioInternalFrame();
        frame.setLayout(new BorderLayout());

        frame.setVisible(true);
        desktop.add(frame);

        try {
            frame.setSelected(true);
        } catch (java.beans.PropertyVetoException e) {}
    }

    //Quit the application.
    protected void quit() {
        System.exit(0);
    }

}

@SuppressWarnings("serial")
class DrawEllipses extends JPanel {
    private double translateX; //
    private double translateY; //
    protected static double scale; //
    private static final int OVAL_WIDTH = 15;
    private static final Color INACTIVE_COLOR = Color.RED;
    private static final Color ACTIVE_COLOR = Color.green;
    private java.util.List<Point> points; //
    private java.util.List<Ellipse2D> ellipses = new ArrayList<>();
    private Map<Ellipse2D, Color> ellipseColorMap = new HashMap<>();

    public DrawEllipses(java.util.List<Point> points) {
        this.points = points; //
        translateX = 0; //
        translateY = 0; //
        scale = 1; //
        setOpaque(true); //
        setDoubleBuffered(true); //


        for (Point p : points) {
            int x = p.x - OVAL_WIDTH / 2;
            int y = p.y - OVAL_WIDTH / 2;
            int w = OVAL_WIDTH;
            int h = OVAL_WIDTH;
            Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
            ellipses.add(ellipse);
            ellipseColorMap.put(ellipse, INACTIVE_COLOR);
        }

        MyMouseAdapter mListener = new MyMouseAdapter();
        addMouseListener(mListener);
        addMouseMotionListener(mListener);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        AffineTransform tx = new AffineTransform(); //
        tx.translate(translateX, translateY); //
        tx.scale(scale, scale); //

        Graphics2D g2 = (Graphics2D) g;
        g2.setTransform(tx);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        for (Ellipse2D ellipse : ellipses) {
            g2.setColor(ellipseColorMap.get(ellipse));
            g2.fill(ellipse);
        }
    }

    private class MyMouseAdapter extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            for (Ellipse2D ellipse : ellipses) {
                if (ellipse.contains(e.getPoint())) {
                    Color c = ellipseColorMap.get(ellipse);
                    c =  (c == INACTIVE_COLOR) ? ACTIVE_COLOR : INACTIVE_COLOR;
                    ellipseColorMap.put(ellipse, c);
                }
            }
            repaint();
        }
    }

    //Used for button click action to change all ellipses to ACTIVE_COLOR
    public void activateAll(){
        for (Ellipse2D ellipse : ellipses){
            ellipseColorMap.put(ellipse, ACTIVE_COLOR);
        }
        repaint();
    }

    //Used for button click action to change all ellipses to INACTIVE_COLOR
    public void deactivateAll(){
        for (Ellipse2D ellipse : ellipses){
            ellipseColorMap.put(ellipse, INACTIVE_COLOR);
        }
        repaint();
    }
}

class RFInternalFrame extends JInternalFrame implements ComponentListener {
    protected static double scale = 1; //
    static int openFrameCount = 0;
    static final int xOffset = 300, yOffset = 0;

    public RFInternalFrame() {
        super("RF Panel #" + (++openFrameCount),
                true, //resizable
                true, //closable
                true, //maximizable
                true);//iconifiable

        setSize(300, 300);
        setMinimumSize(new Dimension(300, 300));
        addComponentListener(this);

        if (openFrameCount == 1) {

            setLocation(0,0);
        }
        else if (openFrameCount <= 4) {

            //Set the window's location.
            setLocation(xOffset * (openFrameCount - 1), yOffset * (openFrameCount - 1));
        }
        else if (openFrameCount == 5) {

            setLocation(xOffset - 300, yOffset + 300);
        }
        else if (openFrameCount == 6) {

            setLocation(xOffset + 600, yOffset + 300);
        }
    }

    @Override
    public void componentResized(ComponentEvent e) {
        String str = "";
        if (getWidth() < 300) {
            str = "0." + getWidth();
        } else {
            str = "1." + (getWidth() - 300);
            System.out.println(getWidth() - 300);
        }
        double dou = Double.parseDouble(str);
        MainPanel.frame.scale = dou;
        repaint();
    }

    @Override
    public void componentMoved(ComponentEvent componentEvent) {

    }

    @Override
    public void componentShown(ComponentEvent componentEvent) {

    }

    @Override
    public void componentHidden(ComponentEvent componentEvent) {

    }
}

class ScenarioInternalFrame extends JInternalFrame {
    static int openFrameCount = 0;
    static final int xOffset = 300, yOffset = 300;

    public ScenarioInternalFrame() {
        super("Test Scenario" + (++openFrameCount),
                true, //resizable
                true, //closable
                true, //maximizable
                true);//iconifiable

        //...Create the GUI and put it in the window...

        //...Then set the window size or call pack...
        setSize(600, 300);

        //Set the window's location.
        setLocation(xOffset, yOffset);
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-08-26 03:09:33

据我所知,Graphics对象已经包含一个转换,该转换用于计算内部框架标题栏的高度。当您替换转换时,您将丢失此转换,因此您的代码将绘制在标题栏下的框架顶部。

  1. 不要更改传递给paintComponent()方法的Graphics对象的属性。相反,创建一个可以自定义的Graphics2D对象。
  2. 创建新转换时,需要先应用现有转换,然后再添加新转换。

基本结构如下:

代码语言:javascript
复制
super.paintComponent(g);

Graphics2D g2 = (Graphics2D)g.create();

AffineTransform tx = new AffineTransform(); //
tx.concatenate( g2.getTransform() );
tx.scale(...); 
g2.setTransform(tx);

// do custom painting

g2.dispose(); // release Graphics resources

这只会对画有所帮助。您仍然有几个问题(我无法解决):

  1. 您的缩放值永远不会更新。应该将ComponentListener添加到DrawEllipse面板中。您可能希望在面板中创建一个setScale()方法,在调整面板大小时调用该方法来设置缩放。
  2. 一旦你画了缩放的圆圈,你的MouseListener就不能工作了。所有圆圈的位置都会不同,因为它们已经被缩放了。您可能能够在遍历圆圈列表时缩放每个圆圈。

另外,当您有问题时,发布一个适当的SSCCE来演示问题。关于在面板上使用转换,您有一个简单的问题。所以用一个面板创建一个框架,并在面板上画几个圆圈来测试这个概念。

所有其他代码都与问题无关。菜单项无关,第二个内部框架不相关。MouseListener单击代码与此无关。我们没有时间去阅读100行代码来理解这个问题。

编辑:

我改变了密码的顺序。Tx.scale(.)方法之前,必须在将转换设置为Graphics对象之前调用。

票数 5
EN

Stack Overflow用户

发布于 2015-08-26 03:44:52

我的经验是,画在秋千上将用双缓冲完成。意味着创建绘图缓冲区(即。ImageBuffer)。将所有绘图逻辑应用于绘图缓冲区的图形,包括转换,然后将缓冲区绘制到组件的图形中。

我就是这样解决你的问题的。

代码语言:javascript
复制
class DrawEllipses extends JComponent { // I change from JPanel to JComponent, this might not be necessary though...
...
...
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    // create the drawing buffer.
    BufferedImage bi = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics big = bi.getGraphics();

    // prepare transform
    AffineTransform tx = new AffineTransform(); //
    tx.translate(translateX, translateY); //
    tx.scale(scale, scale); //

    // get the buffer graphics and paint the background white.
    Graphics2D g2 = (Graphics2D) big;
    g2.setColor(Color.WHITE);
    g2.fillRect(0, 0, this.getWidth(), this.getHeight());

    // apply drawing logic to the Graphics of the buffer
    g2.setTransform(tx);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    for (Ellipse2D ellipse : ellipses) {
        g2.setColor(ellipseColorMap.get(ellipse));
        g2.fill(ellipse);
    }

    // finally, draw the buffer to the component graphics.
    g.drawImage(bi, 0, 0, null);
}

试试吧..。希望它能起作用并有所帮助。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32216929

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档