Java swing: Models and Button color changes - java

I'm super confused with what exactly models are supposed to do in java swing. At present I'm basically trying to create a model for JButton to detect if it isPressed(); My essential goal of the model is to do something like this:
if(myButton.isPressed() ) {
myButton.setBackground(Color.RED);
}
else{//when any other button is pressed?
myButton.setBackground(Color.WHITE);
}
At present my code is something like this:
numberButton = new JButton("Num");
numberButton.setBounds(20,40,80,30);
numberButton.addChangeListener(new ChangeListener() {
public void stateChanged (ChangeEvent e){
if (model.isPressed() ){
doColorChange(model);
}
}
});
I understand that this is totally wrong, but I have no idea where, and I haven't found a tutorial that really explains what I'm doing wrong or why I need a model for this at all.
Please help me restore my sanity! Thanks a lot!

Use radio buttons in a button group.
import java.awt.image.BufferedImage;
import java.awt.*;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;
class RedAndWhite {
public static Image getColoredImage(Color color, int size) {
BufferedImage bi = new BufferedImage(
size,
size,
BufferedImage.TYPE_INT_RGB);
Graphics g = bi.getGraphics();
g.setColor(color);
g.fillRect(0,0,size,size);
g.dispose();
return bi;
}
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Image red = getColoredImage(Color.RED, 32);
Image white = getColoredImage(Color.WHITE, 32);
JPanel p = new JPanel(new GridLayout(1,0,5,5));
ButtonGroup bg = new ButtonGroup();
for (int ii=0; ii<6; ii++) {
JRadioButton b = new JRadioButton();
b.setSelectedIcon(new ImageIcon(red));
b.setIcon(new ImageIcon(white));
b.setContentAreaFilled(true);
bg.add(b);
p.add(b);
}
JOptionPane.showMessageDialog(null, p);
}
});
}
}

I think what you want is register an ActionListener or Action with the button in order to handle button presses, right?
Edit: rereading your answer, it seems you want to highlight the button that is being pressed, right? In that case, try to use your own button ui (subclass the one of the look and feel you're using). BasicButtonUI has a method protected void paintButtonPressed(Graphics g, AbstractButton b) that you could override to apply the highlight when the button is being pressed.

there are follows ways how to make JButton nicer
1) most nicest way is painting only Borders from ButtonModel on Mouse events
2) override whole BasicButtonUI with all rellevant Look and Feels
3) put there Icons and override all possible methods implemented in JButton API
4) best at all would be implement Custom Look and Feel
5) create Custom JComponent based on JLabel/JComponent

