How to make Round JButtons in Java - java

First, I am a Web Developer and a novice Java programmer.
My boss is asking me to make this button in an application:
My custom button class must extend JButton or BasicButtonUI so that it can be reused.
I did some research on Stack Overflow, but I did not understand the answers, especially with the time restraints from my boss.

You should create your own component for this.
Override the paintComponent method on a JPanel, and inside the paintComponent method draw (ie fill) a rounded rectangle2D in the color gray :
RoundRectangle2D roundedRectangle = new RoundRectangle2D.Float(x, y, w, h, 10, 10);
g.fill(roundedRectangle);
(The last two values determine the curvature. Play around until you get what you want)
Now move the x,y and reduce width and height so that when you draw the next rectangle, it sits inside the gray rectangle. Set the graphics color to blue then do something like this :
RoundRectangle2D roundedRectangle2 = new RoundRectangle2D.Float(x + 5, y + 5, w - 10, h - 10, 10, 10);
g.fill(roundedRectangle2);
You will also need to add text. Adding text requires an x and y position. The exact x and y position can be tricky to calculate, so you may need to use FontMetrics to get some more information about the rectanglar shape of the string. Fontmetrics has methods like stringWidth() and getHeight() which will help you determine what your x and y should be.
g.drawString("Click Me", x, y);
Finally you need to have a mouse motion listener on your panel. The listener needs to find when the mouse is over the button and then redraw the component.
Your rectangle can be cast to a shape object, and a calculation can be made as to whether the mouse is in the shape. Eg :
shape.contains(x,y)
If it contains, change the color, then call repaint() or updateUI() on the panel.
Note: your color object should be kept as a class level field in the class, so that it can be changed via the mouseover.
Hope this helps!

If you don't want to draw the images by yourself using the graphics API or you can't becaue the images come from a graphic designer, than you can use them as ImageIcon objects and use setRolloverIcon() and setIcon().
In this case I would do it this way
class ButtonRollover {
private String normalImagePath;
private String rolloverImagePath;
public ButtonRollover(String normalImagePath, String rolloverImagePath) {
this.normalImagePath = normalImagePath;
this.rolloverImagePath = rolloverImagePath;
}
public void apply(AbstractButton abstractButton) {
abstractButton.setBorderPainted(false);
abstractButton.setBackground(new Color(0, 0, 0, 0));
abstractButton.setRolloverIcon(createImageIcon(rolloverImagePath));
abstractButton.setIcon(createImageIcon(normalImagePath));
}
private ImageIcon createImageIcon(String path) {
java.net.URL imgURL = getClass().getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
}
and than use it. E.g.
public class Main extends JFrame {
public static void main(String[] args) {
Main main = new Main();
main.setBackground(Color.WHITE);
main.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
main.setSize(640, 480);
Container contentPane = main.getContentPane();
ButtonRollover buttonRollover = new ButtonRollover("/bt_normal.png",
"/bt_hover.png");
JButton btn = new JButton();
buttonRollover.apply(btn);
contentPane.add(btn);
main.setVisible(true);
}
}
Just put the image files in the classpath.

There are ways to do it.
1) JButton has inbuilt API setIcon. You could set ImageIcon here.
2) You could add mouse listener (Mouse entered, Mouse exited) change the icons to the needed ones.
3) Make a button round - Refer for creating the curvy buttons.

public class Main extends JFrame {
public static void main(String[] args) {
Main main = new Main();
main.setBackground(Color.WHITE);
main.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
main.setSize(640, 480);
Container contentPane = main.getContentPane();
ButtonRollover buttonRollover = new ButtonRollover("/bt_normal.png",
"/bt_hover.png");
JButton btn = new JButton();
buttonRollover.apply(btn);
contentPane.add(btn);
main.setVisible(true);
}
}

Related

JPanel dropdown menu cut off

