Создание графического интерфейса для решателя судоку (в комплекте с примером ASCII)

.

ОБЗОР, ОБРАЗЕЦ

Привет всем,

Я создал базовый решатель Судоку, который может решить большинство проблемы довольно быстро. У меня впереди еще много работы, чтобы он решал даже самые сложные проблемы, но я хотел бы сначала попытаться реализовать базовый графический интерфейс JFrame.

В прошлом я работал с интернет-апплетами, но никогда раньше с JFrames. настройки и т. д.) Мне нужно использовать, чтобы соответствовать моим требованиям. Мне еще предстоит найти способ ограничить свои текстовые поля числами и не дать пользователю вставлять более одного значения за раз. Являются ли текстовые поля действительно лучшим вариантом, или я упускаю что-то, что может более точно соответствовать моим потребностям?

Мне нужно не только знать, какие классы мне нужны, но также и как организовать их так, чтобы кнопка оставалась комфортно между двумя головоломки и текстовое поле находится внизу. Из того, что я прочитал , MigLayout , похоже на возможность упростить этот процесс.

.

END NOTES

Большое, большое спасибо всем, кто помогает. Если какая-то часть этого вопроса кажется немного грубой или резкой, прошу прощения. Я склонен оставлять большинство своих вопросов ночью, так что у сообщества есть несколько часов, чтобы обдумать это, прежде чем я попробую все ответы (это и тот факт, что я делаю вещи большую часть дня).

Я не сплю еще 1-2 часа, чтобы ответить на любые вопросы

Еще раз спасибо,

Юстян

16
задан Community 23 May 2017 в 11:46
поделиться

6 ответов

Графический интерфейс судоку

Хорошо, я ничего не мог с собой поделать ... Вот моя попытка. Все в одном пакете:

  • GUI со всеми элементами, соответствующими спецификации (вопрос)
  • гибкий макет
  • без внешних зависимостей - используются стандартные макеты Swing
  • проверка ввода (только цифры 0-9)
  • Модель Архитектура контроллера представления
  • средство выполнения фоновых задач (ваш графический интерфейс никогда не зависает)
  • некоторые встроенные методы отладки (вывод судоку в виде текста)
  • фиктивная реализация - имитирует длительные вычисления, демонстрирующие реакцию графического интерфейса

Я попробовал свой Лучше сделать код как можно более читабельным. Могут быть довольно непонятные части. Вероятно, часть потоковой передачи не ясна, но если кто-то найдет в этом какую-то пользу, я буду рад описать это лучше.

Итак, моей целью было как можно более простое использование. Если вы посмотрите на интерфейсы, очень сложно сломать этот материал (заморозить UI, получить Null Pointer Exc и т. Д.) В качестве упражнения по написанию общедоступных API. Возможно, это не лучшая реализация, но это одна из лучших, которые я написал. :)

Надеюсь, это поможет.

Вот как это выглядит: Running example

(примечание: значения случайны)

Использование

Все, что вам нужно сделать, это реализовать интерфейс:

public interface SudokuImplementation {

    void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor);
}

Просто выполните все вычисления в этом методе и сохраните результаты с помощью resultAcceptor.setSudokuResult ()

Вот как на самом деле показать GUI:

    SudokuImplementation sudokuImplementation =
        new YourSuperSudoku(); // <- your implementation

    SudokuView sudokuView = new SudokuView();
    sudokuView.setSudokuImplementation(sudokuImplementation);
    sudokuView.setVisible(true);

И все!

Код

Все классы находятся в пакете по умолчанию - рефакторинг по желанию.Вот их список:

  1. SudokuView - основной графический интерфейс
  2. SudokuRun - пример запуска
  3. SudokuController - позволяет безопасно управлять видом
  4. SudokuImplementation - интерфейс для реализации судоку
  5. DummySudokuImplementation - пример реализации

1.SudokuView:

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.border.*;
/**
 * View which constructs every component and creates it's own controller.
 */
public class SudokuView extends JFrame {

