如何使用getWidth()和getHeight()在java中居中星形

时间:2016-01-29 00:36:42

标签: java swing

我有问题以星形为中心。问题是我硬编码了每个坐标的点。而这个问题在于我没有找到合适的位置,尽管我创造了一个完美的5星。更多问题是,如果我想通过单击按钮重新缩放形状以使其变大或变小? 这是我的代码。

public void run() {
            DifferentShapes panel = new DifferentShapes();
            JFrame frame = new JFrame("Different Shapes");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400,400);
            frame.setVisible(true);  
            frame.add(panel);
            frame.setJMenuBar(panel.getMenuBAr());
        }

    public void star(Graphics shape) {
    //        int sizeReq = 1;
    int [] starX =  new int[] {250, 262, 304, 268, 278, 250, 222, 232, 196, 238};
    int []starY =  new int[] {200, 236, 236, 254, 296, 272, 296, 254, 236, 236};
    shape.fillPolygon(starX, starY, starX.length);    

}

2 个答案:

答案 0 :(得分:4)

没有Shapes API

您可能会花费大量时间手动翻译多边形数组,但更好的解决方案可能是将多边形转换为0x0,以便左/右上角位于0x0

基本上,我计算了每个数组的最小值/最大值,然后从所有值中减去“min”值......

protected static int[] minMax(int[] values) {
    int min = Integer.MAX_VALUE;
    int max = Integer.MIN_VALUE;
    for (int value : values) {
        min = Math.min(min, value);
        max = Math.max(max, value);
    }

    return new int[]{min, max};
}

protected static int[] normalise(int[] values, int min) {
    for (int index = 0; index < values.length; index++) {
        values[index] = values[index] - min;
    }
    return values;
}

首先......

[250, 262, 304, 268, 278, 250, 222, 232, 196, 238]
[200, 236, 236, 254, 296, 272, 296, 254, 236, 236]

翻译为......

[54, 66, 108, 72, 82, 54, 26, 36, 0, 42]
[0, 36, 36, 54, 96, 72, 96, 54, 36, 36]

在绘制时,看起来像......

Top/Left

你现在可能正在考虑,好吧,这不太好,我想把它放在中心,但要进入中心,我们需要多边形的宽度和高度

为了达到这个目的,我获取了已翻译的数组,并通过minMax方法传递了它们,并使用了返回值的第二个元素,它给了我108x96的值width x height),现在我们拥有了塑造中心所需的信息

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics copy = g.create();
    int x = (getWidth() - maxWidth) / 2;
    int y = (getHeight() - maxHeight) / 2;
    copy.translate(x, y);
    copy.fillPolygon(starX, starY, starX.length);
    copy.dispose();
}

基本上,所有这一切都使用maxWidthmaxHeight值来计算当前组件中的中心位置,将Graphics上下文转换为适当的偏移并绘制多边形。平移移动原点位置(0x0上下文的Graphics点),允许用于更改多边形的绘制位置。

Star in the middle

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Arrays;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    protected static int[] minMax(int[] values) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int value : values) {
            min = Math.min(min, value);
            max = Math.max(max, value);
        }

        return new int[]{min, max};
    }

    protected static int[] normalise(int[] values, int min) {
        for (int index = 0; index < values.length; index++) {
            values[index] = values[index] - min;
        }
        return values;
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private int starX[];
        private int starY[];

        private int maxWidth, maxHeight;

        public TestPane() {
            starX = new int[]{250, 262, 304, 268, 278, 250, 222, 232, 196, 238};
            starY = new int[]{200, 236, 236, 254, 296, 272, 296, 254, 236, 236};

            int[] minMaxX = minMax(starX);
            int[] minMaxY = minMax(starY);

            starX = normalise(starX, minMaxX[0]);
            starY = normalise(starY, minMaxY[0]);

            minMaxX = minMax(starX);
            minMaxY = minMax(starY);

            maxWidth = minMaxX[1];
            maxHeight = minMaxY[1];
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics copy = g.create();
            int x = (getWidth() - maxWidth) / 2;
            int y = (getHeight() - maxHeight) / 2;
            copy.translate(x, y);
            copy.fillPolygon(starX, starY, starX.length);
            copy.dispose();
        }

    }

}

所以,关于知道,你应该看到为你的形状使用0x0原点的重要性。

有关详细信息,请查看2D GraphicsTransforming Shapes, Text, and Images

规范化...

缩放可以通过Graphics2D#scale ...

完成

Scale

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D copy = (Graphics2D) g.create();
    double scale = slider.getValue() / 100d;
    int x = (getWidth() - maxWidth) / 2;
    int y = (getHeight() - maxHeight) / 2;
    copy.translate(x, y);
    copy.scale(scale, scale);
    copy.fillPolygon(starX, starY, starX.length);
    copy.dispose();
}

但是这会缩放可能无法提供所需结果的像素。

在有人跳下我的喉咙之前,如果你想让它再次居中,你还需要缩放maxWidthmaxHeight

使用Shapes API

正如我在过去所说,使用2D形状API可以获得更好的效果,它们是自包含的,它们很容易被移动,并且在缩放时会得到更好的结果

