I want to set text above and below a JButton's icon. At the moment, in order to achieve this, I override the layout manager and use three JLabel instances (i.e. 2 for text and 1 for the icon). But this seems like a dirty solution.
Is there a more direct way of doing this?
Note -I'm not looking for a multi-line solution, I'm looking for a multi-label solution. Although this article refers to it as a multi-line solution, it actually seems to refer to a multi-label solution.
EXAMPLE
import java.awt.Component;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public final class JButtonDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new JMultiLabelButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static final class JMultiLabelButton extends JButton
{
private static final long serialVersionUID = 7650993517602360268L;
public JMultiLabelButton()
{
super();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JCenterLabel("Top Label"));
add(new JCenterLabel(UIManager.getIcon("OptionPane.informationIcon")));
add(new JCenterLabel("Bottom Label"));
}
}
private static final class JCenterLabel extends JLabel
{
private static final long serialVersionUID = 5502066664726732298L;
public JCenterLabel(final String s)
{
super(s);
setAlignmentX(Component.CENTER_ALIGNMENT);
}
public JCenterLabel(final Icon i)
{
super(i);
setAlignmentX(Component.CENTER_ALIGNMENT);
}
}
}
There is not way to split the text between the top/bottom of a JButton. This would involve custom painting.
Since I'm not sure of your exact requirement I'll just through out a few random ideas:
You can use a JButton with text & icon. There are methods in the API that allow you to controal where text is positioned relative to the icon. Then you would need a second label for the other line of text. Basically the same as you current solution but you only need two labels.
You could use the Text Icon and Compound Icon classes to create 1 Icon out of 3 separate Icons. Then you can just add the icon to a button.
Use a JTextPane. Its supports an insertIcon() method. So you could add a line of text, add the icon and then add the other line of text. You can play with the paragraph attributes of the text pane to align the text horizontally within the space if you don't want the text left justified. You can also play with the background color to make it look like a label.
Example using the CompoundIcon:
import java.awt.*;
import javax.swing.*;
public final class JButtonDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JButton button = new JButton();
CompoundIcon icon = new CompoundIcon(CompoundIcon.Axis.Y_AXIS,
new TextIcon(button, "Top Label"),
UIManager.getIcon("OptionPane.informationIcon"),
new TextIcon(button, "Bottom Label"));
button.setIcon( icon );
button.setFocusPainted( false );
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add( button );
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
you have two four options
1) use JLabel + Icon + Html (<= Html 3.2)
2) use XxxButtonUI and override all required methods from API
3) JLayeredPane with translucency???, another Layout with translucency, as JLabel or JComponent to the JButton,
4) there are around lots of Graphics SW that can to prepare Background as *.jpg for Icon, then is very simple to change whatever by Event, Action or actual setting for JButton
not correct way is looking for JLabel + Whatever instead of JButton, I think that is halfsized workaround
Find another LayoutManager, here: http://download.oracle.com/javase/tutorial/uiswing/layout/visual.html
Related
What's wrong? ImageIcon and the frame's size are working properly.
But the JTextField and the JButton aren't.
I need the solution.
import javax.swing.*;
import javax.swing.ImageIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Frame {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Alkalmazás");
frame.setVisible(true);
frame.setSize(500,500);
frame.setResizable(false);
JTextField field = new JTextField();
field.setBounds(40,250, 300,35);
JButton button = new JButton(new ImageIcon("table.png"));
button.setBounds(40,400, 250,25);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
tf.setText(""something);
}
});
frame.add(field);
frame.add(button);
}
}
You didn't mention what's "not working properly", but there are a few errors with your code:
Don't call your class Frame, it may confuse you or others about java.awt.Frame, something that may work would be MyFrame
Right now all your class is inside the main method and it's not placed inside the Event Dispatch Thread (EDT), to fix this, create an instance of your class and call a method createAndShowGUI (or whatever you want to name it) inside SwingUtilities.invokeLater()
For Example:
public static void main(String args[]) {
SwingUtilities.invokeLater(new MyFrame()::createAndShowGUI)
}
Or if using Java 7 or lower, use the code inside this answer in point #2.
setVisible(true) should be the last line in your code, otherwise you may find some visual glitches that may be resolved until you move your mouse above your window or something that triggers the call to repaint() of your components.
Instead of calling setSize(...) directly, you should override getPreferredSize(...) of your JPanel and then call pack() on your JFrame, see this question and the answers in it: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
You're adding 2 components to the CENTER of BorderLayout, which is a JFrame's default layout manager, there are other layout managers and you can combine them to make complex GUI's.
setBounds(...) might mean that you're using null-layout, which might seem like the easiest way to create complex layouts, however you will find yourself in situations like this one if you take that approach, it's better to let Swing do the calculations for you while you use layout managers. For more, read: Why is it frowned upon to use a null layout in Swing?
With all the above tips now in mind, you may have a code similar to this one:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class MyFrame {
private JFrame frame;
private JPanel pane;
private JTextField field;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MyFrame()::createAndShowGUI);
}
private void createAndShowGUI() {
frame = new JFrame("Alkalmazás");
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
field = new JTextField(10);
button = new JButton("Click me");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
field.setText("something");
}
});
pane.add(field);
pane.add(button);
frame.add(pane);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Now you have an output similar to this one:
What about you want the JTextField to have a more "normal" size? Like this one:
You'll have to embed field inside another JPanel (with FlowLayout (the default layout manager of JPanel)), and then add that second JPanel to pane, I'm not writing the code for that as I'm leaving that as an exercise to you so you learn how to use multiple layout managers
I have been practicing my code with Java Swing and have gotten decent on controlling where to place some items, such as labels and or buttons, but I was wondering if you can do the same with classes? I have just a simple class with enough code to put a button in it and that's it, that I am trying to create an instance of the class and then control for to put on the left and right side but when I do, all it does is create two separate windows with the button in the middle and that's it. Am I doing something wrong, or can you not do classes the same way?
The code:
import java.awt.Color;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Fun extends JFrame
{
private final int WIDTH = 500;
private final int HEIGHT = 400;
public Fun()
{
setTitle("Fun Management");
setSize(WIDTH, HEIGHT);
BuildPanel west = new BuildPanel(); /// BuildPanel is the name of the class that has just a button in it.
BuildPanel east = new BuildPanel(); ///
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(west, BorderLayout.WEST); /// I am doing the same thing with the instances as I would with buttons or labesl
add(east, BorderLayout.EAST);
setVisible(true);
}
public static void main(String[] args)
{
new Fun();
}
}
I took your code and created the following GUI.
Oracle has a rad tutorial, Creating a GUI With Swing, that will show you how to create Swing GUIs. Skip the Netbeans section.
Always start your Swing application with a call to the SwingUtilities invokeLater method. This method ensures that your Swing components are created and executed on the Event Dispatch Thread.
Use Swing components. Don't extend a Swing component unless you want to override one or more of the component methods.
The JFrame methods must be called in a specific order. This is the order I recommend for most Swing applications. Use the JFrame pack method and let the components size the JFrame.
I created a BuildPanel class to build a JPanel. There are good reasons to do this, but be careful. You have to manage each instance of the class you create. As an example, what if you want the text of the two buttons to be different? What if you want to assign two different ActionListener classes, one to each button?
Here's the complete runnable code. I made the BuildPanel class an inner class so I can post the code as one block.
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TwoPanelExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TwoPanelExample());
}
#Override
public void run() {
JFrame frame = new JFrame("Fun Management");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BuildPanel west = new BuildPanel();
BuildPanel east = new BuildPanel();
frame.add(west.getPanel(), BorderLayout.WEST);
frame.add(east.getPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class BuildPanel {
private final JPanel panel;
public BuildPanel() {
this.panel = createMainPanel();
}
private JPanel createMainPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 30, 5, 30));
JButton button = new JButton("Click Me");
panel.add(button);
return panel;
}
public JPanel getPanel() {
return panel;
}
}
}
The following example creates a JFrame with JButton, JTextField and JLabel.
When the button is pressed it increments the value in the text field and label.
This example also creates a 2nd JFrame that is a copy of the first.
The button, text field and label is copied as well.
The issue at hand is the button on the copied frame still updates the text field and label on the original. The 'why' is fairly obvious and is because the code makes specific reference to the text field and label.
Although this isn't written in the best manner but it is a great example of the scenario in which I am addressing.
The objective is, without a major rewrite, what would be the least invasive way to have the copied button action update the copied test field and label instead of the original?
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class ButtonTextFieldLabel extends JFrame
{
JButton bnt1 = new JButton("B1");
JTextField tf1 = new JTextField("1");
JLabel lbl1 = new JLabel("100");
public ButtonTextFieldLabel()
{
super("Main Frame");
setLayout(null);
bnt1.setBounds(50,100,120,40);
tf1.setBounds(300,100, 80,40);
lbl1.setBounds(200,100,80,40);
bnt1.addActionListener(new ListenerHolder(this));
add(bnt1);
add(tf1);
add(lbl1);
setSize(500,500);
makeCopy(this);
setVisible(true);
}
private void makeCopy(ButtonTextFieldLabel originalObj)
{
JFrame copyFrame = new JFrame();
copyFrame.setTitle("Copy of " + originalObj.getTitle());
copyFrame.setSize(originalObj.getSize());
copyFrame.setLocation(originalObj.getX()+100, originalObj.getY()+100);
copyFrame.setLayout(null);
JButton copyBnt1 = new JButton();
copyBnt1.setBounds(originalObj.bnt1.getBounds());
copyBnt1.setLabel(originalObj.bnt1.getLabel());
copyFrame.add(copyBnt1);
for (ActionListener al : originalObj.bnt1.getActionListeners())
{
copyBnt1.addActionListener(al);
}
JTextField copyTf1 = new JTextField();
copyTf1.setBounds(originalObj.tf1.getBounds());
copyTf1.setText(originalObj.tf1.getText());
JLabel copyLbl1 = new JLabel();
copyLbl1.setBounds(originalObj.lbl1.getBounds());
copyLbl1.setText(originalObj.lbl1.getText());
copyFrame.add(copyBnt1);
copyFrame.add(copyTf1);
copyFrame.add(copyLbl1);
copyFrame.setVisible(true);
}
public void runThis()
{
tf1.setText( Integer.toString(Integer.parseInt(tf1.getText())+1) );
lbl1.setText( Integer.toString(Integer.parseInt(lbl1.getText())+1) );
}
}
class ListenerHolder implements ActionListener
{
ButtonTextFieldLabel ph;
public ListenerHolder(ButtonTextFieldLabel ph)
{
this.ph = ph;
}
#Override
public void actionPerformed(ActionEvent arg0)
{
ph.runThis();
}
}
public class TestBTL
{
public static void main(String[] args){
new ButtonTextFieldLabel();
}
}
You already know the reason for the problem -- you're copying the original ActionListener, complete with its reference to the original GUI components. The overall solution is not to copy the action listener but rather to create your GUI's to hold and maintain their own unique state. One solution is rather than try to copy components via kludge, to create a self-contained GUI object that holds and updates its own state. You can create multiple GUI's using a factory method if desired.
For example:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class TestBtl2 {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndDisplayFrame("Frame 1").setVisible(true);
createAndDisplayFrame("Frame 2").setVisible(true);
});
}
// Factory method
private static JFrame createAndDisplayFrame(String text) {
BtlPanel btlPanel = new BtlPanel();
JFrame frame = new JFrame(text);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(btlPanel);
frame.pack();
frame.setLocationByPlatform(true);
return frame;
}
}
class BtlPanel extends JPanel {
private int value = 0;
private JButton button1 = new JButton(new ButtonAction("Button 1"));
private JLabel label1 = new JLabel("00");
private JTextField textField1 = new JTextField("00");
public BtlPanel() {
textField1.setFocusable(false);
add(button1);
add(Box.createHorizontalStrut(20));
add(label1);
add(Box.createHorizontalStrut(20));
add(textField1);
setPreferredSize(new Dimension(300, 100));
}
public void incrementValue() {
value++;
String text = String.format("%02d", value);
label1.setText(text);
textField1.setText(text);
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
incrementValue();
}
}
}
Side Recommendations:
While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Check out: The Use of Multiple JFrames, Good/Bad Practice?
I've made a JFrame with Diferent JButtons and i'd like to get an image from another class. Any ideas? Or how draw on the same class but on the action performed?
Because it doesnt let me to do any drawings...my complier always gives me error messages
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.*;
public class red extends JFrame {
public JButton b;
public JButton b1;
public JButton b2;
public JButton b3;
public JButton b4;
public static Image p;
public static Graphics g;
public red() throws IOException {
gui1 x = new gui1();
setTitle(" ");
setSize(1200,700);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
b= new JButton("click");
b1= new JButton();
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e0){
b1.setBounds(0, 0, 200, 200);
b.show(false);
add(x);
}
});
b.setBounds(0, 0, 100, 100);
add(b1);
add(b);
setVisible(true);
}
public static void main(String[] args) throws IOException {
red k = new red();
}
}
import java.awt.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class gui1 extends Canvas {
public static Image p;
public void paint(Graphics g){
g.drawImage(p, 700, 200, 100, 100, this);
}
{
try {
p= ImageIO.read(new File("Lighthouse.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Phew! I see A LOT of errors in your code (even after I corrected the compilation errors):
You're not following the Java naming conventions:
Class names should be nouns, in mixed case with the first letter of each internal word capitalized
while red is a noun it should be more descriptive and be capitalized. The same goes for gui1
You're extending JFrame which in plain english would say: red is a JFrame, you should really avoid this and create your GUI based on JPanels instead... see Java Swing using extends JFrame vs callint it inside of class
You're setting size (a REAAAAAAALLY big one window for the JButton sizes you're using), instead use pack()
You're using null-layout, while pixel-perfect GUIs might seem like the easiest way to create complex GUIs for Swing newbies, the more you use them the more problems related to this you'll find in the future, they are hard to maintain and cause random problems, they don't resize, etc. Please read Null layout is evil and Why is it frowned upon to use a null layout in Swing? for more information about why you should avoid its use and why you should change your GUI to work with Layout Managers along with Empty Borders for extra spacing between components.
You're making use of a deprecated method JFrame#show() you should be using JFrame#setVisible(...) instead.
Related to point #4, you shouldn't be calling setBounds(...) method, but let that calculations to the layout managers.
You're not placing your program on the Event Dispatch Thread (EDT), Swing is not thread safe, you can fix this by changing your main() method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're mixing AWT and Swing components, instead of using AWT's Canvas use Swing's JPanel which has more functionality and support.
Images will become embedded resources once they're packaged in a JAR file, so it's wise to start treating them as if they already were, not as external files as shown in the embedded-resource tag.
Once you change from Canvas to JPanel you should override its paintComponent(...) method and not paint(...) and call it's super.paintComponent(g) method as the first line, also don't forget to add the #Overrides annotation. See the tutorial on Swing custom painting.
You're abusing the use of static keyword, see how does the static keyword works?
After seeing all the above errors I recommend you to go back and Learn the basics of the language before starting with a graphical environment which will only add more difficulty to your learning.
From what I understand you want to draw an image on a button click, if that's the case then you can wrap your image in a JLabel and add that JLabel to a JPanel which then is added to a parent JPanel which is later added to the JFrame:
As you can see in the GIF above, the icon is displayed after user presses the button.
Obviously this can be improved for the GUI to be more "attractive" with combinations of layout managers and empty borders as stated before.
This was done with the following code:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ImageDrawingFromOneClassToAnother {
private JFrame frame;
private JPanel pane;
private JPanel leftPane;
private JPanel rightPane;
private ImageIcon icon;
private JButton button;
private JLabel label;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageDrawingFromOneClassToAnother().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
icon = new ImageIcon(this.getClass().getResource("king.png")); //Read images as if they were already embedded resources
button = new JButton("Draw image");
label = new JLabel(""); //Create an empty label
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setIcon(icon); //On button click, we set the icon for the empty label
}
});
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200); //Set a size for the main panel
}
};
pane.setLayout(new GridLayout(1, 2)); //The main panel
leftPane = new JPanel(); //The button panel
leftPane.setLayout(new BoxLayout(leftPane, BoxLayout.PAGE_AXIS));
leftPane.add(button);
rightPane = new JPanel(); //The panel where the image will be drawn
rightPane.add(label);
//We add both (button and image) panels to the main panel
pane.add(leftPane);
pane.add(rightPane);
frame.add(pane); //Add the main panel to the frame
frame.pack(); //Calculate its preferred size
frame.setVisible(true); //Set it to be visible
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I'm working on a memory game program. I have 30 JButtons on a JPanel. When the user is clicking and finds a match (meaning two buttons with the same image) I want to change the image on the JButton to a different image. However this does not happen while the program is running.
How can I do this?
I was doing this:
cards[i].setIcon(cardBack);
where cardBack is an ImageIcon that I already have.
you can use this code:
Icon i=new ImageIcon("image.jpg");
jButton1.setIcon(i);
and copy your image (image.jpg) to your project folder!
Use a JToggleButton. More specifically, use the setIcon and setSelectedIcon methods. Using this approach, you'll avoid reinventing the wheel.
Example:
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
final class JToggleButtonDemo {
public static final void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
}
private static final void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout()); // For presentation purposes only.
final JToggleButton button = new JToggleButton(UIManager.getIcon("OptionPane.informationIcon"));
button.setSelectedIcon(UIManager.getIcon("OptionPane.errorIcon"));
frame.add(button);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}