I have some shapes created by class Rectangle and I want to surround them with a thick border. However the methods drawRect and drawOval form Graphics class create a thin line as the border of the shape. How can I adjust them so as me to able to manipulate the thickness of the border line? If this is not possible or quite effective, what is another way to assign an adjustable border on the shapes? May I need Rectangle2D or Graphics2D?
After that, do you know how I can “round” the angles of the border of a square so as not to be sharp?
To make the border thicker, use Graphics2D.setStroke(...). And to draw "rounded" rectangles, use Graphics.drawRoundRect(...).
Look into Graphics2D strokes:
Graphics2D.setStroke()
BasicStroke
If a round join in your stroke isn't soft enough, look into RoundRectangle2D.
I implemented custom rounded shape for icons.
1) The thick border can be painted by :
BasicStroke dashed =new BasicStroke(3.0f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,10.0f);
2) Rounded shape could be painted by:
Ellipse2D.Double circle = new Ellipse2D.Double(x+1, y+1, 14, 14);
Ellipse2D.Double circleBorder = new Ellipse2D.Double(x, y, 15, 15);
All code is here:
public class ColorIcon implements Icon {
private Color color = Color.WHITE;
/**
* Constructor for implement custom colored icon
* #param color - custom parameter for creating colored icon.
*/
public ColorIcon(#Nonnull Color color) {
this.color = color;
}
/**
* Default constructor for implement default icon.
*/
public ColorIcon() {
}
#Override
public void paintIcon(#Nonnull Component c, #Nonnull Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
BasicStroke dashed =new BasicStroke(3.0f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,10.0f);
Ellipse2D.Double circle = new Ellipse2D.Double(x+1, y+1, 14, 14);
Ellipse2D.Double circleBorder = new Ellipse2D.Double(x, y, 15, 15);
g2.setColor(getColor());
g2.setRenderingHints(hints);
g2.fill(circle);
Composite oldComposite=g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0));
g2.setColor(new Color(1,1,1,1));
g2.setStroke(dashed);
g2.draw(circleBorder);
g2.setComposite(oldComposite);
}
#Override
public int getIconWidth() {
return 15;
}
#Override
public int getIconHeight() {
return 15;
}
public Color getColor() {
return color;
}
public void setColor(#Nonnull Color color) {
this.color = color;
}
}
Related
Which layout manager to use on this game?
IMHO, using layouts and components is not a good solution to your problem, personally, I'd lean towards a custom painting solution instead.
Start with a basic concept of a piece, it needs to know it's location, it's size, it's color, be able to paint itself and possibly be relocatable, something like...
public interface Piece {
public Rectangle getBounds();
public Color getColor();
public void setLocation(Point point);
public void paint(Graphics2D g2d);
}
From this, you can define what ever shapes you need, for example...
public abstract class AbstractPiece implements Piece {
private Rectangle bounds;
private Color color;
#Override
public void setLocation(Point point) {
bounds.setLocation(point);
}
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public Color getColor() {
return color;
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public void setColor(Color color) {
this.color = color;
}
}
public class Square extends AbstractPiece {
public Square(Point location, int size, Color color) {
Rectangle bounds = new Rectangle();
bounds.setLocation(location);
bounds.setSize(size, size);
setBounds(bounds);
setColor(color);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.fill(getBounds());
g2d.setColor(Color.GRAY);
Rectangle bounds = getBounds();
g2d.drawLine(bounds.x + (bounds.width / 2), bounds.y, bounds.x + (bounds.width / 2), bounds.y + bounds.height);
g2d.drawLine(bounds.x, bounds.y + (bounds.height / 2), bounds.x + bounds.width, bounds.y + (bounds.height / 2));
}
}
This is just a basic square, nothing fancy, but, it's self contained, easy to create and manage. You can create any combination of shapes you like using this simple pattern, at the end of the day, your board class won't care, it just needs to the space it occupies and how to paint it, speaking for which, you need some kind of container to manage all these shapes...
public class PuzzelPane extends JPanel {
private List<Piece> pieces;
public PuzzelPane() {
Dimension size = getPreferredSize();
pieces = new ArrayList<>(25);
Point location = new Point((size.width - 50) / 2, (size.width - 50) / 2);
pieces.add(new Square(location, 50, Color.BLUE));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Piece piece : pieces) {
Graphics2D g2d = (Graphics2D) g.create();
piece.paint(g2d);
g2d.dispose();
}
}
}
This is a really simply concept, it has a List to maintain all the available shapes and simply loops over this to paint them in the paintComponent method
Couple it with the idea from this example and this example and you have the ability to now drag the shapes
To expand on kaetzacoatl's comment, you should not use a LayoutManager for this at all, for several reasons:
They do not have the flexibility for more complex setups.
They are quite hard to work with for non-trivial movement of elements.
They mostly make sense for elements that can be resized and maybe wrapped or stretched, which I assume does not make sense for tiles in a puzzle game.
Instead, I would advise to use something like a canvas and draw your puzzle pieces with coordinates.
I'm making a simple app that allows you to show different circle objects browse through them with buttons.
The problem is i have no idea how to print out my circleobjects into the jPanel.
When you first run the program the first circleobject should appear in the jPanel. Here's my circleclass:
public class Circle {
private int height;
private int width;
private Color color;
public Circle (int height, int width, Color color){
this.height = height;
this.width = height;
this.color = color;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
}
And here is the first part of the GUI code. I've made 5 circleobjects in an arraylist.
public class CircleGUI extends javax.swing.JFrame{
public ArrayList<Circle> circles = new ArrayList<Circle>();
public CircleGUI(){
initComponents();
circles.add(new Circle(15, 15, Color.blue));
circles.add(new Circle(20, 15, Color.black));
circles.add(new Circle(30, 10, Color.green));
circles.add(new Circle(20, 10, Color.orange));
circles.add(new Circle(35, 35, Color.red));
}
Now, how do i make my first object appear in the jPanel which is marked on the screenshot?
You will have to override the paintComponent(Graphics) method of your JPanel.
Then you do the drawing on the Graphics object, based on the data your current Circle object contains.
Check out Custom Painting Approaches. It does exactly what you want (except it paint Rectangles).
It actually shows two approaches:
Painting objects from an ArrayList (which is what you want)
Painting objects onto a BufferedImage
The basic painting code in your case is:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// Custom code to paint all the Rectangles from the List
for (DrawingArea.ColoredRectangle cr : coloredRectangles)
{
g.setColor( cr.getForeground() );
Rectangle r = cr.getRectangle();
g.drawRect(r.x, r.y, r.width, r.height);
}
}
Of course you would paint ovals and use your Circle class
I need to create a rectangle object and then paint it to the applet using paint(). I tried
Rectangle r = new Rectangle(arg,arg1,arg2,arg3);
Then tried to paint it to the applet using
g.draw(r);
It didn't work. Is there a way to do this in java? I have scoured google to within an inch of its life for an answer, but I haven't been able to find an answer. Please help!
Try this:
public void paint (Graphics g) {
Rectangle r = new Rectangle(xPos,yPos,width,height);
g.fillRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
[edit]
// With explicit casting
public void paint (Graphics g) {
Rectangle r = new Rectangle(xPos, yPos, width, height);
g.fillRect(
(int)r.getX(),
(int)r.getY(),
(int)r.getWidth(),
(int)r.getHeight()
);
}
You may try like this:
import java.applet.Applet;
import java.awt.*;
public class Rect1 extends Applet {
public void paint (Graphics g) {
g.drawRect (x, y, width, height); //can use either of the two//
g.fillRect (x, y, width, height);
g.setColor(color);
}
}
where x is x co-ordinate
y is y cordinate
color=the color you want to use eg Color.blue
if you want to use rectangle object you could do it like this:
import java.applet.Applet;
import java.awt.*;
public class Rect1 extends Applet {
public void paint (Graphics g) {
Rectangle r = new Rectangle(arg,arg1,arg2,arg3);
g.fillRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
g.setColor(color);
}
}
Note:drawRect and fillRect are different.
Draws the outline of the specified rectangle:
public void drawRect(int x,
int y,
int width,
int height)
Fills the specified rectangle. The rectangle is filled using the graphics context's current color:
public abstract void fillRect(int x,
int y,
int width,
int height)
How can I draw an outline around any text in AWT, something similar to this picture?
two examples
Font and AffineTransform
Font, TextLayout and AffineTransform
output from this paint would be the BufferedImage, for AWT Components use method paint(), for Swing JComponents is there paintComponet()
Also, from code linked in a comment:
Try the following:
public void paintTextWithOutline(Graphics g) {
String text = "some text";
Color outlineColor = Color.white;
Color fillColor = Color.black;
BasicStroke outlineStroke = new BasicStroke(2.0f);
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D) g;
// remember original settings
Color originalColor = g2.getColor();
Stroke originalStroke = g2.getStroke();
RenderingHints originalHints = g2.getRenderingHints();
// create a glyph vector from your text
GlyphVector glyphVector = getFont().createGlyphVector(g2.getFontRenderContext(), text);
// get the shape object
Shape textShape = glyphVector.getOutline();
// activate anti aliasing for text rendering (if you want it to look nice)
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setColor(outlineColor);
g2.setStroke(outlineStroke);
g2.draw(textShape); // draw outline
g2.setColor(fillColor);
g2.fill(textShape); // fill the shape
// reset to original settings after painting
g2.setColor(originalColor);
g2.setStroke(originalStroke);
g2.setRenderingHints(originalHints);
}
}
Not sure how you're drawing the text now, but one way you could do it is use a BufferedImage as an overlay to whatever it is that you're drawing on.
Create BufferedImage using the dimensions of the string and font you are wanting to draw with (look at FontMetrics class for this).
Fill the BufferedImage with transparency.
Draw your string onto the BufferedImage with whatever color you want.
Iterate over every pixel in the BufferedImage and see how far away it is from a pixel of your text's color. If it's within a certain distance, draw that pixel black, and maybe more transparent if it's further away from the color of your text. Of course, if the pixel is already the same color as your text color, then ignore it.
Draw BufferedImage onto whatever it is that you're painting onto.
EDIT
There may be libraries out there that already do this, but if I had to code it from scratch, this is how I'd try to do it.
Here is a hacky example. It is not as sophisticated as others, but it is simpler to understand, and it behaves like a JLabel.
public class OutlineLabel extends JLabel {
private Color outlineColor = Color.WHITE;
private boolean isPaintingOutline = false;
private boolean forceTransparent = false;
public OutlineLabel() {
super();
}
public OutlineLabel(String text) {
super(text);
}
public OutlineLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
}
public Color getOutlineColor() {
return outlineColor;
}
public void setOutlineColor(Color outlineColor) {
this.outlineColor = outlineColor;
this.invalidate();
}
#Override
public Color getForeground() {
if ( isPaintingOutline ) {
return outlineColor;
} else {
return super.getForeground();
}
}
#Override
public boolean isOpaque() {
if ( forceTransparent ) {
return false;
} else {
return super.isOpaque();
}
}
#Override
public void paint(Graphics g) {
String text = getText();
if ( text == null || text.length() == 0 ) {
super.paint(g);
return;
}
// 1 2 3
// 8 9 4
// 7 6 5
if ( isOpaque() )
super.paint(g);
forceTransparent = true;
isPaintingOutline = true;
g.translate(-1, -1); super.paint(g); // 1
g.translate( 1, 0); super.paint(g); // 2
g.translate( 1, 0); super.paint(g); // 3
g.translate( 0, 1); super.paint(g); // 4
g.translate( 0, 1); super.paint(g); // 5
g.translate(-1, 0); super.paint(g); // 6
g.translate(-1, 0); super.paint(g); // 7
g.translate( 0, -1); super.paint(g); // 8
g.translate( 1, 0); // 9
isPaintingOutline = false;
super.paint(g);
forceTransparent = false;
}
public static void main(String[] args) {
JFrame w = new JFrame();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
OutlineLabel label = new OutlineLabel("Test", OutlineLabel.CENTER);
label.setOpaque(true);
w.setContentPane(new JPanel(new BorderLayout()));
w.add(label, BorderLayout.CENTER);
w.pack();
w.setVisible(true);
}
}
some stupidest workarounds:
-type same words twice but one of them is black and the other is white, put white on top of the black one, you may get something similar.
-find a font looks like above the example, and use it.
Well, I have an image that I would like to put as a background to a button (or something clicable). The problem is that this image is round, so I need to show this image, without any borders, etc.
The JComponent that holds this button has a custom background, so the button really needs to only show the image.
After searching Google, I couldn't manage to do so. I have tried all the following, but with no luck:
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.setOpaque(true);
And after I paint the icon at the background, the button paints it, but holds an ugly gray background with borders, etc. I have also tried to use a JLabel and a JButton. And to paint an ImageIcon at it, but if the user resizes or minimizes the window, the icons disappear!
How can I fix this?
I just need to paint and round an image to a JComponent and listen for clicks at it...
Create a new Jbutton:
JButton addBtn = new JButton("+");
addBtn.setBounds(x_pos, y_pos, 30, 25);
addBtn.setBorder(new RoundedBorder(10)); //10 is the radius
addBtn.setForeground(Color.BLUE);
while setting the border for a JButton, call the overridden javax.swing.border.Border class.
addBtn.setBorder(new RoundedBorder(10));
Here is the class
private static class RoundedBorder implements Border {
private int radius;
RoundedBorder(int radius) {
this.radius = radius;
}
public Insets getBorderInsets(Component c) {
return new Insets(this.radius+1, this.radius+1, this.radius+2, this.radius);
}
public boolean isBorderOpaque() {
return true;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
g.drawRoundRect(x, y, width-1, height-1, radius, radius);
}
}
Did you try the following?
button.setOpaque(false);
button.setFocusPainted(false);
button.setBorderPainted(false);
button.setContentAreaFilled(false);
setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); // Especially important
setBorder(null) might work, but there is a bug described at Sun explaining it is by design that the UI sets a border on a component unless the client sets a non-null border which does not implement the UIResource interface.
Rather than the JDK itself setting the border to an EmptyBorder when null is passed in, the clients should set an EmptyBorder themselves (a very easy workaround). That way there is no confusion about who's doing what in the code.
I wrote an OvalButton class that can handle oval, circular and capsule-like shaped JButtons.
In your case, extend the OvalButton class and override getBackgroundImage() method to return the image you want to set as the background.
Then add listeners and text as usually. Only a click on the oval/circular area triggers the action.
Example of your button class:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageButton extends OvalButton {
private BufferedImage image;
public ImageButton() {
super(); // Default is oval/circle shape.
setBorderThickness(0); // Oval buttons have some border by default.
try {
image = ImageIO.read(new File("your_image.jpg")); // Replace with the path to your image.
}
catch (IOException e) {
e.printStackTrace();
image = null;
}
}
#Override
protected BufferedImage getBackgroundImage() {
return image;
}
}
I would recommend overriding paint(Graphics g) method as so:
class JImageButton extends JComponent implements MouseListener {
private BufferedImage img = null;
public JImageButton(BufferedImage img) {
this.img = img;
setMinimumSize(new Dimension(img.getWidth(), img.getHeight()));
setOpaque(false);
addMouseListener(this);
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
}
You can try the following. It works fine for me, and I also faced the same issue with the button.
// Jbutton
JButton imageButton = new JButton();
// Buffered Icon
BufferedImage buttonIcon = null;
try {
// Get the image and set it to the imageicon
buttonIcon = ImageIO.read(getClass().getClassLoader().getResource("images/login.png"));
}
catch(Exception ex) {
}
// Set the image icon here
imageButton = new JButton(new ImageIcon(buttonIcon));
imageButton.setBorderPainted(false);
imageButton.setContentAreaFilled(false);
imageButton.setFocusPainted(false);
imageButton.setOpaque(false);
Drag a normal button to your panel
Right click your button and go to properties:
border = no border
border painted = false
contentAreaFilled = false
focusPainted = false
opaque = false
Set an (icon) and a (rolloverIcon) by importing to project.
Opacity should be set to false, so
button.setOpaque(false);
could already be what you want.
I just had the same problem and answer of #Lalchand inspired me. I created custom class for rounded borders, that is actually a modified version of LineBorder class. It draws two rectangles: inner and outer. For my case it is ok if border is colored like a background, but if you need something else, you should tinker with detentions of inner and outer. And as creative liberty I used subpixel rendering for smoother borders.
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
class RoundedBorder extends LineBorder {
private int radius;
RoundedBorder(Color c, int thickness, int radius) {
super(c, thickness, true);
this.radius = radius;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
// adapted code of LineBorder class
if ((this.thickness > 0) && (g instanceof Graphics2D)) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
Color oldColor = g2d.getColor();
g2d.setColor(this.lineColor);
Shape outer;
Shape inner;
int offs = this.thickness;
int size = offs + offs;
outer = new RoundRectangle2D.Float(x, y, width, height, 0, 0);
inner = new RoundRectangle2D.Float(x + offs, y + offs, width - size, height - size, radius, radius);
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
path.append(outer, false);
path.append(inner, false);
g2d.fill(path);
g2d.setColor(oldColor);
}
}
}
You can create an empty border to the button like this:
button.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));