    SudokuController controller;

    public void setSudokuImplementation(SudokuImplementation listener) {
        controller.setListener(listener);
    }

    /** Creates new form NewJFrame */
    public SudokuView() {
        controller = new SudokuController();
        setTitle("Sudoku Solver 1.0");
        getContentPane().add(createCenterPanel(), BorderLayout.CENTER);
        getContentPane().add(createBottomPanel(), BorderLayout.SOUTH);
        setMinimumSize(new Dimension(600, 300));
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private JPanel createBottomPanel() {
        JPanel bottomPanel = new JPanel(new GridBagLayout());
        JLabel leftLabel = createLabel("left");
        JLabel rightLabel = createLabel("right");

        controller.bindLeftLabel(leftLabel);
        controller.bindRightLabel(rightLabel);

        bottomPanel.add(leftLabel, getWholeCellConstraints());
        bottomPanel.add(new JSeparator(JSeparator.VERTICAL));
        bottomPanel.add(rightLabel, getWholeCellConstraints());

        bottomPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
        return bottomPanel;
    }

    private JLabel createLabel(String text) {
        JLabel label = new JLabel(text);
        label.setHorizontalAlignment(JLabel.CENTER);
        return label;
    }

    private JPanel createCenterPanel() {
        JPanel centerPanel = new JPanel(new GridBagLayout());
        centerPanel.add(createLeftPanel(), getWholeCellConstraints());
        centerPanel.add(createCenterButton(), getPreferredSizeConstraint());
        centerPanel.add(createRightPanel(), getWholeCellConstraints());
        return centerPanel;
    }

    private GridBagConstraints getPreferredSizeConstraint() {
        // default will do
        return new GridBagConstraints();
    }

    private JButton createCenterButton() {
        JButton goButton = new JButton(">");
        controller.bindCenterButton(goButton);
        return goButton;
    }
    private static final Insets sixPixelInset = new Insets(6, 6, 6, 6);

    private JPanel createRightPanel() {
        JPanel rightPanel = create3x3Panel(6);
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                JPanel panel2 = create3x3Panel(2);
                fillPanelWithNonEditable(panel2, i, j);
                rightPanel.add(panel2);

            }
        }
        rightPanel.setBorder(new EmptyBorder(sixPixelInset));
        return rightPanel;
    }

    private JPanel createLeftPanel() {
        JPanel leftPanel = create3x3Panel(6);
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                JPanel panel2 = create3x3Panel(2);
                fillPanelWithEditable(panel2, i, j);
                leftPanel.add(panel2);

            }
        }
        leftPanel.setBorder(new EmptyBorder(sixPixelInset));
        return leftPanel;
    }

    private GridBagConstraints getWholeCellConstraints() {
        GridBagConstraints wholePanelCnstr = getPreferredSizeConstraint();
        wholePanelCnstr.fill = java.awt.GridBagConstraints.BOTH;
        wholePanelCnstr.weightx = 1.0;
        wholePanelCnstr.weighty = 1.0;
        return wholePanelCnstr;
    }

    private void fillPanelWithEditable(JPanel panel, int majorRow, int majorColumn) {
        for (int minorRow = 0; minorRow < 3; minorRow++) {
            for (int minorColumn = 0; minorColumn < 3; minorColumn++) {
                final JFormattedTextField editableField = createEditableField();
                int column = majorColumn * 3 + minorColumn;
                int row = majorRow * 3 + minorRow;
                controller.bindLeftSudokuCell(row, column, editableField);
                panel.add(editableField);
            }
        }
    }

    private void fillPanelWithNonEditable(JPanel panel, int majorRow, int majorColumn) {
        for (int minorRow = 0; minorRow < 3; minorRow++) {
            for (int minorColumn = 0; minorColumn < 3; minorColumn++) {
                final JFormattedTextField editableField = createNonEditableField();
                int column = majorColumn * 3 + minorColumn;
                int row = majorRow * 3 + minorRow;
                controller.bindRightSudokuCell(row, column, editableField);
                panel.add(editableField);
            }
        }
    }

    private JPanel create3x3Panel(int gap) {
        final GridLayout gridLayout = new GridLayout(3, 3, 1, 1);
        gridLayout.setHgap(gap);
        gridLayout.setVgap(gap);
        JPanel panel = new JPanel(gridLayout);
        return panel;
    }

    private JFormattedTextField createNonEditableField() {
        JFormattedTextField field = createEditableField();
        field.setEditable(false);
        field.setBackground(Color.WHITE); // otherwise non-editable gets gray
        return field;
    }

    private JFormattedTextField createEditableField() {
        JFormattedTextField field = new JFormattedTextField();
        // accept only one digit and nothing else
        try {
            field.setFormatterFactory(new DefaultFormatterFactory(new MaskFormatter("#")));
        } catch (java.text.ParseException ex) {
        }
        field.setPreferredSize(new Dimension(16, 30));
        field.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        field.setText(" ");
        field.setBorder(null);
        return field;
    }
}

