¿Por qué el método de dibujo de Graphics no respeta los atributos de trazo?
Pregunta
Quiero crear un borde personalizado con esquinas redondeadas.
Código -
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.border.AbstractBorder;
class JRoundedCornerBorder extends AbstractBorder
{
private static final long serialVersionUID = 7644739936531926341L;
private static final int THICKNESS = 5;
JRoundedCornerBorder()
{
super();
}
@Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
{
Graphics2D g2 = (Graphics2D)g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if(c.hasFocus())
{
g2.setColor(Color.BLUE);
}
else
{
g2.setColor(Color.BLACK);
}
g2.setStroke(new BasicStroke(THICKNESS, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.drawRect(x, y, width - 1, height - 1);
g2.dispose();
}
@Override
public Insets getBorderInsets(Component c)
{
return new Insets(THICKNESS, THICKNESS, THICKNESS, THICKNESS);
}
@Override
public Insets getBorderInsets(Component c, Insets insets)
{
insets.left = insets.top = insets.right = insets.bottom = THICKNESS;
return insets;
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
// Add button with custom border
final JButton button = new JButton("Hello");
button.setBorder(new JRoundedCornerBorder());
frame.add(button);
// Add button without custom border
frame.add(new JButton("Goodbye"));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Resultado -
Como se puede ver, Graphics.drawRect
ignora por completo el BasicStroke.CAP_ROUND
y BasicStroke.JOIN_ROUND
atributos.¿Por qué?
Solución
Como se explica en Aprender Java 2D, Parte 1:
java.awt.BasicStroke.CAP_ROUND
:Esto forma una tapa circular centrada en el extremo, con un diámetro del ancho de la pluma.
La palabra clave es "centrado".Creo que siempre se da el caso de que al dibujar con trazos gruesos, Java2D centrará el grosor de la línea a lo largo de la línea hipotética, infinitamente delgada, entre los centros de los píxeles en las coordenadas inicial y final.Por ejemplo, al dibujar una línea azul vertical de 7 píxeles de espesor, Java2D pinta 3 píxeles a cada lado del segmento de línea hipotético que se está dibujando.
En su ejemplo, el grosor es de 5 píxeles.Debes desplazar las coordenadas para dibujar el trazo completamente dentro del clip de gráficos.Moviéndose en 2 píxeles (o THICKNESS/2
), se hacen visibles las esquinas redondeadas:
//...
g2.setStroke(new BasicStroke(THICKNESS, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.drawRect(x + THICKNESS/2, y + THICKNESS/2, width - 2*(THICKNESS/2) - 1, height - 2*(THICKNESS/2) - 1);
g2.dispose();
}
@Override
public Insets getBorderInsets(Component c) {
return new Insets(THICKNESS + THICKNESS/2, THICKNESS + THICKNESS/2, THICKNESS + THICKNESS/2, THICKNESS + THICKNESS/2);
}
//...
Otros consejos
el problema es el desplazamiento: efectivamente está cortando el borde en el medio para que las esquinas parezcan no estar redondeadas.Teniéndolo en cuenta (aquí solo para el desplazamiento, también es necesario ajustar el ancho)
g2.drawRect(x + thickness/2, y + thickness/2,
width - 1 - thickness, height - 1 - thickness);
<×Editar
Recuento de píxeles descuidado fijo :-)