【问题标题】:Swing - irregular shaped borderSwing - 不规则形状的边框
【发布时间】:2015-12-15 10:02:36
【问题描述】:

我是摇摆新手,有一个问题如何更好地绘制这个形状:

我有两种想法

  1. 绘制常规矩形并为其写入自定义边框?
  2. 绘制规则矩形+复合边框(包含2或3个边框)。但是在这里我没有成功在形状内绘制边框,有可能吗?像这样的东西: figure.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBor‌​der(outside top,left,bottom, right, Color.WHITE), createMatteBorder(inside top,left,bottom, right, Color.WHITE)),其中内边框是小矩形,外面是大矩形-不确定是否可能???

请提供建议,我们将不胜感激!

【问题讨论】:

  • “边框”是什么意思?
  • 感谢您的回答!
  • 对于边框,我的意思是: figure.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(outside top,left,bottom, right, Color.WHITE), createMatteBorder(inside top,left,bottom ,对,Color.WHITE)),其中内边框是小矩形,外边框是大矩形(我会将图片添加到问题中),但不确定是否可能。

标签: java swing border shape


【解决方案1】:

看看Java 2D API。它可以帮助您绘制复杂的形状。

例如

class IrregularShape extends JComponent {

    private int strokeWidth;

    IrregularShape(int strokeWidth){
        this.strokeWidth = strokeWidth;
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D newGraphics = (Graphics2D) g.create();

        Insets borderInsets = new Insets(0, 0, 0, 0);
        Border border = getBorder();
        if (border != null) {
            borderInsets = border.getBorderInsets(this);
        }

        BasicStroke basicStroke = new BasicStroke(strokeWidth);
        newGraphics.setStroke(basicStroke);

        int x = getX() + borderInsets.left + strokeWidth;
        int y = getY() + borderInsets.top + strokeWidth;
        int width = getWidth() - x - borderInsets.right - strokeWidth;
        int height = getHeight() - y - borderInsets.bottom - strokeWidth;

        Double outterRactangleDouble = new Rectangle2D.Double(x, y, width, height);

        Area outterRectangle = new Area(outterRactangleDouble);

        Area innerRectangle = new Area(outterRactangleDouble);
        AffineTransform affineTransform = new AffineTransform();
        affineTransform.scale(0.5, 0.5);
        affineTransform.translate(x + width * 0.10, y + height * 1.2);

        innerRectangle.transform(affineTransform);
        outterRectangle.subtract(innerRectangle);
        newGraphics.draw(outterRectangle);

    }

}

public class MainFrame {

    public static void main(String[] args) {
        JFrame frame = new JFrame("Irregular Shape");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        Container contentPane = frame.getContentPane();
        contentPane.add(new IrregularShape(3));

        frame.setSize(640, 150);

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

}

结果

它也可以调整大小

【讨论】:

  • 非常感谢!我认为这对我来说会更容易找到使用 2 个或更多边框(集成到现有代码中,到目前为止只支持矩形,我在问题中添加了更多图片)但是如果我不成功,我会使用您的想法,这将需要在现有代码中插入更多内容,谢谢!
【解决方案2】:

你可以使用多边形类(java.awt.Polygon

int xs = new int[]{1,2,3...7}; //your x-coordinates
int ys = new int[]{1,2,3...7}; //your y-coordinates
Shape irr = new Polygon(xs, ys, xs.length); 

如果你想使用某些边框,你可以使用Graphics2D

public void paintComponent(Graphics gr){ 
    Graphics2D g2d = (Graphics2D)gr;

    GradientPaint redToWhite = new GradientPaint(0,0,color.RED,100, 0,color.WHITE);
    g2d.setPaint(redtowhite)
    g2d.fill(irr); //fill special color

    Stroke customBorder = getCustomBorder();
    g2d.setStroke(customBorder);
    g2d.draw(irr); //draw 'special' borders

}

看看stroke and fill

请注意,Polygon 实现了 contains(double x, double y) 方法,可让您检测您是否在里面

【讨论】:

  • 对不起,我没有你的形状点坐标,请自行插入...
  • 非常感谢!多边形是一个很好的选择,但我认为使用 2 个或更多边框会更容易找到(集成到现有代码中,到目前为止只支持矩形)。
  • 你可以使用 Graphics2D - 我会把它添加到我的答案中
【解决方案3】:

例如,您可以使用Area...

public class TestPane extends JPanel {

    public TestPane() {
    }

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

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        Area area = new Area(new Rectangle(10, 10, getWidth() - 20, getHeight() - 20));
        area.subtract(new Area(new Rectangle(20, getHeight() / 2, getWidth() / 2, getHeight() - 10)));
        g2d.draw(area);
        g2d.dispose();
    }

}

您定义一个自定义形状...

public class TestPane extends JPanel {

    public TestPane() {
    }

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

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        Path2D path = new Path2D.Float();
        path.moveTo(10, 10);
        path.lineTo(getWidth() - 20, 10);
        path.lineTo(getWidth() - 20, getHeight() - 20);
        path.lineTo(getWidth() / 2, getHeight() - 20);
        path.lineTo(getWidth() / 2, getHeight() / 2);
        path.lineTo(20, getHeight() / 2);
        path.lineTo(20, getHeight() - 20);
        path.lineTo(10, getHeight() - 20);
        path.closePath();
        g2d.draw(path);
        g2d.dispose();
    }

}

实际上编写自定义边框会非常非常困难,因为形状样式不规则,组件实际包含在哪里?

可能会创建两个或多个边框,然后可以对其进行布局,以便显示为一个

更多详情请见Working with Geometry

更新为Border 示例...

Border 实际工作要困难得多,因为预期边框的内部区域将是矩形。