2. SudokuRun:

import java.awt.EventQueue;
import javax.swing.UIManager;

public class SudokuRun implements Runnable {

    public void run() {
        // ******************** here You can swap Your true implementation
        SudokuImplementation sudokuImplementation = new DummySudokuImplementation();
        // ***************************** *************** ********* **** ** *


        SudokuView sudokuView = new SudokuView();
        sudokuView.setSudokuImplementation(sudokuImplementation);
        sudokuView.setVisible(true);
    }

    public static void main(String args[]) {
        tryToSetSystemLookAndFeel();
        EventQueue.invokeLater(new SudokuRun());
    }

    private static void tryToSetSystemLookAndFeel() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
            System.out.println("Couldn't set LAF");
        }
    }
}

3. СудокуКонтроллер:

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;

public class SudokuController {

    JLabel leftLabel, rightLabel;
    JFormattedTextField[][] leftSudoku, rightSudoku;
    JButton goButton;

    public SudokuController() {
        leftSudoku = new JFormattedTextField[9][9]; // standard sudoku size
        rightSudoku = new JFormattedTextField[9][9];
    }

    void bindLeftLabel(JLabel label) {
        leftLabel = label;
    }

    void bindRightLabel(JLabel label) {
        rightLabel = label;
    }

    void bindLeftSudokuCell(final int row, final int column, JFormattedTextField field) {
        field.addPropertyChangeListener("value", new PropertyChangeListener() {

            // if user edits field than You could do something about it here
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getNewValue() != null) {
                    String newValue = (String) evt.getNewValue();
                    userEditedValueAt(row, column, Integer.valueOf(newValue));
                }
            }
        });
        leftSudoku[row][column] = field;
    }

    void userEditedValueAt(int row, int column, int value) {
        System.out.println("Value changed at row:" + row + ", column:" + column + " to " + value);
    }

    void bindRightSudokuCell(int row, int column, JFormattedTextField field) {
        rightSudoku[row][column] = field;
    }

    void spitOutSudokus() {
        System.out.println("Left:");
        System.out.println(getPrettyPrinted(leftSudoku));
        System.out.println("Right:");
        System.out.println(getPrettyPrinted(rightSudoku));
    }

    private String getPrettyPrinted(JFormattedTextField[][] sudoku) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 9; i++) {
            sb.append("|");
            for (int j = 0; j < 9; j++) {
                if (sudoku[i][j] != null) {
                    sb.append(sudoku[i][j].getText());
                } else {
                    sb.append("-");
                }
                sb.append(" ");
            }
            sb.append("|\n");
        }
        return sb.toString();
    }

    void bindCenterButton(JButton goButton) {
        this.goButton = goButton;
        goButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                goButtonPressed();
            }
        });
    }
    SudokuImplementation listener;

    public void setListener(SudokuImplementation listener) {
        this.listener = listener;
    }
    Thread backGroundThread;

    private void goButtonPressed() {
        if (listener != null) {
            if (backGroundThread == null || (backGroundThread != null && !backGroundThread.isAlive())) {
                backGroundThread = new Thread() {

                    @Override
                    public void run() {
                        listener.goButtonPressed(getLeftValues(), SudokuController.this);
                    }
                };
                backGroundThread.start();
            }
        }
    }

    private Integer[][] getLeftValues() {
        Integer[][] values = new Integer[9][9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (!leftSudoku[i][j].getText().equals(" ")) {
                    values[i][j] = Integer.valueOf(leftSudoku[i][j].getText());
                }
            }
        }
        return values;
    }

    public void setSudokuResult(final Integer[][] result) {
        // Any GUI interaction must be done on EDT
        // We don't want to block computation so we choose invokeLater
        // as opposed to invokeAndWait.
        EventQueue.invokeLater(new Runnable() {

            public void run() {
                for (int i = 0; i < 9; i++) {
                    for (int j = 0; j < 9; j++) {
                        rightSudoku[i][j].setValue(String.valueOf(result[i][j]));
                    }
                }
            }
        });
    }

    public void setSudokuTime(final String time) {
        EventQueue.invokeLater(new Runnable() {

            public void run() {
                leftLabel.setText("<html>Running time: <b>" + time);
            }
        });
    }

    public void setSudokuCompleted(final boolean completed) {
        EventQueue.invokeLater(new Runnable() {

            public void run() {

                rightLabel.setText("<html>Completely Solved: <b>" + completed);
                if (completed) {
                    spitOutSudokus();
                }

            }
        });
    }
}