I always took it that the ButtonModel is intended (mostly) for use by the UI class to render the button in its pressed/not-pressed/armed/selected/... state and track changes to that state.
If you simply want to paint the button red (that is waht you want?) while it is pressed your solution seems fine to me.
If you have a Toggle Button (that remains pressed after it has been clicked to indicate a "selected" state, you might want to use an ItemListener and check for
ItemEvent.ITEM_STATE_CHANGED == ItemEvent.SELECTED // paint red
ItemEvent.ITEM_STATE_CHANGED == ItemEvent.DESELECTED // paint white
If you want to execute application logic when the button is clicked, use an ActionListener.

Related

Issue with button icons, Java 8-Queens puzzle

The problem I am running into is this: I have a grid of buttons in a JPanel, these buttons are supposed to change to an image of a queen when I click them. The code looks like this:
private Component createButtonBlack() {
final JButton button = new BoardButton();
final ImageIcon queen = new ImageIcon("/images/queen.png");
button.setBackground(Color.BLACK);
button.setPreferredSize(new Dimension(40, 40));
class QueenClick implements ActionListener {
public void actionPerformed(ActionEvent event) {
button.setIcon(queen);
button.repaint();
}
} // end QueenClick
ActionListener queenClicker = new QueenClick();
button.addActionListener(queenClicker);
return button;
} // end createButtonBlack
The problem (image not appearing) occurs on both the methods for creating black and white buttons but the methods are the same except for the color. Ideally I would like to be able to un-click the buttons and the image disappears but I do not know how to do that.
I am having difficulty with other parts of my 8queens GUI based problem so if you have any suggestions let me know!
Also if you need more code I will certainly supply it. Thank you.
State the exact problem when asking a question.
These buttons are supposed to change to an image of a queen when I click them.
So I'm guessing the icon doesn't change?
Did you:
Verify the ActionListener code is executed?
Verify the Icon was read properly?
You can easily add a System.out.println(...) to verify both of the above.
final ImageIcon queen = new ImageIcon("/images/queen.png");
I'm guessing the problem is the leading "/" in the path. The "/" tells the file system to look at the root of the drive.
if you have any suggestions let me know!
There is no need to create two methods. You can just do:
Component button = createButton();
button.setBackground( Color.BLACK );
There is no need to create individual ActionListeners. You can create a single generic listener with code like:
ActionListener queenClicker = new ActionListener()
{
#Override
public void actionPerformed(Action Event e)
{
JButton button = (JButton)e.getSource();
button.setIcon( queen );
//button.repaint(); // not needed the setIcon method will do the repaint()
}
}

How to move 3 buttons inside a window

In the below code I am attempting to move the three buttons to the left when you click the left button. When I click it; nothing happens currently. Can anyone explain to me what I am doing wrong here? Also, for some reason it has stopped compiling correctly and I am unsure why but I BELIEVE it is because of a mistake in my code while attempting to get the buttons to move to the left when you click the button. I do NOT want the window to move. Just the buttons within the window. Does any one see what I am doing wrong and can you explain it?
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Buttons extends JFrame {
//Control Definitions
JButton resetButton;
JButton leftButton;
JButton colorButton;
JPanel buttonPanel;
// Layout Definiton
eventHandle evt;
FlowLayout flt;
Point point; //to Hold Previous Window Position
Color color; //to Hold Previous Color
public Buttons() {
super("Buttons Window");
flt = new FlowLayout();//inialize the Flow Layout
buttonPanel = new JPanel(flt);
//inialize the buttonPanel With Flow Layout
//initialize buttons
resetButton = new JButton("Reset");
leftButton = new JButton("Left");
colorButton = new JButton("Blue");
evt = new eventHandle(); //initiate the eventhandle class
buttonPanel.add(leftButton); //add leftButton
buttonPanel.add(colorButton);//add colorButton
buttonPanel.add(resetButton);//add colorButton
getContentPane().add(buttonPanel);//buttonPanel
//add actionlistners
leftButton.addActionListener(evt);
colorButton.addActionListener(evt);
resetButton.addActionListener(evt);
setBounds(20, 120, 250, 70);
//following Initate the point with Center of Scren
point = new Point((Toolkit.getDefaultToolkit().
getScreenSize().width - getWidth()) / 2,
(Toolkit.getDefaultToolkit().getScreenSize().height
- getHeight()) / 2);
setLocation(point); //locates the window in center
color = buttonPanel.getBackground();//stores the initial color
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
class eventHandle implements ActionListener { //Event Handler
public void actionPerformed(ActionEvent e) {
{
if (e.getSource() == leftButton) ///if its from leftButton
{
leftButton.setAlignmentX(Component.LEFT_ALIGNMENT);
colorButton.setAlignmentX(Component.LEFT_ALIGNMENT);
resetButton.setAlignmentX(Component.LEFT_ALIGNMENT);
//setLocation( (point.x -150), point.y);//shift the window 150 pixels left
} else if (e.getSource() == colorButton) {
buttonPanel.setBackground(color.BLUE);
//sets the backgorund to Blue
} else {
leftButton.setAlignmentX(Component.CENTER_ALIGNMENT);
//sets the location to previous location
colorButton.setAlignmentX(Component.CENTER_ALIGNMENT);
resetButton.setAlignmentX(Component.CENTER_ALIGNMENT);
}
}
}
}
public static void main(String[] args) {
Buttons buttonwindow = new Buttons();
}
}
It has stopped compiling, because you deleted one accolade, so put one accolade "}" just above the method:
public static void main(String[] args)
and the code should compile. pls feedback.
EDIT:
Also rewrite your main method like this:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Buttons buttonwindow = new Buttons();
}
}
);
}
Every usage of Swing components must be done thorugh the Event Dispatch Thread (abbreviated EDT) or you will probably get unwanted visual effects. See here for explanation.
EDIT^2:
To achieve the desired behavior, rewrite the the action listener like this:
if (e.getSource() == leftButton) {
((FlowLayout)buttonPanel.getLayout()).setAlignment(FlowLayout.LEFT); //1
buttonPanel.revalidate(); //2
}
else if (e.getSource() == colorButton) {
buttonPanel.setBackground(color.BLUE);
}
else {
((FlowLayout)buttonPanel.getLayout()).setAlignment(FlowLayout.CENTER);
buttonPanel.revalidate();
}
Any change to the visual appereance to the Swing component must be done through the assigned layout manager, in this case FlowLayout - in line 1.
To see the change you must notify the Swing components layout manager to rearrange the components - in line 2 the revalidate() method "notifies" the layout manager to recalculate the new positions and eventually "notifies" the EDT to draw it on the screen.
You should update the layout manager to align the components to the left or right. Try something like;
((FlowLayout)getLayout()).setAlignment(FlowLayout.LEFT);
Instead
You code won't compile as the static main method appears inside the inner class eventHandle. You can fix simply by moving it into the class body of the outer class Buttons.
As you have all the objects references at class level, you could do the button alignment using, for instance:
flt.setAlignment(FlowLayout.RIGHT);
buttonPanel.revalidate();
...
Here you are adjusting the layout alignment of your FlowLayout and revalidating to visually reflect the updated changes on your panel.

