5

我之前用 Java 创建了一些 GUI 应用程序,每次我真的只是在乱搞,直到事情完成我想要它做的事情,使用我在网上找到的几个 GUI 示例的片段。最近,我阅读了更多关于使用 Swing 在 Java 中编写 GUI 时的良好实践,但我仍然不清楚。让我首先描述一下我想在下一个项目中做什么,以便我们可以以此为例:

我想创建一个可执行的应用程序,让用户能够加载图像、对这些图像执行一些操作并保存项目。核心应该有一个主图像查看器,其下方有一个简单的导航器,用于在加载的图像之间切换。应该有带有对图像执行操作的按钮的面板(例如按钮“从图像中选择背景颜色”),并且应该可以选择从工具栏或菜单访问这些操作。

我知道例如 GUI 应该从 Event Dispatch Thread 启动,并且我可以使用 SwingWorker 进行耗时的操作。我还了解到,通过使用操作,我可以将功能与状态分开,并为面板按钮、工具栏按钮和菜单项创建一个操作。

我不明白的是所有这些东西是如何相互交流的,以及我把什么放在哪里。例如:我是否在一个单独的模型中维护我的程序的状态(因此当前显示的是哪个图像,设置了哪些设置)并将对该模型的引用放在我的主窗口类中?那么控制器呢?我是否保留对控制器中模型的引用是否正常?当我对图像进行一些计算时,我是从控制器更新 gui 中的图像,还是使用简单的重绘从 gui 本身更新图像?

我想我的主要问题是我真的不明白如何让我的程序的不同部分进行通信。一个 GUI 由很多部分组成,然后是所有这些动作和侦听器、模型、控制器,所有这些都需要以某种方式进行交互。我不断在所有这些对象中添加对几乎所有内容的引用,但这会使所有内容都变得非常混乱。

我在网上找到的另一个例子:http: //www.devdaily.com/java/java-action-abstractaction-actionlistener

我理解这是如何工作的,但我不明白的是如何真正改变“会做'Cut'动作”。进入实际的切割动作。假设它涉及在查看器中从我的图像中剪切一部分,我会将图像传递给动作吗?如果这个过程需要很长时间,我会在动作中创建一个新的 SwingWorker 吗?然后我如何让 SwingWorker 在计算时更新 GUI?我是否会将 GUI 的引用传递给 SwingWorker,以便它可以不时更新它?

有没有人有一个很好的例子来说明如何做到这一点,或者可能有一些关于如何正确学习这个的提示,因为我有点茫然。有很多信息和很多不同的做事方式,我真的很想学习如何使用干净的代码创建可扩展的应用程序。是否有一个很好的开源项目,代码不多,可以很好地演示我描述的 GUI,以便我可以从中学习?

4

1 回答 1

7

我已经构建了一些 Swing 和 SWT GUI。我发现 GUI 需要它自己的模型 - 视图 (MV) 结构。应用程序控制器与 GUI 模型交互,而不是与 GUI 组件交互。

基本上,我为我的 GUI 中的每个 Swing JPanel 构建了一个 Java bean。GUI 组件与 Java bean 交互,应用程序控制器与 Java bean 交互。

这是我创建的 Spirograph GUI。

在此处输入图像描述

这是管理 Spirograph 参数的 Java bean。

import java.awt.Color;

public class ControlModel {

    protected boolean isAnimated;

    protected int jpanelWidth;
    protected int outerCircle;
    protected int innerCircle;
    protected int penLocation;
    protected int penSize;

    protected Color penColor;
    protected Color backgroundColor;

    public ControlModel() {
        init();
        this.penColor = Color.BLUE;
        this.backgroundColor = Color.WHITE;
    }

    public void init() {
        this.jpanelWidth = 512;
        this.outerCircle = 1000;
        this.innerCircle = 520;
        this.penLocation = 400;
        this.penSize = 2;
        this.isAnimated = true;
    }

    public int getOuterCircle() {
        return outerCircle;
    }

    public void setOuterCircle(int outerCircle) {
        this.outerCircle = outerCircle;
    }

    public int getInnerCircle() {
        return innerCircle;
    }

    public void setInnerCircle(int innerCircle) {
        this.innerCircle = innerCircle;
    }

    public int getPenLocation() {
        return penLocation;
    }

    public void setPenLocation(int penLocation) {
        this.penLocation = penLocation;
    }

    public int getPenSize() {
        return penSize;
    }

    public void setPenSize(int penSize) {
        this.penSize = penSize;
    }

    public boolean isAnimated() {
        return isAnimated;
    }

    public void setAnimated(boolean isAnimated) {
        this.isAnimated = isAnimated;
    }

    public Color getPenColor() {
        return penColor;
    }

    public void setPenColor(Color penColor) {
        this.penColor = penColor;
    }

    public Color getBackgroundColor() {
        return backgroundColor;
    }

    public void setBackgroundColor(Color backgroundColor) {
        this.backgroundColor = backgroundColor;
    }

    public int getJpanelWidth() {
        return jpanelWidth;
    }

