Java Tips Weblog

  • Blog Stats

    • 2,189,673 hits
  • Categories

  • Archives

  • Advertisements

Stretch Icon

Posted by Darryl Burke on March 31, 2012

An implementation of the Icon interface reports its size via the two methods getIconWidth() and getIconHeight().  It’s normal to expect that the Icon’s paintIcon(Component, Graphics, int, int) method will respect these bounds.  If I had designed Swing’s interaction with Icons, I would have made sure of that by setting a clip to the Graphics passed to paintIcon(…).

Luckily for me, I didn’t design Swing, as that would have made StretchIcon impossible.

StretchIcon is an Icon that scales its image to fill the component area, excluding any border or insets, optionally maintaining the image’s aspect ratio by padding and centering the scaled image horizontally or vertically.  Since the component now determines the size of the Icon, rather than the other way round, StretchIcon can be used only in conjunction with a component whose size is determined by the size and layout of the container it is placed in; for example, a JLabel placed in the BorderLayout.CENTER of a JFrame.

Using a StretchIcon is a viable alternative to extending a JComponent or JPanel solely to paint a custom background.  The class can also improve the aesthetics of JLabels or JButtons in a resizable GridLayout, as in a game board. For both use cases, the image chosen should be of a size equal or near the maximum anticipated size of the component, since drawing an image larger than its natural size can lead to pixelation.

StretchIcon is a drop-in replacement for ImageIcon, which it extends, except that ImageIcon’s no-arg constructor isn’t supported. That’s because I feel that an ImageIcon without an Image is an oxymoron.

Get The Code

StretchIcon.java

See Also

Shrink Icon
Thumbnail Icon

Related Reading

Java API: javax.swing.Icon
Java API: javax.swing.ImageIcon
The Java™ Tutorials: How to Use Icons

Advertisements

20 Responses to “Stretch Icon”

  1. Gildas said

    That worked great, thank you!

  2. Bill h said

    So worked great on windows, exactly what I needed. Having a little issue on Mac OS X though. It seems that when you press and hold on a button with the StretchIcon in it, there is a phantom image in the background that becomes visible. It seems like it is something from ImageIcon that maybe the StretchIcon doesn’t take into account?!?! It’s a bit strange….trying to see if I can figure out a work around currently. Thanks again though for the original code!!

    • Bill H said

      Tried to fix it and boiled it down to the MacOS System Look and Feel. No other OS or L&F caused this issue (tried Linux, Metal, Nimbus, Motif, Windows 8). For now I just switched those buttons to be Nimbus when the OS is Mac as those are pretty close looking. Would love to figure this out, but couldn’t figure out how to dig deeper into the ImageIcon class and the L&F behavior when a JButton is pressed.

  3. bspkrs said

    Thanks for making this, saved me from having to figure out the math myself! I did make one small alteration that is highlighted in this gist: https://gist.github.com/bspkrs/bd4b368204d710256bc7#file-stretchicon-java-L319-L330

    This change produces better quality scaled images :D

  4. Thanks for sharing your code. Could you please add a suitable license to it, so it can be used by others without worry?

  5. if (proportionate) {
    int iw = image.getWidth(c);
    int ih = image.getHeight(c);

    if (iw * h < ih * w) {
    iw = (h * iw) / ih;
    x += (w – iw) / 2;
    w = iw;
    } else {
    ih = (w * ih) / iw;
    y += (h – ih) / 2;
    h = ih;
    }
    }

    Can you please explain this math logic ? i tried to understand it but couldn't.

  6. Thomas said

    Thanks for this. One problem using JButton though. It seems that it tries to keep the icon /inside/ the margins. This is a problem if I want the Icon to cover the button wall to wall, while /keeping/ the margins. Setting the JButton margins to 0/0/0/0 doesn’t help (in, say, FlowLayout), because then I predictably get an icon just as large as the text itself only (That’s the true size of the Jbutton).

    Can I get the icon to spread to the edges of the button, while ignoring the margin insets?

    • Thomas said

      I’m sorry, what I wrote might have been confusing. The mention of FlowLayout was just my testing vehicle….what’s important here is that the (JButton).setMargin(new Insets(0,0,0,0)) will cause a button that is precisely the size of the text, as it should.

      If I keep the insets there, the icon is still the size of the text: It seems to be taking its size cues from AbstractButton in such a way that the size given it is not including the space around it.

      I would like the margins to be there still (so that there is a gutter around the text), but have the icon flood to the edges.

      • Read the second sentence of the third paragraph at the top of this page carefully. Yes, the icon *is* taking its size cues from the button.

        For your second requirement, maybe you could put something together using Rob Camick’s TextIcon and my DualIcoan.

        https://tips4java.wordpress.com/2009/04/02/text-icon/
        https://tips4java.wordpress.com/2009/03/22/dual-icon/

        Darryl

      • Thomas said

        Yes, but it’s that exact sentence that is the problem with my one (and only) requirement. The layout manager /always/ determines how the object is sized and placed, even the “null layout manager” does this. It turns out that BorderLayout is one of the few that completely ignores the requests of the component (getPreferredSize(), getMin/MaxSize()) entirely. If I use a GridBagLayout for instance, I can stretch the button, but if the button has required specific sizes, it’ll obey those sizes and simply pad around the button within a stretched space. This is different from BorderLayout which IMHO is a bad example.

        However even in the case of FlowLayout, if I have default JButtons, they appear as standard height. The height is managed properly. If I then plop in a StretchIcon, it attempts to make the icon the size of the text only.

        Do you know where within the AbstractButton/JButton hierarchy the icon size is determined? It’s likely /that/ place that needs overriding in my case. (I think).

        But then, I’m rapidly closing in on the amount of effort it would take to simply grow my own JButton (or AbstractButton) subclass and blast BufferedImages over the background. That would likely better match my needs anyway. Icons are a crazy sugar coating around them anyway, probably born of what happened before BufferedImage, and other Images that no longer require observers within the component, existed.

      • Thomas said

        Thanks for your replies, but you know what, looking at how icons were always intended to stay within the margins anyway, StretchIcon is similarly going to be the wrong approach.

        I’ve grown customized buttons many times before; it’s easy compared to this.

        It stems from this: Obviously, the text size doesn’t automatically stretch (for good reason). This means that any button margin along with the font size will determine the overall size of the icon (outside of BorderLayout and similar). I’ve misunderstood the (limited) model employed by the AbstractButton).

      • Thomas said

        I mean that StretchIcon is going to be the wrong approach for me and my needs, and similarly that growing my own button is comparatively easy for my needs. Not that StretchIcon is borked in someway.

    • Thomas said

      I mean that StretchIcon is going to be the wrong approach for me and my needs, and similarly that growing my own button is comparatively easy for my needs. Not that StretchIcon is borked in someway.

  7. Anonymous said

    sorry im a beginner. how to use this?

  8. Thomas Fritz said

    Using either StretchIcon or ShrinkIcon instead of normal ImageIcons I get no images drawn onto my JLabels. With ImageIcons though the icons my professor gave are displayed way too largely though.
    Basically I need to write an interface for the game Connect Four, only with variable grid sizes for the board. So the icons are placed onto my main JPanel as JLables based on the grid size chosen by the player. The bigger the board, the more icons will need to be displayed.

    So yeah, any idea why it doesn’t work for me? Because right now I have absolutely none :P

    Warm Regards,

    Thomas Fritz

    • Read the third paragraph at the top of the page:

      … StretchIcon can be used only in conjunction with a component whose size is determined by the size and layout of the container it is placed in …

      I suspect this is not happening with your choice of Layout Manager(s).

      Darryl

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: