I've created a JToggleButton and when it is selected the image is shown. It all works perfectly except the image isn't centered on the button. The top left corner of the image is on the center point of the button and then the image goes down and out towards the bottom right corner of the button.
JToggleButton LayoutButton = new JToggleButton();
LayoutButton.setIcon(new ImageIcon());
LayoutButton.setSelectedIcon(new ImageIcon("Image.png"));
Any ideas how I center the image?
Thanks
The problem is that your initial image does not match the dimensions of the selected image so, the selected image will appear in a different location, bottom right in this case.
You could create a placeholder for your initial 'unselected' image:
public class PlaceHolderIcon implements Icon {
private final int width;
private final int height;
public PlaceHolderIcon(int width, int height) {
this.width = width;
this.height = height;
}
public int getIconHeight() {
return height;
}
public int getIconWidth() {
return width;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
}
}
and replace your first zero-dimension image with:
ImageIcon selectedIcon = new ImageIcon("Image.png");
Image image = selectedIcon.getImage();
PlaceHolderIcon placeHolderIcon = new PlaceHolderIcon(image.getWidth(this), image.getHeight(this));
JToggleButton layoutButton = new JToggleButton();
layoutButton.setIcon(placeHolderIcon);
layoutButton.setFocusPainted(false);
layoutButton.setSelectedIcon(selectedIcon);
You should use the setHorizontalAlignment(...) and setVerticalAlignment(...) methods of JToggleButton (actually of AbstractButton). Pass SwingConstants.CENTER in as the parameter to center all.
Though note that the default value for the horizontalAlignment and verticalAlignment properties already are SwingConstants.CENTER. So if this doesn't help, consider posting a small compilable and runnable program that uses images off of the net readily available to us to show us your problem, an sscce as well as posting images of what your current buttons look like.
Related
Now I create gui music player with java. In java.swing.JButton, there are square buttons , but I want to customize that button like music player's button. How to make buttons like 'Play Button' in music player? And I also want stop and reset buttons, too.
play button is like ▶ this shape in circle.
stop button is like || this shape in circle.
reset button is like ■ this shape in circle.
thanks for your comment
You could simply set the text of the JButton as the symbol ▶
JButton button = new JButton("▶");
You need to save the .java file with UTF-8 character set though, in eclipse it's really easy as you get a popup.
It's the easiest but least customizable solution.
Another workaround would be to create an image with whatever symbol you wish the button to show. Then add a rectangle to the image's bounds. To check for mouse clicks, simply use a MouseListener and do something similar to this:
if(mouse.isClicked() && rect.contains(mouse.x, mouse.y) { //do stuff }
You can set the play like image to the JButton.
Save your image named "playname" to "path/to/image/" and call as shown in this code:
// JButton play = new JButton();
// assuming play is the place where you've added your JButton
ImageIcon playimg = new ImageIcon("path/to/image/playname");
play.setIcon(playimg);
You can similarly add the same logic for other buttons too.
You could make the button have an image in i, or make the image a button itself.
You can do it by setting the immage you want to the Button. This way you can have a button with Play Icon!
Example:
JButton button= new JButton();
ImageIcon img = new ImageIcon("imgfolder/name.png");
button.setIcon(img);
If you want to make custom button, there is only one easy way to do it. First way is find a custom button library or make your button from image.
Else you can try look on this another post.
Alternatively to using rendered images (like a PNG) and an ImageIcon, you could use Java2D Shapes/Areas.
E.g. to create a Play Area, do something like this:
final GeneralPath play = new GeneralPath();
play.moveTo(1.0/5.0, 1.0/5.0);
play.lineTo(1.0/5.0, 1.0*4.0/5.0);
play.lineTo(1.0*4.0/5.0, 1.0/2.0);
play.closePath();
final Area playArea = new Area(play);
To draw the shape as Icon, use this custom class:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
public class ShapeIcon implements Icon {
private final Shape shape;
private final Paint paint;
private final Color color;
private final int size;
private final boolean fill;
private final Stroke stroke;
public ShapeIcon(final Shape shape, final Color color, final int size) {
this(shape, color, size, true, new BasicStroke(0.5f));
}
public ShapeIcon(final Shape shape, final Color color, final int size, final boolean fill, final Stroke stroke) {
this.stroke = stroke;
this.fill = fill;
this.color = color;
// allow for customization of fill color/gradient
// a plain color works just as well—this is a little fancier
this.paint = new GradientPaint(0, 12, color.brighter(), 0, 20, color);
this.size = size;
// you could also define different constructors for different Shapes
if (shape instanceof Path2D) {
this.shape = ((Path2D)shape).createTransformedShape(AffineTransform.getScaleInstance(size, size));
} else if (shape instanceof Area) {
this.shape = ((Area) shape).createTransformedArea(AffineTransform.getScaleInstance(size, size));
} else {
this.shape = new Area(shape).createTransformedArea(AffineTransform.getScaleInstance(size, size));
}
}
#Override
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
final Graphics2D g2d = (Graphics2D)g.create();
g2d.translate(x, y);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (fill) {
g2d.setPaint(paint);
g2d.fill(shape);
}
g2d.setPaint(color);
g2d.setStroke(stroke);
g2d.draw(shape);
g2d.dispose();
}
#Override
public int getIconWidth() {
return size;
}
#Override
public int getIconHeight() {
return size;
}
}
Use the setBorder method of JButton.
roundButton.setBorder(new RoundedBorder(10));
I am doing the initial section of a simple platform game in Java. I have created a class called entity which extends JPanel and successfully added it to the window.
import javax.swing.*;
import java.awt.*;
/**
* Created by bw12954 on 27/05/16.
*/
public abstract class Entity extends JPanel {
private final SpriteSheet sprites;
private Point location;
private Dimension dimensions;
public Entity(int x, int y, int w, int h, SpriteSheet sprites)
{
location = new Point(x, y);
dimensions = new Dimension(w, h);
this.sprites = sprites;
}
public Entity(int x, int y, int w, int h)
{
this(x, y, w, h, null);
}
#Override
public Dimension getPreferredSize()
{
return dimensions;
}
public void setLocation(int x, int y)
{
location.setLocation(x, y);
}
/* Some code removed here for brevity */
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(sprites.get(),
(int)location.getX(),
(int)location.getY(),
null);
}
}
If I add this directly to the JFrame as below, then the graphic shows up on the window as I would expect it to (note that Player is a very simple subclass of Entity)
public class Window {
private JFrame window;
public Window()
{
SwingUtilities.invokeLater(this::run);
}
private void run()
{
try {
window = new JFrame();
window.setDefaultCloseOperation(window.EXIT_ON_CLOSE);
window.setLocationByPlatform(true);
window.setUndecorated(true);
Player p = new Player(0,0);
window.add(p);
window.setExtendedState(JFrame.MAXIMIZED_BOTH);
window.setVisible(true);
} catch (IOException e) {
// TODO handle exception
e.printStackTrace();
}
}
}
However - when I create a class called World which also extends JPanel, add that to the window instead, then use the add() method in its constructor to add a new Player to it, it doesn't appear. Interestingly, if I add setBackground() to the constructor for Player/Entity, I can see a coloured square where the entity SHOULD be. Its just that drawImage doesn't appear to work.
If anyone else has any idea what is going on here, it will be greatfully appreciated!
If I add this directly to the JFrame as below, then the graphic shows up on the window as I would expect it to
The default layout manager of the content pane of the frame is a BorderLayout. Any component added to the frame without a constraint will be added to the "CENTER" which means the component is automatically resized to fill the entire space.
However - when I create a class called World which also extends JPanel, add that to the window instead, then use the add() method in its constructor to add a new Player to it, it doesn't appear.
The default layout manager of a JPanel is a FlowLayout, which respects the preferred size of any component added to it and will reset the location of the component based on the rules of the layout manager.
Interestingly, if I add setBackground() to the constructor for Player/Entity, I can see a coloured square where the entity SHOULD be. Its just that drawImage doesn't appear to work
Probably because your preferred size calculation is incorrect and the image is truncated.
location = new Point(x, y);
dimensions = new Dimension(w, h);
The above code will only work if the Point is (0, 0). The more general case code should be:
location = new Point(x, y);
dimensions = new Dimension(x + w, y + h);
because you need to consider where you actually paint the image realative to the component.
So when you do this you should see the image, however you will not see the image in the proper location because the layout manager will override the location.
So it you want to continue with this approach of using components you will need to use a null layout, which means you will manually need to use the setSize(...) and setLocation(...) of each component. In this case the size of the component will be the (w, h) and you will draw the image using:
g.drawImage(sprites.get(), 0, 0, this);
Note however if you use the component approach there is no need to even create a custom component. You could just use a JLabel with an ImageIcon. You would assign the Icon when you create the label.
I want to create a windows 7 loading bar on the taskbar. something like this:
I already have a jframe frame where a game loads in it.
I want to make the loadingbar show the progress of downloading the cache of the game. The jframe and the downloading are handled in two seperate classes.
When I looked on the web, I found 2 solutions.
SWT: where you can create the loadingbar, but I think you can't combine that with a jframe.
bridj: which is possible to add to jframe, but I don't have any idea how to do this with an existing jframe and the progress and the jframe handled in two different classes.
There is not a direct solution to what you ask since the progress task bar is OS unique, but trashgod actually got this answered at his post here.
You could create an icon which is affected by your progress & update it, so the taskbar will show what you ask for.
I was inspired, so had to indicate it in case OP didn't catch this. Nice job!
private static class ProgressIcon implements Icon {
private static final int H = 16;
private static final int W = 3 * H;
private Color color;
private int w;
public ProgressIcon(Color color) {
this.color = color;
}
public void update(int i) {
w = i % W;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(color);
g.fillRect(x, y, w, H);
}
public int getIconWidth() {
return W;
}
public int getIconHeight() {
return H;
}
}
Here is what I am trying to do. I have extended JButton and overwritten the paintComponent method creating my desired effect of a rounded edge button, and a color fading effect when the button is rolled over by a mouse. All that works great. My problem is that the JButton is still painting a white rectangle area as the images show.
I would like 1) the white corners to be gone and 2) the cetner of the button to show the panel behind it. Here is what I have tried:
1- when painting the button use getParent().getBackground() and paint the button first. This works great for opaque panels. However I would love this button to work on a partially or fully transparent panel. With transparent panels it paints the color, but on the white background hiding anything behind the panel (like an image).
2- I have tried many combinations of setOpaque(false) or setContentAreaFilled(false). I have tried this while calling super.paintComponent(g) and not calling it. None of those seem to work.
3- The button looks right when I don't use the method g2.clearRect(0,0,width,height) (clearing the graphics area before painting), but since the graphics object is never covered up the fade effect stops working after one rollover of the button.
4- I use a JLabel for the text and have tried setting it opaque or just not using it and the issue still remains. so I don't think that is the issue.
Since I only want an affect for the JButton and not other swing components I'm really hoping to avoid making my own ButtonUI.
Thanks and I hope this makes sense. Below is the code for my button.
import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.*;
/**
* WButton.java
*
* An extension of JButton but with custom graphics
*
*/
public class WButton extends JButton{
private Timer timer;
private float[] background = {.3f,.6f,.8f,0f};
private boolean fadeUp = true;
private boolean fadeDown = false;
private JLabel label;
/**
* Default Constructor
*/
public WButton(){
super();
label = new JLabel();
setupButton();
}
/**
* Text constructor
*/
public WButton(String text){
super(text);
label = new JLabel(text);
setupButton();
}
/**
* common setup functions
*/
private void setupButton(){
timer = new Timer(24,new TimerAction(this));
label.setLabelFor(this);
add(label);
}
/**
* Set the background color
*/
#Override
public void setBackground(Color bg){
background = bg.getRGBComponents(background);
background[3] = 0f;
super.setBackground(new Color(background[0],background[1],
background[2],background[3]));
repaint();
}
/**
* get background
*/
#Override
public Color getBackground(){
if(background!=null)
return new Color(background[0],background[1],background[2],background[3]);
return new Color(.5f,.5f,.5f);
}
/**
* Set the font of the button
*/
#Override
public void setFont(Font font){
super.setFont(font);
if(label!=null)
label.setFont(font);
}
/**
* Override the set text method
*/
#Override
public void setText(String t){
super.setText(t);
if(label!=null)
label.setText(t);
}
/**
* Paint the button
*/
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
Graphics2D g2 = (Graphics2D)g;
g2.clearRect(0,0,width,height);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//Check Button Model state
if(model.isPressed())
paintPressedButton(g2,width,height);
else{
if(model.isRollover()){
if(fadeUp){
fadeUp = false;
timer.start();
}
}
else{
if(fadeDown){
fadeDown = false;
timer.start();
}
}
g2.setPaint(new Color(background[0],background[1],background[2],background[3]));
g2.fillRoundRect(0,0,width-1,height-1,height,height);
}
}
/**
* Draw a pressed button
*/
private void paintPressedButton(Graphics2D g2,int width,int height){
float[] temp = new float[4];
for(int i=0;i<background.length;i++)
temp[i] = background[i]-.4f < 0f ? 0f : background[i]-.4f;
g2.setPaint(new Color(temp[0],temp[1],temp[2],temp[3]));
g2.fillRoundRect(0,0,width-1,height-1,height,height);
}
/**
* paint the border
*/
public void paintBorder(Graphics g){
int width = getWidth();
int height = getHeight();
g.setColor(Color.BLACK);
g.drawRoundRect(0,0,width-1,height-1,height,height);
}
/**
* Inner action listener class
*/
private class TimerAction implements ActionListener{
private float alphaInc = .2f;
WButton button;
public TimerAction(WButton b){
button = b;
}
public void actionPerformed(ActionEvent e){
if(model.isRollover()){
background[3] += alphaInc;
if(background[3] > 1.0f){
timer.stop();
background[3] = 1.0f;
fadeDown = true;
}
}
else{
background[3] -= alphaInc;
if(background[3] < 0f){
timer.stop();
background[3] = 0f;
fadeUp = true;
}
}
button.repaint();
}
}
}
EDIT 1
What aly suggested got me closer, but not quite there. Instead of g2.clearRect() I painted the object with a transparent color as suggested. The white box is gone, but a different color is there. Upon investigation is is the color of the parent panel but with no transparency. Here are pictures for an example (the panel has 70% transparency). The first pictures is when the program starts. The second picture is after 1 mouse rollover.
What you could do is instead of using clearRect(), clear the background with a completely transparent color.
g2.setColor(new Color(0,0,0,0));
g2.drawRect(0,0,width,height);
You still need to setOpaque(false) on the JButton so that it doesn't use the blue rollover color as the background once you hover over it once.
Edit: After seeing what you just posted, I think the problem is that the main frame isn't repainted.
Try:
SwingUtilities.getWindowAncestor(this).repaint();
in the paint method to repaint the frame, that might fix the problem.
The problem here is, swing uses paintImmediately(x, y, width, height) on JButton to repaint the changed area. If you look inside that method, it iterates through the parent hierarchy (if parent is not opaque) until it finds an opaque component. Then call repaint on it. As you may notice, this approach don't take the alpha component in background color into account because those components are opaque (isOpaque = true).
To address this issue, you can override the paintImmediately() method in JButton as follows:
#Override
public void paintImmediately(int x, int y, int w, int h) {
super.paintImmediately(x, y, w, h);
Component component = this;
boolean repaint = false;
while (!component.isOpaque() || (component.getBackground() != null && component.getBackground().getAlpha() < 255)) {
if (component.getBackground() != null && component.getBackground().getAlpha() < 255) {
repaint = true;
}
if (component.getParent() == null) {
break;
}
component = component.getParent();
if (!(component instanceof JComponent)) {
break;
}
}
// There is no need to repaint if no component with an alpha component in
// background is found and no parent component is found (implied by component != this)
// since super.paintImmediately() should have handled the general case.
if (repaint && component != this) {
component.repaint();
}
}
This method will check for the parent of the topmost component which is either not opaque or has a transparent background and repaints it. Performance point of view, this is much better than the currently accepted answer (which is redrawing the entire window, everytime when a button is hovered/pressed).
I have a picture that details what I want to achieve for all values of Game.height. Problem is my mathematical expression in the variable circle_dy seems to be incorrect when it comes to scale the image's location for all values of Game.height.
// images of the game
private Image circle;
// ball's spawning location
private int circle_dx = 0;
private int circle_dy = (Game.height/2) + 30 ;
public class GameBoard{
public GameBoard(){
// construct an ImageIcon specified by the given string directory
ImageIcon circle = new ImageIcon("src/pics/circle.png");
// get image type of the ImageIcon and assign it into the image instance //variable
this.circle = circle.getImage();
setBackground(Color.black);
}
// appropriate method to paint is paintComponent
public void paintComponent(Graphics g){
// if you are overriding a method call the super class paintComponent method
// because you are creating your own version of the paintComponent method
super.paintComponent(g);
// draw the image itself at a particular location on the JPanel
g.drawImage(this.circle,circle_dx,circle_dy,this);
}
}
public class Game extends JFrame{
public static final int width = 600;
public static final int height = 200;
public Game(){
// add the JPanel to the jframe
add(new GameBoard());
setVisible(true);
// set the size of the jframe
setSize(width,height);
}
}
As a suggestion:
Write a listener for windows resize that will update the size of your ball.
To calculate the required size changes, grab the x and y sizes of your window (would suggest saving them each time listener finishes). Work out the old and new values as a % then scale your ball by the same %.
For reference: http://docs.oracle.com/javase/tutorial/uiswing/events/componentlistener.html