    public int getJpanelHeight() {
        return jpanelWidth;
    }

}

编辑添加控制面板类。

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;

import com.ggl.spirograph.model.ControlModel;

public class ControlPanel {

    protected static final Insets entryInsets = new Insets(0, 10, 4, 10);
    protected static final Insets titleInsets = new Insets(0, 10, 20, 10);

    protected ControlModel model;

    protected DrawingPanel drawingPanel;

    protected JButton drawButton;
    protected JButton stopButton;
    protected JButton resetButton;
    protected JButton foregroundColorButton;
    protected JButton backgroundColorButton;

    protected JLabel message;

    protected JPanel panel;

    protected JTextField outerCircleField;
    protected JTextField innerCircleField;
    protected JTextField penLocationField;
    protected JTextField penSizeField;
    protected JTextField penFadeField;

    protected JToggleButton animationToggleButton;
    protected JToggleButton fastToggleButton;

    protected static final int messageLength = 100;
    protected String blankMessage;

    public ControlPanel(ControlModel model) {
        this.model = model;
        this.blankMessage = createBlankMessage();
        createPartControl();
        setFieldValues();
        setColorValues();
    }

    public void setDrawingPanel(DrawingPanel drawingPanel) {
        this.drawingPanel = drawingPanel;
    }

    protected String createBlankMessage() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < messageLength / 4; i++) {
            sb.append("    ");
        }
        return sb.toString();
    }

    protected void createPartControl() {
        panel = new JPanel();
        panel.setLayout(new GridBagLayout());

        int gridy = 0;

        JLabel title = new JLabel("Spirograph Parameters");
        title.setHorizontalAlignment(SwingConstants.CENTER);
        addComponent(panel, title, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

        resetButton = new JButton("Reset Default Parameters");
        resetButton.setHorizontalAlignment(SwingConstants.CENTER);
        resetButton.addActionListener(new ResetButtonListener());
        addComponent(panel, resetButton, 0, gridy++, 4, 1, entryInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

        JLabel outerCircleLabel = new JLabel("Outer circle radius:");
        outerCircleLabel.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, outerCircleLabel, 0, gridy, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        outerCircleField = new JTextField(5);
        outerCircleField.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, outerCircleField, 2, gridy++, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        JLabel innerCircleLabel = new JLabel("Inner circle radius:");
        innerCircleLabel.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, innerCircleLabel, 0, gridy, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        innerCircleField = new JTextField(5);
        innerCircleField.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, innerCircleField, 2, gridy++, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        JLabel penLocationLabel = new JLabel("Pen location radius:");
        penLocationLabel.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, penLocationLabel, 0, gridy, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        penLocationField = new JTextField(5);
        penLocationField.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, penLocationField, 2, gridy++, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        JLabel penSizeLabel = new JLabel("Pen size:");
        penSizeLabel.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, penSizeLabel, 0, gridy, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        penSizeField = new JTextField(5);
        penSizeField.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, penSizeField, 2, gridy++, 2, 1, entryInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        message = new JLabel(blankMessage);
        message.setForeground(Color.RED);
        message.setHorizontalAlignment(SwingConstants.LEFT);
        addComponent(panel, message, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL);

        title = new JLabel("Drawing Speed");
        title.setHorizontalAlignment(SwingConstants.CENTER);
        addComponent(panel, title, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

        JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 4, 0));

        animationToggleButton = new JToggleButton("Animated");
        animationToggleButton.setSelected(model.isAnimated());
        animationToggleButton.setHorizontalAlignment(SwingConstants.CENTER);
        animationToggleButton.addActionListener(new DrawingSpeedListener(
                animationToggleButton));
        buttonPanel.add(animationToggleButton);

        fastToggleButton = new JToggleButton("Fast");
        fastToggleButton.setSelected(!model.isAnimated());
        fastToggleButton.setHorizontalAlignment(SwingConstants.CENTER);
        fastToggleButton.addActionListener(new DrawingSpeedListener(
                fastToggleButton));
        buttonPanel.add(fastToggleButton);

        addComponent(panel, buttonPanel, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

        title = new JLabel("Drawing Colors");
        title.setHorizontalAlignment(SwingConstants.CENTER);
        addComponent(panel, title, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

        buttonPanel = new JPanel(new GridLayout(1, 2, 4, 0));

        foregroundColorButton = new JButton("Pen");
        foregroundColorButton.setHorizontalAlignment(SwingConstants.CENTER);
        foregroundColorButton.addActionListener(new ColorSelectListener(
                foregroundColorButton));
        buttonPanel.add(foregroundColorButton);

        backgroundColorButton = new JButton("Paper");
        backgroundColorButton.setHorizontalAlignment(SwingConstants.CENTER);
        backgroundColorButton.addActionListener(new ColorSelectListener(
                backgroundColorButton));
        buttonPanel.add(backgroundColorButton);

        addComponent(panel, buttonPanel, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

        title = new JLabel("Drawing Controls");
        title.setHorizontalAlignment(SwingConstants.CENTER);
        addComponent(panel, title, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);

        buttonPanel = new JPanel(new GridLayout(1, 2, 4, 0));

        drawButton = new JButton("Draw");
        drawButton.setHorizontalAlignment(SwingConstants.CENTER);
        drawButton.addActionListener(new DrawButtonListener());
        buttonPanel.add(drawButton);

        stopButton = new JButton("Stop");
        stopButton.setHorizontalAlignment(SwingConstants.CENTER);
        stopButton.addActionListener(new StopButtonListener());
        buttonPanel.add(stopButton);

        addComponent(panel, buttonPanel, 0, gridy++, 4, 1, titleInsets,
                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);
    }

    protected void addComponent(Container container, Component component,
            int gridx, int gridy, int gridwidth, int gridheight, 
            Insets insets, int anchor, int fill) {
        GridBagConstraints gbc = new GridBagConstraints(gridx, gridy,
                gridwidth, gridheight, 1.0, 1.0, anchor, fill, insets, 0, 0);
        container.add(component, gbc);
    }

    protected void setFieldValues() {
        outerCircleField.setText(Integer.toString(model.getOuterCircle()));
        innerCircleField.setText(Integer.toString(model.getInnerCircle()));
        penLocationField.setText(Integer.toString(model.getPenLocation()));
        penSizeField.setText(Integer.toString(model.getPenSize()));
    }

    protected void setColorValues() {
        foregroundColorButton.setForeground(model.getBackgroundColor());
        foregroundColorButton.setBackground(model.getPenColor());

        backgroundColorButton.setForeground(model.getPenColor());
        backgroundColorButton.setBackground(model.getBackgroundColor());
    }

    public JPanel getPanel() {
        return panel;
    }

    public class ResetButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            model.init();
            setFieldValues();
        }

    }

    public class StopButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            drawingPanel.stop();
        }

    }

    public class DrawButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent event) {
            message.setText(blankMessage);

            int ocTest = isNumeric(outerCircleField.getText());
            int icTest = isNumeric(innerCircleField.getText());
            int plTest = isNumeric(penLocationField.getText());
            int psTest = isNumeric(penSizeField.getText());

            boolean isInvalid = false;

            if (psTest < 0) {
                message.setText("Pen size is not a valid integer");
                isInvalid = true;
            }

            if (plTest < 0) {
                message.setText("Pen location radius is not a valid integer");
                isInvalid = true;
            }

            if (icTest < 0) {
                message.setText("Inner circle radius is not a valid integer");
                isInvalid = true;
            }

            if (ocTest < 0) {
                message.setText("Outer circle radius is not a valid integer");
                isInvalid = true;
            }

            if (isInvalid) {
                return;
            }

            if (ocTest > 1000) {
                message.setText("The outer circle cannot be larger than 1000");
                isInvalid = true;
            }

            if (ocTest <= icTest) {
                message.setText("The inner circle must be smaller than the outer circle");
                isInvalid = true;
            }

            if (icTest <= plTest) {
                message.setText("The pen location must be smaller than the inner circle");
                isInvalid = true;
            }

            if (isInvalid) {
                return;
            }

            model.setOuterCircle(ocTest);
            model.setInnerCircle(icTest);
            model.setPenLocation(plTest);
            model.setPenSize(psTest);

            drawingPanel.draw(model.isAnimated());
        }

        protected int isNumeric(String field) {
            try {
                return Integer.parseInt(field);
            } catch (NumberFormatException e) {
                return Integer.MIN_VALUE;
            }
        }

    }

    public class DrawingSpeedListener implements ActionListener {

        JToggleButton selectedButton;

        public DrawingSpeedListener(JToggleButton selectedButton) {
            this.selectedButton = selectedButton;
        }

        @Override
        public void actionPerformed(ActionEvent arg0) {
            if (selectedButton.equals(animationToggleButton)) {
                model.setAnimated(true);
            } else {
                model.setAnimated(false);
            }

            animationToggleButton.setSelected(model.isAnimated());
            fastToggleButton.setSelected(!model.isAnimated());
        }

    }

    public class ColorSelectListener implements ActionListener {

        JButton selectedButton;

        public ColorSelectListener(JButton selectedButton) {
            this.selectedButton = selectedButton;
        }

        @Override
        public void actionPerformed(ActionEvent arg0) {
            if (selectedButton.equals(foregroundColorButton)) {
                Color initialColor = model.getPenColor();
                Color selectedColor = JColorChooser.showDialog(drawingPanel,
                        "Select pen color", initialColor);
                if (selectedColor != null) {
                    model.setPenColor(selectedColor);
                }
            } else if (selectedButton.equals(backgroundColorButton)) {
                Color initialColor = model.getBackgroundColor();
                Color selectedColor = JColorChooser.showDialog(drawingPanel,
                        "Select paper color", initialColor);
                if (selectedColor != null) {
                    model.setBackgroundColor(selectedColor);
                }
            }
            setColorValues();
        }

    }

 }
于 2012-05-04T15:28:22.430 回答