java – What is a good way to make an application selector layout?

I, personally, would start with a JList with a HORIZONTAL_WRAP

See How to Use Lists for more details

For example…

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Image;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    List<Game> games = new ArrayList<>(Arrays.asList(new Game[]{
                        new Game("Assiassins Creed - Black Flag", ImageIO.read(getClass().getResource("/boxart/AssassinsCreedBlackFlag.png"))),
                        new Game("Assiassins Creed - Unit", ImageIO.read(getClass().getResource("/boxart/AssassinsCreedUnity.png"))),
                        new Game("Battlefield 3", ImageIO.read(getClass().getResource("/boxart/BattleField3.png"))),
                        new Game("BioShock", ImageIO.read(getClass().getResource("/boxart/BioShock.png"))),
                        new Game("Borderlands", ImageIO.read(getClass().getResource("/boxart/BorderLands.png"))),
                        new Game("Dead standing", ImageIO.read(getClass().getResource("/boxart/DeadStanding.png"))),
                        new Game("Dishonored", ImageIO.read(getClass().getResource("/boxart/Dishnonored.png"))),
                        new Game("Fallout 4", ImageIO.read(getClass().getResource("/boxart/Fallout4.png"))),
                        new Game("Far Cry Primal", ImageIO.read(getClass().getResource("/boxart/FarCryPrimal.png"))),
                        new Game("Halflife", ImageIO.read(getClass().getResource("/boxart/HalfLife.png"))),
                        new Game("Jedi Fallen Order", ImageIO.read(getClass().getResource("/boxart/JediFallenOrde.png"))),
                        new Game("Kings Quest VII", ImageIO.read(getClass().getResource("/boxart/KingsQueteVII.png"))),
                        new Game("Leisure Suit Larry in the Land of the Lounge Lizards", ImageIO.read(getClass().getResource("/boxart/LSL.png"))),
                        new Game("Mass Effect 2", ImageIO.read(getClass().getResource("/boxart/Mass Effect 2.png"))),
                        new Game("Mass Effect 3", ImageIO.read(getClass().getResource("/boxart/Mass Effect 3.png"))),
                        new Game("Mass Effect", ImageIO.read(getClass().getResource("/boxart/Mass Effect.png"))),
                        new Game("Space Quest IV", ImageIO.read(getClass().getResource("/boxart/SpaceQuestIV.png"))),
                        new Game("Tomb Raider", ImageIO.read(getClass().getResource("/boxart/TombRaider.png"))),
                        new Game("Zelda - Breath of the wild", ImageIO.read(getClass().getResource("/boxart/Zelda.png")))
                    }));

                    JFrame frame = new JFrame();
                    frame.add(new LaunchPane(games));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }

            }
        });
    }

    public class LaunchPane extends JPanel {
        public LaunchPane(List<Game> games) {
            System.out.println(games.size());
            setLayout(new BorderLayout());
            DefaultListModel<Game> model = new DefaultListModel<>();
            model.addAll(games);

            JList list = new JList(model);
            list.setBackground(Color.BLACK);
            list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
            list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
            list.setCellRenderer(new GameListCellRenderer());
            list.setVisibleRowCount(3);

            add(new JScrollPane(list));
        }
    }

    public class GameListCellRenderer extends DefaultListCellRenderer {
        public GameListCellRenderer() {
            setHorizontalAlignment(CENTER);
            setVerticalAlignment(CENTER);
            setHorizontalTextPosition(CENTER);
            setVerticalTextPosition(BOTTOM);
        }

        @Override
        public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if (value instanceof Game) {
                Game game = (Game) value;
                setIcon(new ImageIcon(game.getBoxArt()));
                setText(null);
            }
            return this;
        }

    }

    public class Game {
        private String title;
        private Image boxArt;

        public Game(String title, Image boxArt) {
            this.title = title;
            this.boxArt = boxArt;
        }

        public String getTitle() {
            return title;
        }

        public Image getBoxArt() {
            return boxArt;
        }

    }
}

Now, if you want to customise it a bit more, you could create a fully custom ListCellRenderer for example…

public class GameListCellRenderer extends JPanel implements ListCellRenderer<Game> {

    private static Border EMPTY_BORDER = new EmptyBorder(1, 1, 1, 1);
    private static Border SELECTED_BORDER = new LineBorder(Color.BLUE, 1);

    private Map<Game, String> cachedTitle = new HashMap<>();
    private Map<Game, ImageIcon> cachedIcon = new HashMap<>();

    private JLabel titleLabel;
    private JLabel artboxLabel;

    public GameListCellRenderer() {
        setOpaque(false);

        titleLabel = new JLabel();
        titleLabel.setForeground(Color.WHITE);
        artboxLabel = new JLabel();

        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = gbc.REMAINDER;
        gbc.weightx = 1;
        gbc.fill = gbc.HORIZONTAL;

        add(artboxLabel, gbc);
        add(titleLabel, gbc);
    }

    @Override
    public Component getListCellRendererComponent(JList<? extends Game> list, Game value, int index, boolean isSelected, boolean cellHasFocus) {
        ImageIcon icon = cachedIcon.get(value);
        if (icon == null) {
            icon = new ImageIcon(value.getBoxArt());
            cachedIcon.put(value, icon);
        }
        String title = cachedTitle.get(value);
        if (title == null) {
            FontMetrics fm = getFontMetrics(titleLabel.getFont());
            title = value.getTitle();

            if (fm.stringWidth(title) > 150) {
                do {
                    title = title.substring(0, title.length() - 1);
                } while (fm.stringWidth(title) > 150);
                title = title.substring(0, title.length() - 3) + "...";
            }
            cachedTitle.put(value, title);
        }

        titleLabel.setText(title);
        artboxLabel.setIcon(icon);
        setBorder(isSelected ? SELECTED_BORDER : EMPTY_BORDER);
        return this;
    }

}

And without changing anything else, it will produce…

enter image description here

Leave a Comment