I'm practicing Java by creating a text analyzing program (word count, TF-IDF, etc).
I'm now constructing a small GUI to be displayed at the top of the frame. I want a dropdown menu, and I'm trying to create one using a JPanel and several JButton components. But to make the dropdown reusable, I've been trying to construct a class to manage it.
However, I encountered a problem: when I try to create the dropdown menu it's only visible in the upper left corner of the frame, and if I try to move it, it gets cut off or disappears completely.
I tried creating a simple method to do the same thing, but the problem remains the same. Here's the relevant code (ignore the processor and State stuff):
public class ExperimentState extends State {
private JFrame frame;
public ExperimentState(Processor processor) {
super(processor);
initialize();
}
private void initialize() {
int width, height;
//these variables are temporary, for testing
width = 800;
height = 640;
//Set up the frame
frame = new JFrame();
frame.setSize(width, height);
frame.getContentPane().setLayout(null);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Test code
String[] buttonNames = { "42", "Don't panic!", "Towel"};
// x, y, width, height, button names
JPanel panel = createPanel(0, 0, 100, 25, buttonNames);
frame.getContentPane().add(panel);
}
public JPanel createPanel(int x, int y, int width, int height, String[] names) {
//Create and setup the panel
JPanel panel = new JPanel();
panel.setLayout(null);
panel.setBounds(x, y, width, height);
JButton[] buttons = new JButton[names.length];
for(int i = 0; i < names.length; i++) {
JButton button = new JButton(names[i]);
button.setBounds(x, y + height*i, width, height); //each button has the size of the panel, and are placed on top of each other
panel.add(button);
buttons[i] = button;
}
buttons[0].addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
panel.setBounds(x, y, width, height*names.length); //the panel extends to reveal all the buttons
}
public void mouseExited(MouseEvent e) {
panel.setBounds(x, y, width, height); //the panel retracts
}
});
return panel;
}
#Override
public void display() {
frame.setVisible(true);
}
#Override
public void hide() {
frame.setVisible(false);
}
}
I thought there would be a problem with the mouse listener, since I don't really know how they work and thought they wouldn't be able to access the x, y, width and height variables anytime they were called, but there was no problem.
The problem appears when I call createPanel() with something like createPanel(50, 0, 100, 25, buttonNames).
Here, x is 50 instead of 0. When displayed, the panel is moved to the right, as it should be, but its buttons are now cut in half by some invisible line. If x = 100, it will go completely invisible.
Why is this happening? What am I missing?
Components inside a Container will only completely show (without a LayoutManager) if the bounds of these Components are completely inside it. By changing the x-coordinate from 0 to 50 you move the location of the Button. If you do not change its size or the size of your container your Button will be cut off.
The easiest way to fix this problem is to replace x and y in the location of your button with 0 and replace the 0‘s in the location of the Panel with x and y.
EDIT: Similar Problem here: Components not showing in second JPanel

Trouble Using MouseMotionListener

I just started coding just a while back. I'm trying to make a FruitNinja type game right now using a JPanel and I've pretty much done with everything(with the exception of the loops that make the game go on and the rest of the fruit/bomb animations)
So far, I've only animated the Apple.png (will get to animating other fruits once I'm done with this)
I'm trying to make a checkHit method that checks whether or not my Mouse goes over the image(slicing through the fruit), thus the MouseMotionListener that needs to be used. If the mouse does go over the fruit, I need it to disappear/move off the frame.
So how do I retrieve the coordinates from MouseMotionListener, check to see if it is within 40 pixels of my fruit and if it is, to move that fruit of the frame? If possible, not only to do that but to also play a sound when that is done.
I've got another user to help me out with this issue but I still can't quite figure it out.
import javax.swing.*;//imports JPanel class
import java.awt.*;//imports the Graphics class
import java.awt.event.*;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
//imports packages needed for the hovering
public class FruitNinja extends JPanel{
private Image dojo;
private Image apple;
private Image orange;
private Image pear;
private Image strawberry;
private Image banana;
private Image bomb;
//declares the import image variables
private javax.swing.Timer timer;
private int dx, dy;
private int vx =40;
//velocity of the object moving in the x direction
private int x=-10;
private int vy=40;
//velocity of the object moving in the y direction
private int y=770;
private int divide=(int)(2*Math.random())+1;//sets random number
//determines when the fruit will bounce off
public FruitNinja() { // a constructor to set up graphics windo
super();
setBackground(Color.WHITE);
loadImage();
dx = 25;
dy = 25;
timer = new javax.swing.Timer( 30, new TimerListener() );
//interval of 10 milliseconds
timer.start();
//timer is a facility for threads to schedule tasks for future execution in a background thread.
}
private void loadImage() {
ImageIcon ii = new ImageIcon("Dojo.jpg");
dojo = ii.getImage();
ImageIcon oo = new ImageIcon("Orange.ico");
orange = oo.getImage();
ImageIcon ss = new ImageIcon("Strawberry.png");
strawberry = ss.getImage();
ImageIcon bb = new ImageIcon("Banana.png");
banana = bb.getImage();
ImageIcon pp = new ImageIcon("Pear.png");
pear = pp.getImage();
ImageIcon aa = new ImageIcon("Apple.png");
apple = aa.getImage();
ImageIcon bo=new ImageIcon("Bomb.png");
bomb=bo.getImage();
//loads neccaseary images for this game
}#Override
public void paintComponent(Graphics g){ // draw graphics in the panel
super.paintComponent(g);// to make panel display correctly
g.drawImage(dojo, 0,0, this);
//draws out dojo
g.drawImage(apple, x,y, this);
g.drawImage(orange, -300,-300, this);
g.drawImage(pear, -300,-300, this);
g.drawImage(banana, -300,-300, this);
g.drawImage(strawberry, -300,-300, this);
//draws out the fruits somewhere
g.drawImage(bomb,-300,-300,this);
//draws out the bomb that will make the player lose
}
private class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//implements the ActionListenter
x+=vx; //adds velocity to the x, which makes it travel in x direction
if (x<0){
vx=(-1*vx);
//tells the velocity to change direction
}
y+=vy; //adds velocity to the y, which makes it travel in y direction
if (y>=getHeight()||y<=getHeight()/4){
vy=(-1*vy);
//tells the velocity to change direction
}
repaint(); // causes paintComponent to be called by the JVM
}
}
public static void main(String[] args) {
FruitNinja panel = new FruitNinja(); // window for drawing
JFrame f = new JFrame(); // the program itself
f.setTitle("Fruit Ninja");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//when the X button is clicked, the program quits
f.setSize(1280,800);//size of the frame
Container pane = f.getContentPane();//pane refers to the interior of the JFrame
FruitNinja p1 = new FruitNinja();
pane.add(p1);//add the FacePanel object to the interior of the frame
f.setVisible(true);
}
}
Please let me know if I'm doing anything wrong with what I have here or/and how I can use this MouseMotionListner inside it. I'm still quite new to this, so I don't know how to retrieve the coordinates or set up the 'MouseMotionListener'. If my question is unclear, feel free to go into the comments and let me know if that is the case; I'm willing to explain the issue more thoroughly.
Thanks
Edit:
Is this the method?
private class HandleMouse extends MouseAdapter {
//extends MouseAdapter rather than implement Mouse Adapter
public void mousePressed( MouseEvent e ){
Rectangle hitBox = new Rectangle(e.getX() - 20, e.getY() - 20, 40, 40);
Rectangle appleHitBox=new Rectangle(ax-128,ay-128,256,256);
Area area = new Area(hitBox);
area.intersect(new Area(appleHitBox));
if(!area.isEmpty()){
ax=-500;
ay=-500;
avx=0;
avy=0;
}
else{
numOfMisses++;
}
}
}
So how do I retrieve the coordinates from MouseMotionListener,
MouseMotionListener passes a MouseEvent to the method, which contains the various properties of the event, this includes the coordinates of the event in the component coordinate context
check to see if it is within 40 pixels of my fruit and if it is
So, this is basic collision detection, there is a number of possible ways you might approach this, but because I'm lazy, I might consider using Rectangle to represent the user hit box
Rectangle hitBox = new Rectangle(e.getX() - 20, e.getY() - 20, 40, 40);
This generates a box 20 pixels around the center point of the MouseEvent.
"But why?" you ask? Because Rectangle has a number of helpful methods which can be used for collision detection including
intersection
intersects
union
to name a few
So given the x/y position and size of the fruit, you can simply create a second Rectangle and determine if the first hits the second
and if it is, to move that fruit off the frame?
Well, this would be a process of setting up a flag, which the "main loop" (ie your TimerListener) that tells it that the fruit has been hit, it's now up to the "main loop" to remove it from the "paint" list and setup the next object
If possible, not only to do that but to also play a sound when that is done
As a general answer, is. Once you been able to detect that a collision has occured, playing a sound is relatively simple, I'd be tempted, again, to use the "main loop" to do this as well, when it detects that a hit has occured

How does the opaque property work in Swing?

