-1

I have a JPanel that has a GridLayout. To this grid layout I add JButton objecs. I want to add background images to the buttons, but I don't know what size to specify the images because I am not able to get the correct size of the buttons in the grid layout. When I call getHeight() and getWidth() as well as getPreferedSize() I get faulty data. How can I know what size to make the images so that I can add them to the buttons?

Here is the way I am trying to implement this - what is missing is adding the images to the button.

public class CustomPanel extends JPanel{

    final int GRIDHEIGHT = 4;
    final int GRIDLENGTH = 6;

    public CustomPanel(){
        super();
        init();
    }

    public void init(){

        setLayout(new GridLayout(GRIDHEIGHT, GRIDLENGTH, 10, 10));
        setBorder(new EmptyBorder(10, 10, 10, 10) );

        for(int i = 0;i<GRIDHEIGHT;i++){
            for( int j=0;j<GRIDLENGTH;j++){
                 button = new JButton("(" + i + ", " + j + ")");

                 add(button);
        }
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
user2871354
  • 530
  • 1
  • 5
  • 15
  • 1) For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete Verifiable Example) or [SSCCE](http://www.sscce.org/) (Short, Self Contained, Correct Example). 2) One way to get image(s) for an example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). – Andrew Thompson Mar 20 '15 at 05:43
  • @NeerajJain [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi) – MadProgrammer Mar 20 '15 at 05:44
  • @NeerajJain That will cause more problems than it solves. Further, if the layout does not honor the preferred size, it won't even solve the stated problem. See [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/q/7229226/418556) (Yes.) – Andrew Thompson Mar 20 '15 at 05:44
  • The size of the component will be variable, based on the properties of the button and the mechanism by which it is rendered by the underlying system. The best you can do is either scale the image accordingly or tile it in some way – MadProgrammer Mar 20 '15 at 05:45
  • @MadProgrammer How would you "scale the image accordingly or tile it in some way"? – user2871354 Mar 20 '15 at 05:58
  • That would depend on entirely how you are setting up your code... – MadProgrammer Mar 20 '15 at 06:01

1 Answers1

3

This is an idea, based on the ideas from Java: maintaining aspect ratio of JPanel background image.

Basically, what this does is creates a special button which takes a "image" as the master image, it then scales this image to fit within the button confines of the button as needed.

Scale

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new CustomPanel());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class CustomPanel extends JPanel {

        final int GRIDHEIGHT = 4;
        final int GRIDLENGTH = 6;

        public CustomPanel() {
            super();
            init();
        }

        public void init() {

            File[] images = new File("Directory full of images").listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    String name = pathname.getName().toLowerCase();
                    return name.endsWith(".jpg") ||
                                    name.endsWith(".png") ||
                                    name.endsWith(".gif");
                }
            });

            List<File> imageList = new ArrayList<>(Arrays.asList(images));
            Collections.shuffle(imageList);

            setLayout(new GridLayout(GRIDHEIGHT, GRIDLENGTH, 10, 10));
            setBorder(new EmptyBorder(10, 10, 10, 10));

            for (int i = 0; i < GRIDHEIGHT; i++) {
                for (int j = 0; j < GRIDLENGTH; j++) {
                    try {
                        BufferedImage img = ImageIO.read(imageList.remove(0));
                        JButton button = new ImageButton(img);
                        add(button);
                    } catch (IOException exp) {
                        exp.printStackTrace();
                    }
                }
            }
        }
    }

    public class ImageButton extends JButton {

        private BufferedImage background;
        private BufferedImage scaled;

        public ImageButton(BufferedImage background) {
            this.background = background;
            setContentAreaFilled(false);
            setBorderPainted(false);
            setFocusPainted(false);
            setOpaque(false);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        public void invalidate() {
            super.invalidate();
            scaled = getScaledInstanceToFit(background, getSize());
        }

        @Override
        protected void paintComponent(Graphics g) {
            if (scaled != null) {
                int x = (getWidth() - scaled.getWidth()) / 2;
                int y = (getHeight() - scaled.getHeight()) / 2;
                g.drawImage(scaled, x, y, this);
            }
            super.paintComponent(g);
        }

    }

    public static BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {

        double scaleFactor = getScaleFactorToFit(img, size);

        return getScaledInstance(img, scaleFactor);

    }

    protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {

        BufferedImage imgScale = img;

        int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
        int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);

//      System.out.println("Scale Size = " + iImageWidth + "x" + iImageHeight);
        if (dScaleFactor <= 1.0d) {

            imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight);

        } else {

            imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight);

        }

        return imgScale;

    }

    protected static BufferedImage getScaledDownInstance(BufferedImage img,
                    int targetWidth,
                    int targetHeight) {

//      System.out.println("Scale down...");
        int type = (img.getTransparency() == Transparency.OPAQUE)
                        ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

        BufferedImage ret = (BufferedImage) img;

        if (targetHeight > 0 || targetWidth > 0) {

            int w = img.getWidth();
            int h = img.getHeight();

            do {

                if (w > targetWidth) {
                    w /= 2;
                    if (w < targetWidth) {
                        w = targetWidth;
                    }
                }

                if (h > targetHeight) {
                    h /= 2;
                    if (h < targetHeight) {
                        h = targetHeight;
                    }
                }

                BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
                Graphics2D g2 = tmp.createGraphics();
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2.drawImage(ret, 0, 0, w, h, null);
                g2.dispose();

                ret = tmp;

            } while (w != targetWidth || h != targetHeight);

        } else {

            ret = new BufferedImage(1, 1, type);

        }

        return ret;

    }

    protected static BufferedImage getScaledUpInstance(BufferedImage img,
                    int targetWidth,
                    int targetHeight) {

        int type = BufferedImage.TYPE_INT_ARGB;

        BufferedImage ret = (BufferedImage) img;
        int w = img.getWidth();
        int h = img.getHeight();

        do {

            if (w < targetWidth) {
                w *= 2;
                if (w > targetWidth) {
                    w = targetWidth;
                }
            }

            if (h < targetHeight) {
                h *= 2;
                if (h > targetHeight) {
                    h = targetHeight;
                }
            }

            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();

            ret = tmp;
            tmp = null;

        } while (w != targetWidth || h != targetHeight);

        return ret;

    }

    public static double getScaleFactorToFit(BufferedImage img, Dimension size) {

        double dScale = 1;

        if (img != null) {

            int imageWidth = img.getWidth();
            int imageHeight = img.getHeight();

            dScale = getScaleFactorToFit(new Dimension(imageWidth, imageHeight), size);

        }

        return dScale;

    }

    public static double getScaleFactor(int iMasterSize, int iTargetSize) {

        double dScale = 1;
        dScale = (double) iTargetSize / (double) iMasterSize;

        return dScale;

    }

    public static double getScaleFactorToFit(Dimension original, Dimension toFit) {

        double dScale = 1d;

        if (original != null && toFit != null) {

            double dScaleWidth = getScaleFactor(original.width, toFit.width);
            double dScaleHeight = getScaleFactor(original.height, toFit.height);

            dScale = Math.min(dScaleHeight, dScaleWidth);

        }

        return dScale;

    }

    public static double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {

        double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
        double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);

        double dScale = Math.max(dScaleHeight, dScaleWidth);

        return dScale;

    }
}

Be warned though, this is very slow. Scaling a number of images can take time. Normally I'd consolidate the invalidate calls so I'm only attempting to scale the image when I really have to...

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366