4. Реализация судоку:

public interface SudokuImplementation {

    void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor);
}

5. DummySudokuImplementation:

import java.util.concurrent.TimeUnit;

/**
 * Simulates Sudoku solver. Demonstrates how to update GUI. The whole
 * implementation is constructed so GUI never freezes.
 */
class DummySudokuImplementation implements SudokuImplementation {

    public DummySudokuImplementation() {
    }

    public void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor) {
        System.out.println("Long running computation simulation...");
        for (int i = 0; i < 50; i++) {
            resultAcceptor.setSudokuCompleted(false);
            resultAcceptor.setSudokuTime(String.valueOf(i * 50) + "ms");
            resultAcceptor.setSudokuResult(getRandomResult());
            waitSomeTime();
        }
        resultAcceptor.setSudokuResult(leftSudokuValues);
        resultAcceptor.setSudokuCompleted(true);
        waitSomeTime();
        System.out.println("Done!");
    }

    private void waitSomeTime() {
        try {
            TimeUnit.MILLISECONDS.sleep(50);
        } catch (InterruptedException ex) {
        }
    }

    private Integer[][] getRandomResult() {
        Integer[][] randomResult = new Integer[9][9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                randomResult[i][j] = (int) (Math.random() * 9);
            }
        }
        return randomResult;
    }
}

Explanation

Я не утверждаю, что способ, который я сделал, является лучшим. Я бы хотел увидеть другой ответ, скажем, со всем просмотром, выполненным с помощью MigLayout. Было бы очень поучительно. Я изучал Swing GUI, когда реализация Sun была только одна, поэтому она преобладала в моем стиле. Тем не менее, я рекомендую проконсультироваться Краткий курс Sun's Swing GUI . Он также включает простой учебный пример. После его прочтения почти вся часть SudokuView должна стать понятной.

Я разделил код, чтобы сделать его более читабельным. Вот почему контроллер - это другой класс, а не часть представления. Представление предназначено только для создания виджетов и компоновки, но чтобы упростить его (не создавать еще несколько классов), я также инициализирую в нем контроллер.

Настоящая работа находится в контроллере. Он содержит самые тонкие детали ... Threading также идет туда, поэтому не так очевидно, что он на самом деле делает. Я реализовал класс Thread с нуля. Есть альтернатива: использовать SwingWorker . Это может быть клише, но проясните: я использую многопоточность, чтобы графический интерфейс всегда реагировал. Без правильной потоковой передачи весь графический интерфейс зависнет, когда будут выполняться вычисления.Я решил сделать это как можно проще с точки зрения реализации судоку, например, неблокирующие инкрементальные обновления.