This a simple application I got from here this answer to How to set a Transparent Background of JPanel
that's supposed to explain how setOpaque() works.
public class TwoPanels {
public static void main(String[] args) {
JPanel p = new JPanel();
// setting layout to null so we can make panels overlap
p.setLayout(null);
CirclePanel topPanel = new CirclePanel();
// drawing should be in blue
topPanel.setForeground(Color.blue);
// background should be black, except it's not opaque, so
// background will not be drawn
topPanel.setBackground(Color.black);
// set opaque to false - background not drawn
topPanel.setOpaque(false);
topPanel.setBounds(50, 50, 100, 100);
// add topPanel - components paint in order added,
// so add topPanel first
p.add(topPanel);
CirclePanel bottomPanel = new CirclePanel();
// drawing in green
bottomPanel.setForeground(Color.green);
// background in cyan
bottomPanel.setBackground(Color.cyan);
// and it will show this time, because opaque is true
bottomPanel.setOpaque(true);
bottomPanel.setBounds(30, 30, 100, 100);
// add bottomPanel last...
p.add(bottomPanel);
// frame handling code...
JFrame f = new JFrame("Two Panels");
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
// Panel with a circle drawn on it.
private static class CirclePanel extends JPanel {
// This is Swing, so override paint*Component* - not paint
protected void paintComponent(Graphics g) {
// call super.paintComponent to get default Swing
// painting behavior (opaque honored, etc.)
super.paintComponent(g);
int x = 10;
int y = 10;
int width = getWidth() - 20;
int height = getHeight() - 20;
g.fillArc(x, y, width, height, 0, 360);
}
}
}
The thing the I don't get is how come he is adding the opaque layer on top of transparent layer? shouldn't be the other way around?
The way that I picture how it should work is by adding the transparent layer on top of the opaque one, kinda of like how you put a screen protector over a phone(sorry for the dumb example)
Can someone please explain how transparency works in java?
I apologize of my question is a bit naive but this has been bothering me for a while!
Yes, the example reliest on the fact that with a null layout, the child components are indeed drawn in reverse order. An implementation dependency. That at least deserves mention. Adding a visible border would make it more evident:
private static class CirclePanel extends JPanel {
CirclePanel() {
setBorder(BorderFactory.createLineBorder(Color.RED));
}

Painting a custom JButton and a text line

I'm designing an optimization system for public transport in a big city. So I have a map with some points on it, but don't care about it)
All I need is: my own JButton, which looks like a color-filled circle and a small text tag near it. I got some problems while overriding the paintComponent() method.. the round button is painted correctly, but not the text.
BUT, when i'm resizing the window manually, the text appears for a second, then it gets repainted again and dissapears.
Hope you guys understood my needs, thanks for help ;)
import java.awt.*;
import javax.swing.*;
public class JRoundButton extends JButton {
String label;
Color color;
int x,y;
public JRoundButton(Color color,int x,int y,String str)
{
label=str;
this.x=x;
this.y=y;
this.color=color;
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Dimension size = getPreferredSize();
setPreferredSize(size);
this.setBounds(0, 0, 10, 10);
setContentAreaFilled(false);
g.setFont(new Font("Arial",Font.BOLD,14));
g.drawChars(label.toCharArray(), 0, label.length(), 12,12);
g.fillOval(0,0,8,8);
}
public void paintBorder(Graphics g)
{
g.setColor(Color.white);
g.drawOval(0,0, 9, 9);
}
public static void main(String[] args)
{
JButton button = new JRoundButton(Color.GRAY,150,150,"Times Square");
JFrame frame = new JFrame();
frame.getContentPane().setBackground(Color.black);
frame.setSize(300, 300);
frame.setVisible(true);
frame.add(button);
}
}
Seems that the call to 'setBounds( 0, 0, 10, 10 )' sets a component footprint that is too small to accomodate the text string. Extending the bounds to 100px wide and bringing down the point size to 6 looks to work okay.
1) NEVER set properties of the button in the paintComponent() method.
Dimension size = getPreferredSize();
setPreferredSize(size);
this.setBounds(0, 0, 10, 10);
setContentAreaFilled(false);
Get rid of the above code.
2) Dont set the Font of the Graphics object in the paintComponent() method. Thats what the setFont(...) method is used for.
3) There is no need to do any custom painting. If you want a circle, then add an Icon to the JLabel.
4) Don't override the paintBorder() method. If you want a Border then create a custom border and add it to the button using the setBorder() method.
In short there is no need to extend the button. Get rid of your JRoundButton class. Your code should simply look something like:
JButton = new JButton("Times Square");
button.setFont( new Font("Arial",Font.BOLD,14) );
button.setIcon( new OvalIcon(Color.WHITE, iconSize) );
Of course you will need to create an OvalIcon class but that is easy to implement since there are only three methods and you already know what the painting code should be.
I'd just cheat and use a unicode circle in the JButton's text. E.g.:
import javax.swing.*;
JFrame frame = new JFrame();
frame.getContentPane().add(new JButton("<html><font size='+10' color='red'>●</font> I'm next to a red circle!</html>"));
frame.pack();
frame.show();

