带三角形边框的 JPanel
JPanel with triangular border
希望创建一个 JPanel 边框,如下图中以红色突出显示的那样......即一侧或两侧的三角形。我已经使用信息 here and here 成功实现了圆形 JPanel 边框。有什么想法或提示吗?
三角形的主要问题是两个组件需要相互重叠,而标准 Java 布局旨在防止重叠。
您有 3 个选项:
- 您需要 "fake" 重叠,方法是让一个组件在左侧绘制组件的三角形。
- 或者您可以创建一个位于两个矩形组件之间并呈现重叠的组件。
- 或者您需要使用以所需方式重叠它们的自定义布局管理器。
无论哪种方式,您都会遇到点击重叠区域只会转到其中一个按钮的问题,除非您添加特殊逻辑来适当地转发点击。
您或许可以使用 FlowLayout
:
FlowLayout
: 在水平间隙中设置为负值。
Override JRadioButton#contains(int, int)
Override JPanel#isOptimizedDrawingEnabled()
import java.awt.*;
import java.awt.geom.*;
import java.io.Serializable;
import java.util.*;
import java.util.List;
import javax.swing.*;
public final class FlowLayoutOverlapTest {
private static final int BORDER = 1;
public JComponent makeUI() {
JPanel p = new JPanel(new GridLayout(0, 1));
p.setBorder(BorderFactory.createEmptyBorder(20, 2, 20, 2));
p.add(makeBreadcrumbList(0, Color.PINK, Arrays.asList("overlap:", "0px", "button")));
p.add(makeBreadcrumbList(5, Color.CYAN, Arrays.asList("overlap:", "5px", "button")));
p.add(makeBreadcrumbList(9, Color.ORANGE, Arrays.asList("overlap:", "9px", "button")));
return p;
}
private static AbstractButton makeButton(String title, Color color) {
final ToggleButtonBarCellIcon icon = new ToggleButtonBarCellIcon();
AbstractButton b = new JRadioButton(title) {
//http://java-swing-tips.blogspot.jp/2008/11/rounded-corner-jbutton.html
@Override public boolean contains(int x, int y) {
if (Objects.nonNull(icon) && Objects.nonNull(icon.area)) {
return icon.area.contains(x, y);
} else {
return super.contains(x, y);
}
}
};
b.setIcon(icon);
b.setContentAreaFilled(false);
b.setBorder(BorderFactory.createEmptyBorder());
b.setHorizontalTextPosition(SwingConstants.CENTER);
b.setFocusPainted(false);
b.setOpaque(false);
b.setBackground(color);
return b;
}
private static JPanel makePanel(int overlap) {
//http://java-swing-tips.blogspot.com/2013/12/breadcrumb-navigation-with-jradiobutton.html
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEADING, -overlap, 0)) {
@Override public boolean isOptimizedDrawingEnabled() {
return false;
}
};
p.setBorder(BorderFactory.createEmptyBorder(BORDER, overlap + BORDER, BORDER, BORDER));
p.setOpaque(false);
return p;
}
private static JComponent makeBreadcrumbList(int overlap, Color color, List<String> list) {
JPanel p = makePanel(overlap + 1);
ButtonGroup bg = new ButtonGroup();
for (String title : list) {
AbstractButton b = makeButton(title, color);
p.add(b);
bg.add(b);
}
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new FlowLayoutOverlapTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
//http://java-swing-tips.blogspot.com/2012/11/make-togglebuttonbar-with-jradiobuttons.html
class ToggleButtonBarCellIcon implements Icon, Serializable {
private static final long serialVersionUID = 1L;
private static final int W = 10;
private static final int H = 21;
public Shape area;
public Shape getShape(Container parent, Component c, int x, int y) {
int w = c.getWidth() - 1;
int h = c.getHeight() - 1;
int h2 = (int)(h * .5 + .5);
int w2 = W;
Path2D.Float p = new Path2D.Float();
p.moveTo(0, 0);
p.lineTo(w - w2, 0);
p.lineTo(w, h2);
p.lineTo(w - w2, h);
p.lineTo(0, h);
if (c != parent.getComponent(0)) {
p.lineTo(w2, h2);
}
p.closePath();
return AffineTransform.getTranslateInstance(x, y).createTransformedShape(p);
}
@Override public void paintIcon(Component c, Graphics g, int x, int y) {
Container parent = c.getParent();
if (Objects.isNull(parent)) {
return;
}
area = getShape(parent, c, x, y);
Color bgc = parent.getBackground();
Color borderColor = Color.GRAY.brighter();
if (c instanceof AbstractButton) {
ButtonModel m = ((AbstractButton) c).getModel();
if (m.isSelected() || m.isRollover()) {
bgc = c.getBackground();
borderColor = Color.GRAY;
}
}
Graphics2D g2 = (Graphics2D) g.create();
g2.setPaint(bgc);
g2.fill(area);
g2.setPaint(borderColor);
g2.draw(area);
g2.dispose();
}
@Override public int getIconWidth() {
return 100;
}
@Override public int getIconHeight() {
return H;
}
}
希望创建一个 JPanel 边框,如下图中以红色突出显示的那样......即一侧或两侧的三角形。我已经使用信息 here and here 成功实现了圆形 JPanel 边框。有什么想法或提示吗?
三角形的主要问题是两个组件需要相互重叠,而标准 Java 布局旨在防止重叠。
您有 3 个选项:
- 您需要 "fake" 重叠,方法是让一个组件在左侧绘制组件的三角形。
- 或者您可以创建一个位于两个矩形组件之间并呈现重叠的组件。
- 或者您需要使用以所需方式重叠它们的自定义布局管理器。
无论哪种方式,您都会遇到点击重叠区域只会转到其中一个按钮的问题,除非您添加特殊逻辑来适当地转发点击。
您或许可以使用 FlowLayout
:
FlowLayout
: 在水平间隙中设置为负值。Override JRadioButton#contains(int, int)
Override JPanel#isOptimizedDrawingEnabled()
import java.awt.*;
import java.awt.geom.*;
import java.io.Serializable;
import java.util.*;
import java.util.List;
import javax.swing.*;
public final class FlowLayoutOverlapTest {
private static final int BORDER = 1;
public JComponent makeUI() {
JPanel p = new JPanel(new GridLayout(0, 1));
p.setBorder(BorderFactory.createEmptyBorder(20, 2, 20, 2));
p.add(makeBreadcrumbList(0, Color.PINK, Arrays.asList("overlap:", "0px", "button")));
p.add(makeBreadcrumbList(5, Color.CYAN, Arrays.asList("overlap:", "5px", "button")));
p.add(makeBreadcrumbList(9, Color.ORANGE, Arrays.asList("overlap:", "9px", "button")));
return p;
}
private static AbstractButton makeButton(String title, Color color) {
final ToggleButtonBarCellIcon icon = new ToggleButtonBarCellIcon();
AbstractButton b = new JRadioButton(title) {
//http://java-swing-tips.blogspot.jp/2008/11/rounded-corner-jbutton.html
@Override public boolean contains(int x, int y) {
if (Objects.nonNull(icon) && Objects.nonNull(icon.area)) {
return icon.area.contains(x, y);
} else {
return super.contains(x, y);
}
}
};
b.setIcon(icon);
b.setContentAreaFilled(false);
b.setBorder(BorderFactory.createEmptyBorder());
b.setHorizontalTextPosition(SwingConstants.CENTER);
b.setFocusPainted(false);
b.setOpaque(false);
b.setBackground(color);
return b;
}
private static JPanel makePanel(int overlap) {
//http://java-swing-tips.blogspot.com/2013/12/breadcrumb-navigation-with-jradiobutton.html
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEADING, -overlap, 0)) {
@Override public boolean isOptimizedDrawingEnabled() {
return false;
}
};
p.setBorder(BorderFactory.createEmptyBorder(BORDER, overlap + BORDER, BORDER, BORDER));
p.setOpaque(false);
return p;
}
private static JComponent makeBreadcrumbList(int overlap, Color color, List<String> list) {
JPanel p = makePanel(overlap + 1);
ButtonGroup bg = new ButtonGroup();
for (String title : list) {
AbstractButton b = makeButton(title, color);
p.add(b);
bg.add(b);
}
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new FlowLayoutOverlapTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
//http://java-swing-tips.blogspot.com/2012/11/make-togglebuttonbar-with-jradiobuttons.html
class ToggleButtonBarCellIcon implements Icon, Serializable {
private static final long serialVersionUID = 1L;
private static final int W = 10;
private static final int H = 21;
public Shape area;
public Shape getShape(Container parent, Component c, int x, int y) {
int w = c.getWidth() - 1;
int h = c.getHeight() - 1;
int h2 = (int)(h * .5 + .5);
int w2 = W;
Path2D.Float p = new Path2D.Float();
p.moveTo(0, 0);
p.lineTo(w - w2, 0);
p.lineTo(w, h2);
p.lineTo(w - w2, h);
p.lineTo(0, h);
if (c != parent.getComponent(0)) {
p.lineTo(w2, h2);
}
p.closePath();
return AffineTransform.getTranslateInstance(x, y).createTransformedShape(p);
}
@Override public void paintIcon(Component c, Graphics g, int x, int y) {
Container parent = c.getParent();
if (Objects.isNull(parent)) {
return;
}
area = getShape(parent, c, x, y);
Color bgc = parent.getBackground();
Color borderColor = Color.GRAY.brighter();
if (c instanceof AbstractButton) {
ButtonModel m = ((AbstractButton) c).getModel();
if (m.isSelected() || m.isRollover()) {
bgc = c.getBackground();
borderColor = Color.GRAY;
}
}
Graphics2D g2 = (Graphics2D) g.create();
g2.setPaint(bgc);
g2.fill(area);
g2.setPaint(borderColor);
g2.draw(area);
g2.dispose();
}
@Override public int getIconWidth() {
return 100;
}
@Override public int getIconHeight() {
return H;
}
}