Laying out a keyboard in Swing

扶醉桌前 提交于 2019-11-27 08:02:37

问题


I've been trying to reproduce this layout:

Here I am:

using:

import javax.swing.*;
import java.awt.*;

public class Keyb {

    private JFrame f = new JFrame("Keyboard");

    private JPanel keyboard = new JPanel();

    private static final String[][] key = {
        {"`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace"},
        {"Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\"},
        {"Caps", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "Enter"},
        {"Shift", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "Shift", "\u2191"},
        {" ", "\u2190", "\u2193", "\u2192"}
    };

    public Keyb() {
        keyboard.setLayout(new GridBagLayout());

        JPanel pRow;
        GridBagConstraints c = new GridBagConstraints();
        c.anchor = GridBagConstraints.WEST;
        c.weightx = 1d;

        for (int row = 0; row < key.length; ++row) {
            pRow = new JPanel(new GridBagLayout());

            c.gridy = row;

            for (int col = 0; col < key[row].length; ++col)
                pRow.add(new JButton(key[row][col]));

            keyboard.add(pRow, c);
        }

        f.add(keyboard);
    }

    public void launch() {
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.pack();
        f.setVisible(true);
    }

    public static void main(String[] args) {
        Keyb ui = new Keyb();
        ui.launch();
    }
}

How do I adjust the size of the buttons and align them perfectly, especially Tab, Caps, Shift, Backspace, Enter and the spacebar, perhaps without using the set?Size methods?

Can it be done in a better way with other layout managers?

New at Java, so any other suggestions are welcome.


回答1:


When all else fails, write it your self...

This uses a custom layout manager which defines a "basic" grid but allows components in a given row to expand into portions of the following column(s)...

The default cell size is defined by the largest width/height of the available components that doesn't expand beyond it's own column, makes things a little more even.

Currently the output is anchored to the top/left corner, but I'm sure you can figure out how to calculate the x/y offset needed to get it centered ;)

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Keyb {

    private static final Key[][] keys = new Key[][]{
        {
            createKey("`", 0, 0),
            createKey("1", 0, 1),
            createKey("2", 0, 2),
            createKey("3", 0, 3),
            createKey("4", 0, 4),
            createKey("5", 0, 5),
            createKey("6", 0, 6),
            createKey("7", 0, 7),
            createKey("8", 0, 8),
            createKey("9", 0, 9),
            createKey("0", 0, 10),
            createKey("-", 0, 11),
            createKey("=", 0, 12),
            createKey("Backspace", 0, 13, 2d)},
        {
            createKey("Tab", 1, 0, 1.5d),
            createKey("W", 1, 2),
            createKey("E", 1, 3),
            createKey("R", 1, 4),
            createKey("T", 1, 5),
            createKey("Y", 1, 6),
            createKey("U", 1, 7),
            createKey("I", 1, 8),
            createKey("O", 1, 9),
            createKey("P", 1, 10),
            createKey("[", 1, 11),
            createKey("]", 1, 12),
            createKey("\\", 1, 13)
        },
        {
            createKey("Caps", 2, 0, 1.5d),
            createKey("A", 2, 2),
            createKey("S", 2, 3),
            createKey("D", 2, 4),
            createKey("F", 2, 5),
            createKey("G", 2, 6),
            createKey("H", 2, 7),
            createKey("J", 2, 8),
            createKey("K", 2, 9),
            createKey("L", 2, 10),
            createKey(";", 2, 11),
            createKey("'", 2, 12),     
            createKey("Enter", 2, 13, 2d)
        },
        {
            createKey("Shift", 3, 0, 2d),
            createKey("Z", 3, 2),
            createKey("X", 3, 3),
            createKey("C", 3, 4),
            createKey("V", 3, 5),
            createKey("B", 3, 6),
            createKey("N", 3, 7),
            createKey("M", 3, 8),
            createKey(",", 3, 9),
            createKey(".", 3, 10),
            createKey("/", 3, 11),
            createKey("fill", 3, 12, 0.5d),
            createKey("\u2191", 3, 13),
        },
        {
            createKey("fill", 4, 0, 4d),
            createKey(" ", 4, 1, 6d),
            createKey("fill", 4, 2, 1.5d),
            createKey("\u2190", 4, 3),
            createKey("\u2193", 4, 4),
            createKey("\u2192", 4, 5),
        },
    };

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

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

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

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new KeyBoardLayout());
            for (int row = 0; row < keys.length; row++) {
                for (int col = 0; col < keys[row].length; col++) {
                    Key key = keys[row][col];
                    add(createButton(key.getText()), key.getKeyConstraint());
                }
            }
        }

        protected JComponent createButton(String text) {
            JComponent comp = null;
            if (text == null || text.equalsIgnoreCase("fill")) {
                comp = new JLabel();
            } else {
                comp = new JButton(text);
            }
            return comp;
        }

    }

    public static Key createKey(String text, int x, int y, double span) {
        return new Key(text).setKeyConstraint(new KeyConstraint(x, y, span));
    }

    public static Key createKey(String text, int x, int y) {
        return new Key(text).setKeyConstraint(new KeyConstraint(x, y));
    }

    public static class Key {

        private String text;
        private KeyConstraint keyConstraint;

        public Key(String text) {
            this.text = text;
        }

        public String getText() {
            return text;
        }

        public Key setKeyConstraint(KeyConstraint keyConstraint) {
            this.keyConstraint = keyConstraint;
            return this;
        }

        public KeyConstraint getKeyConstraint() {
            return keyConstraint;
        }

    }

    public static class KeyConstraint {

        public int row, column;
        public double span = 1d;

        public KeyConstraint(int row, int column) {
            this.row = row;
            this.column = column;
        }

        public KeyConstraint(int row, int column, double span) {
            this.row = row;
            this.column = column;
            this.span = span;
        }

    }

    public class KeyBoardLayout implements LayoutManager2 {

        private Map<Component, KeyConstraint> mapComponents;
        private Map<KeyConstraint, Component> mapConstraints;
        private Matrix<Integer, List<JComponent>> matrix = new Matrix<>(); // Virtual grid...

        private Dimension gridSize;

        public KeyBoardLayout() {
            mapComponents = new HashMap<>(25);
            mapConstraints = new HashMap<>(25);
        }

        @Override
        public void addLayoutComponent(String name, Component comp) {
            throw new UnsupportedOperationException("addLayoutComponent(String, Comp) is not supported");
        }

        @Override
        public void removeLayoutComponent(Component comp) {
            KeyConstraint kc = mapComponents.get(comp);
            mapComponents.remove(comp);
            if (kc != null) {
                mapConstraints.remove(kc);
                getCellContents(matrix, kc).remove(comp);
            }
        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {
            int rowHeight = getRowHeight();
            Dimension size = new Dimension();
            size.width = getMaxRowWidth();
            size.height = rowHeight * matrix.getRowCount();
            return size;
        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {
            return preferredLayoutSize(parent);
        }

        protected List<JComponent> getCellContents(Matrix matrix, KeyConstraint constraint) {
            return getCellContents(matrix, constraint.column, constraint.row);
        }

        protected List<JComponent> getCellContents(Matrix<Integer, List<JComponent>> matrix, int col, int row) {
            if (!matrix.contains(col, row)) {
                matrix.add(col, row, new ArrayList<>());
            }
            return matrix.get(col, row);
        }

        protected Dimension getGridSize() {
            if (gridSize == null) {
                int maxCellWidth = 0;
                int maxCellHeight = 0;
                for (int row = 0; row < matrix.getRowCount(); row++) {
                    for (int col = 0; col < matrix.getColumnCount(); col++) {
                        List<JComponent> cell = getCellContents(matrix, col, row);
                        int cellWidth = 0;
                        int cellHeight = 0;
                        for (JComponent comp : cell) {
                            KeyConstraint kc = mapComponents.get(comp);
                            if (kc.span == 1) {
                                cellWidth = Math.max(cellWidth, comp.getPreferredSize().width);
                            }
                            cellHeight = Math.max(cellHeight, comp.getPreferredSize().height);
                        }
                        maxCellWidth = Math.max(cellWidth, maxCellWidth);
                        maxCellHeight = Math.max(cellHeight, maxCellHeight);
                    }
                }
                gridSize = new Dimension(maxCellWidth, maxCellHeight);
            }
            return gridSize;
        }

        protected int getRowHeight() {
            Dimension size = getGridSize();
            return size.height;
        }

        protected int getRowWidth(int row) {
            int rowWidth = 0;
            for (int col = 0; col < matrix.getColumnCount(); col++) {
                Dimension size = getCellSize(col, row);
                rowWidth += size.width;
            }
            return rowWidth;
        }

        protected int getMaxRowWidth() {
            int rowWidth = 0;
            for (int row = 0; row < matrix.getRowCount(); row++) {
                rowWidth = Math.max(getRowWidth(row), rowWidth);
            }
            return rowWidth;
        }

        protected int getColumnWidth(int col) {
            int width = 0;
            for (int row = 0; row < matrix.getRowCount(); row++) {

                Dimension size = getCellSize(col, row);
                width = Math.max(size.width, width);

            }
            return width;
        }

        protected Dimension getCellSize(int col, int row) {
            List<JComponent> comps = matrix.get(col, row);
            Dimension size = new Dimension();
            size.height = getRowHeight();
            for (JComponent comp : comps) {
                Dimension subSize = getCellSize(col, row, comp);
                size.width = Math.max(size.width, subSize.width);
            }

            return size;
        }

        protected Dimension getCellSize(int col, int row, JComponent comp) {
            List<JComponent> comps = matrix.get(col, row);
            Dimension size = new Dimension();
            size.height = getRowHeight();
            int defaultWidth = getGridSize().width;
            KeyConstraint kc = mapComponents.get(comp);
            if (kc.span == 1) {
                size.width = defaultWidth;
            } else {
                int totalWidth = (int)Math.round(defaultWidth * kc.span);
                size.width = totalWidth;
            }

            return size;
        }

        @Override
        public void layoutContainer(Container parent) {
            int rowHeight = getRowHeight();
            int y = 0;
            for (int row = 0; row < matrix.getRowCount(); row++) {
                int x = 0;
                for (int col = 0; col < matrix.getColumnCount(); col++) {
                    List<JComponent> comps = matrix.get(col, row);
                    Rectangle bounds = new Rectangle();
                    bounds.x = x;
                    bounds.y = y;
                    int maxWidth = 0;
                    for (JComponent comp : comps) {

                        Dimension size = getCellSize(col, row, comp);
                        bounds.setSize(size);
                        maxWidth = Math.max(maxWidth, size.width);
                        comp.setBounds(bounds);

                    }
                    x += maxWidth;
                }
                y += rowHeight;
            }
        }

        @Override
        public void addLayoutComponent(Component comp, Object constraints) {
            if (constraints instanceof KeyConstraint) {
                mapComponents.put(comp, (KeyConstraint) constraints);
                mapConstraints.put((KeyConstraint) constraints, comp);
                getCellContents(matrix, (KeyConstraint) constraints).add((JComponent) comp);
            }
        }

        @Override
        public Dimension maximumLayoutSize(Container target) {
            return preferredLayoutSize(target);
        }

        @Override
        public float getLayoutAlignmentX(Container target) {
            return 0.5f;
        }

        @Override
        public float getLayoutAlignmentY(Container target) {
            return 0.5f;
        }

        @Override
        public void invalidateLayout(Container target) {
            gridSize = null;
        }

        public class Matrix<I, O> {

            private Map<I, Map<I, O>> mapRows;

            public Matrix() {
            }

            protected Map<I, Map<I, O>> getRowMap() {
                if (mapRows == null) {
                    mapRows = new HashMap<>(25);
                }
                return mapRows;
            }

            protected Map<I, O> getColumnMap(I row) {
                Map<I, Map<I, O>> rowMap = getRowMap();
                Map<I, O> mapCols = rowMap.get(row);
                if (mapCols == null) {
                    mapCols = new HashMap<>(25);
                    rowMap.put(row, mapCols);
                }
                return mapCols;
            }

            public void add(I col, I row, O obj) {
                Map<I, O> columnMap = getColumnMap(row);
                columnMap.put(col, obj);
            }

            public void remove(I col, I row, O obj) {
                if (contains(col, row)) {
                    Map<I, O> columnMap = getColumnMap(row);
                    columnMap.put(col, obj);
                }
            }

            public void removeColumn(I col) {
                for (I row : getRowMap().keySet()) {
                    Map<I, O> columnMap = getRowMap().get(row);
                    if (columnMap != null) {
                        columnMap.remove(col);
                    }
                }
            }

            public void removeRow(I row) {
                getRowMap().remove(row);
            }

            public int getRowCount() {
                return getRowMap().size();
            }

            public int getColumnCount() {
                int max = 0;
                for (I row : getRowMap().keySet()) {
                    Map<I, O> mapColumns = getRowMap().get(row);
                    max = Math.max(mapColumns.size(), max);
                }
                return max;
            }

            protected boolean containsRow(I row) {
                return getRowMap().containsKey(row);
            }

            protected boolean containsColumn(I col) {
                boolean contains = false;
                for (I row : getRowMap().keySet()) {
                    Map<I, O> columnMap = getRowMap().get(row);
                    if (columnMap != null && columnMap.containsKey(col)) {
                        contains = true;
                        break;
                    }
                }
                return contains;
            }

            public boolean contains(I col, I row) {
                boolean contains = false;
                Map<I, O> colMap = getRowMap().get(row);
                if (colMap != null) {
                    if (colMap.containsKey(col)) {
                        contains = true;
                    }
                }

                return contains;
            }

            public O get(I col, I row) {
                O value = null;
                if (contains(col, row)) {
                    Map<I, O> columnMap = getRowMap().get(row);
                    value = columnMap.get(col);
                }
                return value;
            }

            public boolean contains(O value) {
                boolean contains = false;
                for (I row : getRowMap().keySet()) {
                    Map<I, O> mapColumns = getRowMap().get(row);
                    for (I col : mapColumns.keySet()) {
                        if (mapColumns.containsValue(value)) {
                            contains = true;
                            break;
                        }
                    }
                }
                return contains;
            }

            public boolean rowContains(I row, O value) {
                boolean contains = false;
                Map<I, O> mapColumns = getRowMap().get(row);
                for (I col : mapColumns.keySet()) {
                    if (mapColumns.containsValue(value)) {
                        contains = true;
                        break;
                    }
                }
                return contains;
            }

            public boolean columnContains(I column, O value) {
                boolean contains = false;
                for (I row : getRowMap().keySet()) {
                    Map<I, O> mapColumns = getRowMap().get(row);
                    O colValue = mapColumns.get(column);
                    if (colValue == value) {
                        contains = true;
                        break;
                    }
                }
                return contains;
            }

            public O[] rowToArray(I row, O[] values) {
                List<O> lstValues = new ArrayList<O>(25);
                Map<I, O> mapColumns = getRowMap().get(row);
                lstValues.addAll(mapColumns.values());
                return lstValues.toArray(values);
            }

            public O[] columnToArray(I col, O[] values) {
                List<O> lstValues = new ArrayList<O>(25);
                for (I row : getRowMap().keySet()) {
                    Map<I, O> mapCols = getRowMap().get(row);
                    lstValues.add(mapCols.get(col));
                }
                return lstValues.toArray(values);
            }

            public Iterator<O> columnIterator(I col) {
                List<O> lstValues = new ArrayList<O>(25);
                for (I row : getRowMap().keySet()) {
                    Map<I, O> mapCols = getRowMap().get(row);
                    lstValues.add(mapCols.get(col));
                }
                return lstValues.iterator();
            }

            public Iterator<O> rowIterator(I row) {
                List<O> lstValues = new ArrayList<O>(25);
                Map<I, O> mapColumns = getRowMap().get(row);
                lstValues.addAll(mapColumns.values());
                return lstValues.iterator();
            }
        }
    }
}



回答2:


Another approach. The only restriction is the use of a mono-spaced font to maintain the alignment.

import javax.swing.*;   // JFrame, JPanel, JLabel, JButton
import java.awt.*;      // GridBagLayout, GridBagConstraints, Insets, Font

public class Keyboard {
    private final JFrame f = new JFrame("Keyboard");

    private final JPanel keyboard = new JPanel();

    private static final String[][] key = {
        {"`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace"},
        {"Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\"},
        {"Caps", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "Enter"},
        {"Shift", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "\u2191"},
        {" ", "<", "\u2193", ">"}
    };

    public Keyboard() {
        keyboard.setLayout(new GridBagLayout());

        Insets zeroInset = new Insets(0, 0, 0, 0);
        Font monospace = new Font(Font.MONOSPACED, Font.PLAIN, 12);

        JPanel pRow;
        JButton b;

        GridBagConstraints cRow = new GridBagConstraints(),
                           cButton = new GridBagConstraints();
        cRow.anchor = GridBagConstraints.WEST;
        cButton.ipady = 21;

        // first dimension of the key array
        // representing a row on the keyboard
        for (int row = 0, i = 0; row < key.length; ++row) {
            pRow = new JPanel(new GridBagLayout());

            cRow.gridy = row;

            // second dimension representing each key
            for (int col = 0; col < key[row].length; ++col, ++i) {

                // specify padding and insets for the buttons
                switch (key[row][col]) {
                    case "Backspace":   cButton.ipadx = 0; break;
                    case "Tab":         cButton.ipadx = 17; break;
                    case "Caps":        cButton.ipadx = 10; break;
                    case "Enter":       cButton.ipadx = 27; break;
                    case "Shift":       cButton.ipadx = 27; break;
                    case "/":
                        cButton.insets = new Insets(0, 0, 0, 24);
                        break;
                    case " ":
                        cButton.ipadx = 247;
                        cButton.insets = new Insets(0, 192, 0, 72);
                        break;
                    default:
                        cButton.ipadx = 7;
                        cButton.insets = zeroInset;
                }

                b = new JButton(key[row][col]);
                b.setFont(monospace);
                b.setFocusable(false);
                pRow.add(b, cButton);
            }

            keyboard.add(pRow, cRow);
        }

        f.add(keyboard);
    }

    public void launch() {
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.pack();
        f.setResizable(false);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        Keyboard ui = new Keyboard();
        ui.launch();
    }
}



来源:https://stackoverflow.com/questions/24622279/laying-out-a-keyboard-in-swing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!