Java - Custom Shape Panels?

I am working on an application that involves the user requiring to hover over several moving dots on the screen in order to launch specific popups. At the moment, i am listening for mouseMoved events on the JPanel onto which the dots are rendered, and then launching the required pop ups whenever the cursor is within a specific distance of a dot.
When i have hundreds of dots - this probably becomes quite expensive.
Wouldnt the ideal solution be to represent my 'dots' as small components and register a mouse listener with each dot?
Does anyone know how i might represent a small ellipse with a JComponent?
Many thanks
Here is some old code which shows how to create a "round" JButton. I would extend JComponent instead and the important methods to override are paintComponent() and contains():
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class RoundButton extends JButton {
public RoundButton(String label) {
super(label);
// These statements enlarge the button so that it
// becomes a circle rather than an oval.
Dimension size = getPreferredSize();
size.width = size.height = Math.max(size.width, size.height);
setPreferredSize(size);
// This call causes the JButton not to paint the background.
// This allows us to paint a round background.
setContentAreaFilled(false);
}
// Paint the round background and label.
protected void paintComponent(Graphics g) {
if (getModel().isArmed()) {
// You might want to make the highlight color
// a property of the RoundButton class.
g.setColor(Color.lightGray);
} else {
g.setColor(getBackground());
}
g.fillOval(0, 0, getSize().width-1, getSize().height-1);
// This call will paint the label and the focus rectangle.
super.paintComponent(g);
}
// Paint the border of the button using a simple stroke.
protected void paintBorder(Graphics g) {
g.setColor(getForeground());
g.drawOval(0, 0, getSize().width-1, getSize().height-1);
}
// Hit detection.
Shape shape;
public boolean contains(int x, int y) {
// If the button has changed size, make a new shape object.
if (shape == null || !shape.getBounds().equals(getBounds())) {
shape = new Ellipse2D.Float(0, 0, getWidth(), getHeight());
}
return shape.contains(x, y);
}
// Test routine.
public static void main(String[] args) {
// Create a button with the label "Jackpot".
JButton button = new RoundButton("Jackpot");
button.setBackground(Color.green);
button.setBounds(0, 0, 100, 100);
JButton button2 = new RoundButton("Jackpot2");
button2.setBackground(Color.red);
button2.setBounds(50, 50, 100, 100);
// Create a frame in which to show the button.
JFrame frame = new JFrame();
frame.getContentPane().setBackground(Color.yellow);
frame.getContentPane().setLayout(null);
frame.getContentPane().add(button);
frame.getContentPane().add(button2);
// frame.getContentPane().setLayout(new FlowLayout());
frame.setSize(200, 200);
frame.setVisible(true);
MouseListener mouseListener = new MouseAdapter() {
public void mouseEntered( MouseEvent e )
{}
public void mouseExited( MouseEvent e )
{}
public void mouseClicked( MouseEvent e )
{
System.out.println( "clicked " );
}
public void mousePressed( MouseEvent e )
{
System.out.println( "pressed " );
}
public void mouseReleased( MouseEvent e )
{
System.out.println( "released " );
}
};
button.addMouseListener( mouseListener );
}
}
This simplifies the hit detection easily since there is no custom code. Also it allows you to control overlapping of compnents easily since you can control the Z-Order of each component.
You write "this probably becomes quite expensive"
Whether you code the "isMouseCloseToDot" method yourself, or whether you use something built into Swing, in either case the work still needs to be performed by the computer to find out whether a dot is activated.
I recommend sticking with your current approach unless you have determined that this approach is indeed too expensive. Do a small test with a couple of hundred dots. Is the response time acceptable?
Not sure which approach is more expensive, but component based approach is definitely easier to implement.
All you have to do is to create your own component, override paint and contains methods. Add it to the specific position in the container (using absolute layout or any other depending on your needs). Your listeners will become part of your new component providing components behavior.
From the design and program organization point of view this is much more superior approach.

Categories