Swing - 不规则形状的边框

3

我是 swing 的新手,想问一下如何更好地绘制这个形状:

complicated shape

我有两种想法:
  1. 绘制常规矩形并编写自定义边框?
  2. 绘制常规矩形+复合边框(其中包含2或3个边框)。但是,我无法在形状内部绘制边框,这真的可能吗?类似于这样: two borders figure.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBor‌​der(outside top,left,bottom, right, Color.WHITE), createMatteBorder(inside top,left,bottom, right, Color.WHITE)),其中内部边框是小矩形,外部是大矩形-不确定是否可能?
请给予建议,并且非常感谢任何示例!

“border”是什么意思? - MadProgrammer
请参见具有圆角和透明度的边框 - Andrew Thompson
感谢您的回答! - altavista23
“边框”是指像这样的东西:figure.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(outside top,left,bottom, right, Color.WHITE),createMatteBorder(inside top,left,bottom, right, Color.WHITE))),其中内部边框是小矩形,外部边框是大矩形(我会在问题中添加图片),但不确定是否可能实现。 - altavista23
4个回答

1

你可以使用多边形类(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

}

请查看描边和填充
请注意,Polygon 实现了contains(double x, double y) 方法,可让您检测是否在多边形内部。

很抱歉,我没有您的形状点的坐标,请自己插入它们。 - Martin Frank
非常感谢!多边形是一个很好的选择,但我认为对我来说更容易找到两个或更多边框进行操作(将其集成到现有代码中,该代码目前仅支持矩形)。 - altavista23
你可以使用Graphics2D - 我会在我的答案中添加这个。 - Martin Frank

1
您可以使用一个Area,例如...

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

}

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

Path2D

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

}

实际上编写自定义边框会非常困难,因为形状不规则,组件应该放置在哪里?
可能可以创建两个或更多的边框,然后将它们布局,使其看起来像一个。
有关详细信息,请参见使用几何图形

更新了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;
    }

}

这将需要您提供至少两个等高的面板,例如:

Example layout

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派生这个选项,但是认为对我来说更容易找到玩弄两个或多个边框的方法(将其集成到现有代码中,该代码仅支持矩形直到现在)。 - altavista23
可能可以创建两个或更多的边框,然后将它们布置在一起,使其看起来像一个 - 是的,这就是我想要的(我已经添加到问题中),但没有成功。该图形从JPanel继承并实现为矩形,具有许多逻辑,我希望保持不变,因此我认为使用边框是最好的选择。该图形的父容器也是JPanel。 - altavista23

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

}

结果 enter image description here 并且它也是可调整大小的。

enter image description here


非常感谢!我认为对我来说,找到如何处理两个或更多边框会更容易(将其集成到现有代码中,目前仅支持矩形,我在问题中添加了更多图片)。但是如果我失败了,我将使用您的想法,这将需要在现有代码中进行更多插入,谢谢! - altavista23

0

除了我的第一个答案https://dev59.com/UJLea4cB1Zd3GeqPxhJG#34287251之外

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

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

}

enter image description here


非常感谢您的帮助!我现在会尝试使用这个代码来添加边框! - altavista23

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接