根据您提供的复杂形状,一种解决方案是实际创建两个边框,一个左右钻孔,负责为要在其中布局的组件生成一个“安全”区域,例如:

public class LeftBorder implements Border {

    private int offset;

    public LeftBorder(int offset) {
        this.offset = offset;
    }
    
    @Override
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
        Path2D path = new Path2D.Float();
        
        int xOffset = x + offset;
        int yOffset = y + offset;
        
        width -= offset;
        height -= offset * 2;
        
        float gap = width * 0.1f;
        
        path.moveTo(xOffset, yOffset);
        path.lineTo(xOffset + width, yOffset);
        path.moveTo(xOffset, yOffset);
        
        path.lineTo(xOffset, yOffset + height);
        path.lineTo(xOffset + gap, yOffset + height);
        path.lineTo(xOffset + gap, yOffset + (height - (height / 2)));
        path.lineTo(xOffset + width, yOffset + (height - (height / 2)));
        
        ((Graphics2D)g).draw(path);
    }

    @Override
    public Insets getBorderInsets(Component c) {
        
        int height = c.getHeight();
        height -= (height / 2);
        
        System.out.println(height);
        return new Insets(offset + 4, offset + 4, height + 4, 0);
    }

    @Override
    public boolean isBorderOpaque() {
        return false;
    }

}

public class RightBorder implements Border {

    private int offset;

    public RightBorder(int offset) {
        this.offset = offset;
    }
    
    @Override
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
        Path2D path = new Path2D.Float();
        
        int xOffset = x;
        int yOffset = y + offset;
        
        width -= offset;
        height -= offset * 2;
        
        path.moveTo(xOffset, yOffset);
        path.lineTo(xOffset + width, yOffset);
        path.lineTo(xOffset + width, yOffset + height);
        path.lineTo(xOffset, yOffset + height);
        
        path.lineTo(xOffset, yOffset + (height - (height / 2)));
        
        ((Graphics2D)g).draw(path);
    }

    @Override
    public Insets getBorderInsets(Component c) {
        
        return new Insets(offset + 4, 0, offset + 4, offset + 4);
    }

    @Override
    public boolean isBorderOpaque() {
        return false;
    }

}

这将要求您提供至少两个相同高度的面板,例如:

import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;

public class Main {

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

    public Main() {
        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.setLayout(new GridBagLayout());
                frame.add(new LeftPane());
                frame.add(new RightPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class RightPane extends JPanel {

        public RightPane() {
            setBorder(new RightBorder(10));
            setLayout(new GridBagLayout());
            add(new JLabel("Righty"));
        }

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

    }

    public class LeftPane extends JPanel {

        public LeftPane() {
            setBorder(new LeftBorder(10));
            setLayout(new GridBagLayout());
            add(new JLabel("Lefty"));
        }

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

    }

}

这也将依赖于布局管理器能够将两个组件彼此相邻布局

【讨论】:

  • 非常感谢!我认为这个选项是从 JPanel 派生的,但我认为使用 2 个或更多边框会更容易找到(集成到现有代码中,到目前为止只支持矩形)。
  • 可能会创建两个或多个边框,然后可以将它们布置成一个 - 是的,这就是我想要的(我添加到问题中)但没有成功。该图继承自 Jpanel 并实现为矩形,其中有很多我想保持原样的逻辑,所以玩边框我看到了最好的选择。图的父容器也是JPanel。
【解决方案4】:

除了我的第一个回答https://stackoverflow.com/a/34287251/974186

您也可以将其实现为边框。

class IrregularBorder implements Border {

    private int thickness;

    public IrregularBorder(int thickness) {
        this.thickness = thickness;
    }

    @Override
    public void paintBorder(Component c, Graphics g, int x, int y, int width,
            int height) {
        Graphics2D graphics2d = (Graphics2D) g;

        BasicStroke basicStroke = new BasicStroke(thickness);
        graphics2d.setStroke(basicStroke);

        int halfThickness = thickness / 2;
        Double outterRactangleDouble = new Rectangle2D.Double(
                x + halfThickness, y + halfThickness, width - thickness,
                height - thickness);

        Area outterRectangle = new Area(outterRactangleDouble);

        Area innerRectangle = computeInnerRect(x, y, width, height,
                outterRactangleDouble);
        outterRectangle.subtract(innerRectangle);
        graphics2d.draw(outterRectangle);

    }

    private Area computeInnerRect(int x, int y, int width, int height,
            Double outterRactangleDouble) {
        Area innerRectangle = new Area(outterRactangleDouble);
        AffineTransform affineTransform = new AffineTransform();
        affineTransform.scale(0.5, 0.5);
        affineTransform.translate(x + width * 0.10, y + height * 1.2);

        innerRectangle.transform(affineTransform);
        return innerRectangle;
    }

    @Override
    public Insets getBorderInsets(Component c) {
        int left = (int) (thickness + (c.getWidth() * 0.6));
        return new Insets(thickness, left, thickness, thickness);
    }

    @Override
    public boolean isBorderOpaque() {
        return true;
    }

}

照常使用

public class MainFrame {

    public static void main(String[] args) {
        JFrame frame = new JFrame("Irregular Shape");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        Container contentPane = frame.getContentPane();

        JPanel mainPanel = new JPanel(new BorderLayout());
        mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        contentPane.add(mainPanel);

        JPanel irregularShapeBorderedPanel = new JPanel(new BorderLayout());
        irregularShapeBorderedPanel.add(new JButton("Button"),
                BorderLayout.CENTER);
        irregularShapeBorderedPanel.setBorder(new IrregularBorder(2));

        mainPanel.add(irregularShapeBorderedPanel);

        frame.setSize(640, 150);

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

}

【讨论】:

  • 非常感谢您的帮助!!我现在就试试这个带边框的代码!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多