3

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);
        }
    }

}

enter image description here

mre
  • 43,520
  • 33
  • 120
  • 170
  • 1
    Not sure I understand the requirement. What about the border of the button? If you want the text outside of the border then I would think you need to use your approach. Or are you trying to get the text inside the border? If you get rid of the border then why do you use a button? Do you need mouse support? Post your SSCCE that demonstrates what you currently do so we can see the exact look you are trying to achieve. – camickr Nov 14 '11 at 00:04

3 Answers3

5

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:

  1. 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.

  2. 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.

  3. 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);
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • 2
    So, I recommend you keep doing what your are doing or use the CompoundIcon. – camickr Nov 14 '11 at 01:26
  • Not sure what approach you used, but I posted a CompoundIcon solution. – camickr Nov 14 '11 at 01:45
  • nice solution :-) Though: don't let the TextIcon draw the string manually (tons of aliasing problems ahead), instead let a label configured with the text paint itself to a BufferedImage and render that image. – kleopatra Nov 14 '11 at 11:06
  • @kleopatra, Right again. I just closely compared the rendering and the TextIcon is not exactly the same. However, I just found out that JDK6 now supports "desktop rendering hints". When I use these properties the rendering is now the same. Newer version of TextIcon is available. – camickr Nov 14 '11 at 17:34
  • there is no way you can get the string rendering exactly like the internals would do (the exact rendering hints vary grossly across devices, may or may not depend on both OS and LAF). Methods that would do it for you are package private or even hidden deep into the sun classes. So the general advice is to not even try, you will be bitten sooner or later (was there ;-). Instead let the internals do their stuff for JLabel and grab the image. – kleopatra Nov 14 '11 at 22:27
  • @kleopatra, I'm more confused than ever. I started chasing down how JButton does its rendering. The best I can tell is that it eventually invokes `SwingUtilities2.drawString(...)` to render the text. source code: http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-sun/swing/sun/swing/SwingUtilities2.java.htm. It appears that the code will just invoke `Graphics.drawString()`. Since the painting of the Icon and the text is done with the same Graphics object and method the rendering should be the same. I'll try to whip a a SSCCE tomorrow. – camickr Nov 15 '11 at 06:46
  • if you like bottomless pits :-) – kleopatra Nov 15 '11 at 09:45
  • @camickr, don't forget that even though JButton eventually invokes SwingUtilities2.drawString which invokes Graphics.drawString, there may be rendering hints set somewhere that affect how the text is drawn. – Jason Aug 21 '15 at 15:45
2

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

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
-1

Find another LayoutManager, here: http://download.oracle.com/javase/tutorial/uiswing/layout/visual.html

Cg2916
  • 1,117
  • 6
  • 23
  • 36
  • 3
    As I said above, I already implemented such a solution. I'm looking to see if `JButton` is capable of doing this without me having to do a wholesale replacement of the layout manager. – mre Nov 13 '11 at 19:43