例如,使用上面的“翻译”值,你应该创建一个很好的自定义,可重复使用的类......

public class StarShape extends Path2D.Double {

    public StarShape() {
        moveTo(54, 0);
        lineTo(66, 36);
        lineTo(108, 36);
        lineTo(75, 54);
        lineTo(82, 96);
        lineTo(54, 72);
        lineTo(26, 96);
        lineTo(36, 54);
        lineTo(0, 36);
        lineTo(42, 36);
        closePath();
    }

}

知道,我不了解你,但这更容易阅读,你可以很容易地在一些方格纸上绘制。

现在通过做一些简单的事情很容易填补......

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.fill(new StarShape());
    g2d.dispose();
}

但是,等等,我们希望它集中,一切都很容易......

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    Rectangle bounds = starShape.getBounds();
    int x = (getWidth() - bounds.width) / 2;
    int y = (getHeight() - bounds.height) / 2;
    g2d.fill(starShape.createTransformedShape(AffineTransform.getTranslateInstance(x, y)));
    g2d.dispose();
}

嗯,这比非形状API方法少得多......

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class StarShape extends Path2D.Double {

        public StarShape() {
            moveTo(54, 0);
            lineTo(66, 36);
            lineTo(108, 36);
            lineTo(75, 54);
            lineTo(82, 96);
            lineTo(54, 72);
            lineTo(26, 96);
            lineTo(36, 54);
            lineTo(0, 36);
            lineTo(42, 36);
            closePath();
        }

    }

    public class TestPane extends JPanel {

        private StarShape starShape;

        public TestPane() {
            starShape = new StarShape();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Rectangle bounds = starShape.getBounds();
            int x = (getWidth() - bounds.width) / 2;
            int y = (getHeight() - bounds.height) / 2;
            g2d.fill(starShape.createTransformedShape(AffineTransform.getTranslateInstance(x, y)));
            g2d.dispose();
        }

    }

}

缩放

一般来说,缩放使用与翻译相同的过程,但是你获得的是,你可以扩展Shape然后单独翻译它的好处,让你获得Rectangle界限缩放Shape独立的。它还可以缩放矢量(点)而不是像素,这样可以获得更好的效果......

Scales

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    double scale = slider.getValue() / 100d;
    Shape shape = starShape.createTransformedShape(AffineTransform.getScaleInstance(scale, scale));
    Rectangle bounds = shape.getBounds();
    int x = (getWidth() - bounds.width) / 2;
    int y = (getHeight() - bounds.height) / 2;
    GeneralPath path = new GeneralPath();
    path.append(shape.getPathIterator(AffineTransform.getTranslateInstance(x, y)), true);
    g2d.fill(path);
    g2d.dispose();
}

所以,简短的回答是,使用Shapes API,答案很长,使用Shapes API,你不需要为你遇到的每个问题重新发明轮子

答案 1 :(得分:2)

您可能需要考虑使用Shape Utils辅助类为您生成星星,这样您就不需要手动计算星上的所有点。

基本上你只需要两行代码来使用该类:

star = ShapeUtils.radiusShape(10, 55, 22);

这将产生一个10点,从中心55到22像素交替出现。

生成的第一个点将在水平线上。由于有5个大点,这意味着它们将每72度产生一次。由于您希望顶点位于垂直轴上,因此您需要将所有点旋转-18度:

star = ShapeUtils.rotate(star, -18);

显示如何在面板中心绘制星形的简单示例如下:

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

public class ShapeSSCCE extends JPanel
{
    private Shape star;

    public ShapeSSCCE()
    {
        star = ShapeUtils.radiusShape(10, 55, 22);
        star = ShapeUtils.rotate(star, -18);
    }

    @Override
    public Dimension getPreferredSize()
    {
        Rectangle bounds = star.getBounds();

        return new Dimension(bounds.width, bounds.height);
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g.create();
        Rectangle bounds = star.getBounds();
        int x = (getWidth()  - bounds.width)  / 2;
        int y = (getHeight() - bounds.height) / 2;
        g2d.translate(x, y);
        g2d.fill( star );
        g2d.dispose();
    }

    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("ShapeSSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new ShapeSSCCE() );
        frame.setSize(200, 200);
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

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

尝试使用/不使用旋转形状的代码来查看差异。

如果您不想进行自定义绘画,则可以使用上述链接中的ShapeIcon类。然后你可以将星星视为一个Icon并将其添加到标签:

Shape star = ShapeUtils.radiusShape(10, 55, 22);
star = ShapeUtils.rotate(star, -18);
ShapeIcon starIcon = new ShapeIcon(star, Color.RED);
JLabel starLabel = new JLabel( starIcon );
  

我想通过点击按钮重新缩放形状以使其变大或变小?

你可以:

  1. 使用带有新值的ShapeUtils类创建新形状
  2. 使用AffineTransform缩放现有形状。查看ShapeUtils.rotate(...)方法以查看如何为旋转完成此操作。缩放的逻辑类似。
  3. 使用AffineTransform绘制时动态缩放形状。有关设置转换的信息,请阅读Graphics2D类。