MouseEvent does not fire when implemented by a JPanel

Okay, so in this program I'm making, users will be able to create shortcuts to their favorite apps on their computer. My program will be kind of like a hub, I guess, for apps. I have a small problem though, which involves two classes: AppButton and AppButtonContainer. They both implement MouseListener, but AppButton extends JComponent and AppButtonContainer extends JPanel. Basically, when an AppButton is clicked, it sets a draws the border in a different color to make it look selected. Otherwise, it sets the border to the background color. When you double click it, it opens up the application specified. I have a method in AppButton to remove the focus, and therefore setting the border to the background color. In AppButtonContainer, there is a bit of code so that when, the panel is clicked, it removes the focus from the AppButton.
That's my problem, though. The AppButtonContainer doesn't realize that it's clicked. I'm thinking it has something to do with top level containers or something, but I'm not sure. Can anybody help?
EDIT: I found out that I didn't put the addMouseListener(this) in the constructor of the AppButtonContainer. Thank you for everyone who helped me clear up this problem and give me tips along the way :)
AppButtonContianer:
public class AppButtonContainer extends JPanel implements MouseListener {
private static final long serialVersionUID = 6485231881729120957L;
public List<AppButton> appButtons;
private static final Color BACKGROUND_COLOR = new Color(18, 18, 18);
public AppButtonContainer(List<AppButton> buttons) {
this.appButtons = buttons;
setLayout(new GridLayout(5, 5, 20, 20));
addButtonsToPane();
}
private void addButtonsToPane() {
List<AppButton> buttons = this.appButtons;
for (int i = 0; i < buttons.size(); i++) {
this.add(buttons.get(i));
}
}
private void removeAllButtonFocus() {
for (int i = 0; i < this.appButtons.size(); i++) {
this.appButtons.get(i).removeFocus();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(BACKGROUND_COLOR);
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pane Clicked");
removeAllButtonFocus();
}
...Other MouseEvent methods
You can solve the problem at hand by putting addMouseListener(this) in the constructor for your AppButtonContainer class. Otherwise, it'll never pick up mouse events.
Generally, though, it's not good to turn your classes into mouselisteners like that. Perhaps try making an inner class to listen for mouse events and pass them to the AppButtonContainer instead.

How to set the button color of a JButton (not background color)

I have a JButton that I would like to change the background color of to white. When using the Metal Look And Feel, I achieve the desired effect with setBackground:
Unfortunately, the concept of "background color" is different when using the Windows LAF; the background color is the color drawn around the button:
I would like to use the Windows LAF, but allow the button color of this JButton to be changed to white. How do I do this?
You'll have to decide if it's worth the effort, but you can always create youe own ButtonUI, as shown in this example due to #mKorbel.
I use JDK 6 on XP. It looks like the Window UI doesn't follow the normal painting rules in more ways than 1. As you noticed setBackground() doesn't work. You should be able to do custom painting by telling the component not to fill in the content area:
import java.awt.*;
import javax.swing.*;
public class ButtonBackground extends JFrame
{
public ButtonBackground()
{
setLayout( new FlowLayout() );
JButton normal = new JButton("Normal");
add(normal);
JButton test1 = new JButton("Test 1")
{
#Override
public void paintComponent(Graphics g)
{
g.setColor( Color.GREEN );
g.fillRect(0, 0, getSize().width, getSize().height);
super.paintComponent(g);
}
};
test1.setContentAreaFilled(false);
add(test1);
}
public static void main(String[] args)
{
try
{
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}
catch (Exception e2) {}
ButtonBackground frame = new ButtonBackground();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
When you run the code as is it seems to work properly. That is when you click on the button you see the Border change. However is you run with the Windows XP LAF, the Border never changes to you don't see the button click effect.
Therefore, I guess the issue is with the WindowUI and you would need to customize the UI which is probably too complex to do so I don't have a solution.
but I still think that (modified but by Darryl) is correct UIManager.get("Button.gradient"), because would be crossplatform
EDIT: correct answer would be - Nimbus or some Custom L&F, why reinvent the wheel (by Rob)
Your best option is using SwingX:
JXButton allows you to set a Painter for the background with .setBackgroundPainter(Painter) using a MattePainter achieves exactly what you want. Having that JXButton extends from JButton, the changes are minimal in your code:
Color bg = new Color(new Random().nextInt(16777215)); // Random color
JButton button = new JButton();
button.setBackground(bg);
would become
JXButton button = new JXButton();
button.setBackgroundPainter(new MattePainter(bg));
instead of messing with the button's background color, could you do whatever indication you're trying to show a different way?
displaying an Icon, making the button bold instead of plain text, etc.
Indicating something only through a different background color isn't always obvious, and depending on the user's system colors, may be jarring, or invisible.
for example, what if the user is running windows in "high contrast" mode?

.drawLine on event (button click) Jpanel/Jbutton/JTabbedPane

I know how to do action listeners for button clicks in/on swing, but I have this class which does some stuff but I want it a function/event that when a button is clicked it runs a method similiar to the PaintComponent below... (draws a line)
class CustomPanel extends JPanel {
private int destx = 100;
private int desty = 100;
private int startx = 0;
private int starty = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(startx, starty, destx, desty);
}
}
How would I call this paintcomponent (Or a similar one which draws a line) from a action listener?
Here is my actionlistener: (Its on GUI.java while the code above is on CustomPanel.java)
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == loginButton)
{
//Does other stuff but removed for simplifying
CustomPanel cp = new CustomPanel();
}
}
Thanks alot,
Your question didn't make sense to me in your last posting and it still doesn't make sense to me in this posting.
You still haven't posted a SSCCE that attempts to demonstrate what you want to do.
If you have a "login panel", typically that is done by creating a modal JDialog.
If you are trying to draw a diagonal across the top of all components in the frame, then you would need to use a Glass Pane or a Layered Pane.
Read the section from the Swing tutorial on How to Use Root Panes for examples and more detailed information.
You need to add it to gui. Something like this:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
parentPanel.add(new CustomPanel());
parentPanel.revalidate();
parentPanel.repaint();
}
});
But if you only want to draw a line on the current container that's another thing...
Simply adding your CustomPanel to any other JComponent and updating the UI should do the trick. Swing takes care of all the painting for you.
Here is a reslly useful guide to swing painting;
http://java.sun.com/products/jfc/tsc/articles/painting/#paint_process

Categories