Что касается многопоточности, важно знать, какой код в каком потоке выполняется. Каждое действие, запускаемое компонентом графического интерфейса пользователя, выполняется в EDT (потоке отправки событий). Если вы выполните на нем какую-либо длительную задачу, графический интерфейс не будет реагировать. Поэтому я просто создаю еще один поток (см. Реализацию goButtonPressed () ) и запускаю его. После этого EDT может обрабатывать любые другие события без блокировки.

Итак, ваша судоку выполняется в специальной фоновой цепочке. Он может делать все, что захочет, если только ему не нужно обновлять графический интерфейс. Почти наверняка так и будет, поскольку именно здесь идут частичные обновления. Вот уловка: если вы вызываете любой компонент графического интерфейса напрямую (устанавливаете некоторые значения), то графический интерфейс зависает. Это состояние называется нарушением отправки EDT. Все взаимодействия со Swing должны выполняться на EDT, чтобы избежать зависаний. Как это сделать? Для этого в EDT есть специальная очередь событий . Вы помещаете в очередь событие обновления . Код EDT постоянно отслеживает входящие события и соответствующим образом обновляет графический интерфейс. По сути, это связь между фоновым потоком и EDT. Чтобы разместить событие в очереди, вы можете использовать специальный служебный метод, разработанный специально для этого: EventQueue.invokeLater (new Runnable () {/ * здесь ваше взаимодействие с графическим интерфейсом * /}); . Взгляните на методы SudokuController :

  • setSudokuResult ()
  • public void setSudokuTime ()
  • setSudokuCompleted ()

Это были события обновления графического интерфейса.

5
ответ дан 30 November 2019 в 22:09
поделиться

Я не понимаю, как вы могли отказаться от этой потрясающей распечатки ASCII.

Вам действительно стоит взглянуть на руководства, предоставленные @ http://download.oracle.com/javase/tutorial/uiswing/ , и посмотреть, как работают менеджеры компоновки.

Для текстовых полей я бы рекомендовал использовать JTextField. Вот код, который вы можете использовать, чтобы они принимали одновременно только одну цифру:

public class textBox extends JTextField implements KeyListener{
    public textBox() {
        addKeyListener(this);       
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent ke) {

        //consume the event otherwise the default implementation will add it to the text
        ke.consume(); 

        if (Character.isDigit(ke.getKeyChar())) 
            this.setText(Character.toString(ke.getKeyChar()));
    }
}
3
ответ дан 30 November 2019 в 22:09
поделиться

old source: http://i38.tinypic.com/5mieqa.png Этого должно хватить для начала. Просто добавьте логику получения, чтобы вытащить значения, которые они ввели в текстовые поля.

Main:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package sudoku;

import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 *
 * @author nicholasdunn
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        JFrame frame = new JFrame("");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel();

        panel.add(new Board());
        panel.add(new JButton(">"));
        panel.add(new Board());
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }
}

NineSquare:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package sudoku;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

/**
 *
 * @author nicholasdunn
 */
public class NineSquare extends JPanel {

    // What direction in relation to the center square
    private JTextField nw,n,ne,e,se,s,sw,w,c;
    private JTextField[] fields = new JTextField[]{
        nw,n,ne,e,se,s,sw,w,c
    };
    private static final int BORDER_WIDTH = 5;

    public NineSquare(Color bgColor) {
        setLayout(new GridLayout(3,3));
        initGui();
        setBackground(bgColor);
    }

    private void initGui() {
        for (int i = 0; i < fields.length; i++) {
            fields[i] = new JTextField(1);
            fields[i].setDocument(new NumericalDocument());
            add(fields[i]);
        }
        setBorder(BorderFactory.createMatteBorder(BORDER_WIDTH,BORDER_WIDTH,BORDER_WIDTH,BORDER_WIDTH, Color.BLACK));
    }

    public Dimension getPreferredDimension() {
        return new Dimension(100,100);
    }

    public static class NumericalDocument extends PlainDocument {
        String numbers = "0123456789";
        @Override
        public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
            if (getLength() == 0 && str.length() == 1 && numbers.contains(str)) {
                super.insertString(offs, str, a);
            }
            else {
                Toolkit.getDefaultToolkit().beep();
            }
        }
    }
}

Board:

package sudoku;

import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.JPanel;

/**
 *
 * @author nicholasdunn
 */
public class Board extends JPanel {
    private NineSquare[] gridSquares = new NineSquare[9];
    private Color[] bgs = {Color.blue.brighter(), Color.gray};
    public Board() {
        setLayout(new GridLayout(3,3));
        for (int i = 0; i < gridSquares.length; i++) {
            gridSquares[i] = new NineSquare(bgs[i%2]);
            add(gridSquares[i]);
        }
    }
}
6
ответ дан 30 November 2019 в 22:09
поделиться

Таким образом, вам нужно

  1. Ограничить текстовые поля до #s 1-9
  2. Предотвратить вставку более одного значения за раз.

(1) Чтобы ограничить ваши текстовые поля только числом, я думаю, что вы могли бы использовать это JComboBox, который заполнен целыми числами (от 1 до 9), заключенными в поля Integer.

(2) Таким образом, пользователь имеет для выбора JComboBox @ каждой (x, y) точки сетки на доске судоку. Затем необходимо выбрать нужный номер из раскрывающегося списка, предотвращая одновременный ввод нескольких.

Надеюсь, это поможет,
Удачи!

РЕДАКТИРОВАТЬ: ясность + 1

2
ответ дан 30 November 2019 в 22:09
поделиться

Чтобы сделать это действительно полезным, вам придется настроить множество вещей.

Я предлагаю вам использовать кнопки вместо текстовых полей. Когда кто-то нажимает на кнопку, она становится выбранной, когда кто-то набирает номер, он переходит в выбранное текстовое поле (заменяя там номер, если он есть). Это дает вам немного больше контроля над вводом клавиш.

Схема может быть сложной. Было бы неплохо воспользоваться возможностью Swing масштабировать размеры в соответствии с размером экрана, но обязательно масштабируйте и свои шрифты.

Почти каждый сделанный вручную графический интерфейс Swing должен начинаться с BorderLayout.Это дает вам «Стороны», даже если вы используете только север или юг (в вашем примере используется только юг), BorderLayout отлично подходит для распределения всего неиспользуемого пространства в центре.

В центре вы, вероятно, захотите разместить еще один контейнер, возможно, решетку.

Есть один - я думаю, что это "Box", у которого ровный интервал в один ряд. Итак, если вы настроили строку из 3 по горизонтали, а затем добавили 3 по вертикали в каждое поле, вы могли бы ВНУТРИ каждого из них создать поле (чтобы вы могли различать каждую группу из 9), а затем внутри этого поля добавить еще 3 горизонта, в каждой залито по 3 вертикали.

Макеты Swing обычно сводятся к нескольким стандартным приемам (например, всегда смотрят с помощью BorderLayout), за которыми следуют некоторые догадки, эксперименты и некоторый уровень проклятия.

3
ответ дан 30 November 2019 в 22:09
поделиться

IDE Netbeans имеет отличный графический интерфейс для создания графических интерфейсов. Однако это помогает (очень) иметь базовое представление о графических интерфейсах Java, прежде чем пытаться создать его с помощью этого инструмента. Особенно важно поиграть с каждым из менеджеров по расположению и почувствовать, какой из них может помочь в той или иной ситуации. Также обратите внимание, что вы можете вкладывать панели с разными менеджерами компоновки, чтобы получить больший контроль над компоновкой.

2
ответ дан 30 November 2019 в 22:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: