【问题标题】:custom java Swing component Model, UIDelegate, component format自定义java Swing组件模型,UIDelegate,组件格式
【发布时间】:2015-01-04 00:03:21
【问题描述】:

我的任务是创建一个自定义摆动组件。我的组件在包含用于放大和缩小图像的 JSlider 的测试应用程序中正常运行。但是,我需要以模型、UIDelegate 和组件类格式呈现我的自定义组件,我完全不知道如何转换我的代码以使其遵循这种格式。这是我的测试应用程序的代码。

package test;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;

import java.io.File;
import java.net.URL;

import javax.imageio.ImageIO;

public class ZoomDemo extends JComponent implements ChangeListener {

JPanel gui;
/**
 * Displays the image.
 */
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;

public ZoomDemo() {
    size = new Dimension(10, 10);
    setBackground(Color.black);
    try {
         image = ImageIO.read(new File("car.jpg"));
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

public void setImage(Image image) {
    imageCanvas.setIcon(new ImageIcon(image));
}

public void initComponents() {
    if (gui == null) {
        gui = new JPanel(new BorderLayout());
        gui.setBorder(new EmptyBorder(5, 5, 5, 5));
        imageCanvas = new JLabel();
        JPanel imageCenter = new JPanel(new GridBagLayout());
        imageCenter.add(imageCanvas);
        JScrollPane imageScroll = new JScrollPane(imageCenter);
        imageScroll.setPreferredSize(new Dimension(300, 100));
        gui.add(imageScroll, BorderLayout.CENTER);
    }
}

public Container getGui() {
    initComponents();
    return gui;
}

public void stateChanged(ChangeEvent e) {
    int value = ((JSlider) e.getSource()).getValue();
    scale = value / 100.0;
    paintImage();
}

protected void paintImage() {

    int imageWidth = image.getWidth();
    int imageHeight = image.getHeight();
    BufferedImage bi = new BufferedImage(
            (int)(imageWidth*scale), 
            (int)(imageHeight*scale), 
            image.getType());
    Graphics2D g2 = bi.createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
    at.scale(scale, scale);
    g2.drawRenderedImage(image, at);
    setImage(bi);
}

public Dimension getPreferredSize() {
    int w = (int) (scale * size.width);
    int h = (int) (scale * size.height);
    return new Dimension(w, h);
}

private JSlider getControl() {
    JSlider slider = new JSlider(JSlider.HORIZONTAL, 1, 500, 50);
    slider.setMajorTickSpacing(50);
    slider.setMinorTickSpacing(25);
    slider.setPaintTicks(true);
    slider.setPaintLabels(true);
    slider.addChangeListener(this);
    return slider;
}

public static void main(String[] args) {
    ZoomDemo app = new ZoomDemo();
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(app.getGui());
    app.setImage(app.image);

    // frame.getContentPane().add(new JScrollPane(app));  
    frame.getContentPane().add(app.getControl(), "Last");
    frame.setSize(700, 500);
    frame.setLocation(200, 200);
    frame.setVisible(true);
}
}

以下代码是我需要遵循的类格式

组件类

package component;

import javax.swing.JComponent;

import javax.swing.JSlider;
import javax.swing.plaf.ComponentUI;

public class ProgressBar extends JComponent {

public static ComponentUI createUI(JComponent c) {
    return new ZoomUI();
}

public void installUI(JComponent c){



}

public void uninstallUI (JComponent c){

}
}

模型类

public class ZoomModel extends JSLider  {



}

UIDelegate 类

public class ZoomUI extends ComponentUI implements ChangeListener{

}

任何有关如何以这种格式实现自定义组件的帮助将不胜感激。我对 Swing 非常陌生,我在自定义组件上找到的文档非常混乱且帮助不大。

测试应用

package test;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.*;

import java.io.File;
import java.net.URL;

import javax.imageio.ImageIO;

import component.ZoomComponent;

public class ZoomDemo  extends JPanel implements PropertyChangeListener, ActionListener {

ZoomComponent zoomer;
JPanel board;
private BufferedImage image;

public ZoomDemo( ) {
    super(true);  
    setLayout(new BorderLayout( )); 
    board = new JPanel(true); 
    board.setPreferredSize(new Dimension(300, 300)); 
    board.setBorder(new LineBorder(Color.black, 5));

    zoomer = new ZoomComponent();
    add(board, BorderLayout.NORTH);
    add(zoomer, BorderLayout.SOUTH);



}


@Override
public void actionPerformed(ActionEvent arg0) {
    // TODO Auto-generated method stub

}

@Override
public void propertyChange(PropertyChangeEvent arg0) {
    // TODO Auto-generated method stub

}

public static void main(String[] args) {

    UIManager.getDefaults().put("ZoomComponentUI", "component.BasicZoomUI");
    ZoomDemo s= new ZoomDemo();
    JFrame frame = new JFrame("Sample Sketch Application"); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setContentPane(s); 
    frame.pack( ); 
    frame.setVisible(true);
}

}

【问题讨论】:

  • 您的模型类不正确,它应该对数据、UI 进行建模,因此可能包含图像和缩放级别,例如......
  • 对于模型,您应该重用或扩展现有的 Swing 模型。 UIdelegate(“用户界面”类)扩展了 ComponentUI。它负责绘制组件并解释用户的鼠标或键盘操作,因此它应该实现适当的侦听器和接口。 Component 类扩展了 JComponent。这些是我收到的说明,但不明白如何使其工作。
  • “你应该重用或扩展现有的 Swing 模式” 喜欢什么?
  • 我认为这意味着像在我的代码中那样扩展现有模型,例如“JSLider”
  • JSlider 不是模型,而是组件;)

标签: java swing model-view-controller custom-component


【解决方案1】:

好的,这是对我不使用的 API 部分的一次有趣的冒险 :),首先阅读 How to Write a Custom Swing Component 及其相关链接,这将为您提供了解相关内容的基础工作发生...

型号

界面

就我个人而言,我总是从界面开始,界面让生活更美好,它给你更多的灵活性。现在,您应该从哪个模型扩展(根据您的要求)...?

嗯,我能找到的最佳选择是BoundedRangeModelJSlider 也使用它...这实际上意味着我不仅可以将此模型传递给视图,还可以传递给JSlider无需任何额外工作,让滑块更改图像!双赢

import java.awt.Dimension;
import java.awt.Image;
import javax.swing.BoundedRangeModel;

public interface ZoomModel extends BoundedRangeModel {

    public Image getImage();

    public Dimension getScaledSize();

}

摘要

接下来,我喜欢制作一个抽象版本,这是我放置“通用”功能的地方,这对于大多数实现来说可能是相同的,在这种情况下,它可能不是必需的,但我很喜欢这……

import java.awt.Dimension;
import java.awt.Image;
import javax.swing.DefaultBoundedRangeModel;

public abstract class AbstractZoomModel extends DefaultBoundedRangeModel implements ZoomModel {

    public AbstractZoomModel() {
        super(100, 0, 0, 200);
    }

    @Override
    public Dimension getScaledSize() {
        Dimension size = new Dimension(0, 0);
        Image image = getImage();
        if (image != null) {
            double scale = getValue() / 100d;
            size.width = (int) Math.round(image.getWidth(null) * scale);
            size.height = (int) Math.round(image.getHeight(null) * scale);

        }
        return size;
    }

}

所以,你可以在这里看到,我已经定义了一些基本属性,100 的起始缩放级别,200 的最大级别和0 的最小级别,另外我已经实现了@ 987654330@,有点用,让生活更轻松...

默认...

现在,因为我们喜欢友善,我们提供了模型的“默认”实现。这是非常基本的,因为它所做的一切都是引用图像...

import java.awt.Image;

public class DefaultZoomModel extends AbstractZoomModel {
    Image image;

    public DefaultZoomModel(Image image) {
        this.image = image;
    }

    @Override
    public Image getImage() {
        return image;
    }

}

例如,您可以创建从 URL 下载图像的实现...

视图

好的,这实际上是组件本身,它会添加到您的 UI 中。它包含构建和准备 UI 委托和管理模型所需的基本功能。这里感兴趣的关键是使用属性更改支持来提供模型更改通知,这很重要,您将看到...

import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.UIManager;

public class ZoomComponent extends JComponent {

    private static final String uiClassID = "ZoomComponentUI";
    private ZoomModel model;

    public ZoomComponent() {
        setBackground(Color.black);
        setFocusable(true);
        updateUI();
    }

    public void setModel(ZoomModel newModel) {
        if (model != newModel) {
            ZoomModel old = model;
            this.model = newModel;
            firePropertyChange("model", old, newModel);
        }
    }

    public ZoomModel getModel() {
        return model;
    }

    @Override
    public Dimension getPreferredSize() {
        ZoomModel model = getModel();
        Dimension size = new Dimension(100, 100);
        if (model != null) {
            size = model.getScaledSize();
        }
        return size;
    }

    public void setUI(BasicZoomUI ui) {
        super.setUI(ui);
    }

    @Override
    public void updateUI() {
        if (UIManager.get(getUIClassID()) != null) {
            ZoomUI ui = (ZoomUI) UIManager.getUI(this);
            setUI(ui);
        } else {
            setUI(new BasicZoomUI());
        }
    }

    public BasicZoomUI getUI() {
        return (BasicZoomUI) ui;
    }

    @Override
    public String getUIClassID() {
        return uiClassID;
    }
}

UI 委托

现在其他有趣的东西...如果我们遵循标准约定,您通常会提供 UI 委托的 abstract 概念,例如...

import javax.swing.plaf.ComponentUI;

public abstract class ZoomUI extends ComponentUI {       
}

由此,其他代表将成长......

基本 UI 委托

惯例通常会建议您提供一个“基本”实现,做很多繁重的工作,但允许其他实现有机会跳入其中进行更改

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ComponentUI;

public class BasicZoomUI extends ZoomUI {

    private ZoomComponent zoomComponent;
    private MouseAdapter mouseHandler;
    private ChangeListener changeHandler;

    private Action zoomIn;
    private Action zoomOut;
    private PropertyChangeListener propertyChangeHandler;

    protected ChangeListener getChangeHandler() {
        if (changeHandler == null) {
            changeHandler = new ChangeHandler();
        }
        return changeHandler;
    }

    protected void installMouseListener() {
        mouseHandler = new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                zoomComponent.requestFocusInWindow();
            }

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                int amount = e.getWheelRotation();
                ZoomModel model = zoomComponent.getModel();
                if (model != null) {

                    int value = model.getValue();
                    model.setValue(value + amount);

                }
            }

        };
        zoomComponent.addMouseListener(mouseHandler);
        zoomComponent.addMouseWheelListener(mouseHandler);

    }

    protected void installModelPropertyChangeListener() {

        propertyChangeHandler = new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                ZoomModel old = (ZoomModel) evt.getOldValue();
                if (old != null) {
                    old.removeChangeListener(getChangeHandler());
                }
                ZoomModel newValue = (ZoomModel) evt.getNewValue();
                if (newValue != null) {
                    newValue.addChangeListener(getChangeHandler());
                }
            }
        };

        zoomComponent.addPropertyChangeListener("model", propertyChangeHandler);

    }

    protected void installKeyBindings() {

        zoomIn = new ZoomInAction();
        zoomOut = new ZoomOutAction();

        InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");

        ActionMap actionMap = zoomComponent.getActionMap();
        actionMap.put("zoomIn", zoomIn);
        actionMap.put("zoomOut", zoomOut);
    }

    protected void installModelChangeListener() {

        ZoomModel model = getModel();
        if (model != null) {
            model.addChangeListener(getChangeHandler());
        }

    }

    @Override
    public void installUI(JComponent c) {

        zoomComponent = (ZoomComponent) c;

        installMouseListener();
        installModelPropertyChangeListener();
        installKeyBindings();
        installModelChangeListener();

    }

    protected void uninstallModelChangeListener() {

        getModel().removeChangeListener(getChangeHandler());

    }

    protected void uninstallKeyBindings() {

        InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_FOCUSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "donothing");
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "donothing");
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "donothing");
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "donothing");

        AbstractAction blank = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
            }
        };

        ActionMap actionMap = zoomComponent.getActionMap();
        actionMap.put("zoomIn", blank);
        actionMap.put("zoomOut", blank);

    }

    protected void uninstallModelPropertyChangeListener() {

        zoomComponent.removePropertyChangeListener(propertyChangeHandler);
        propertyChangeHandler = null;

    }

    protected void uninstallMouseListener() {

        zoomComponent.removeMouseWheelListener(mouseHandler);
        mouseHandler = null;

    }

    @Override
    public void uninstallUI(JComponent c) {

        uninstallModelChangeListener();
        uninstallModelPropertyChangeListener();
        uninstallKeyBindings();
        uninstallMouseListener();

        mouseHandler = null;
        zoomComponent = null;

    }

    @Override
    public void paint(Graphics g, JComponent c) {
        super.paint(g, c);
        paintImage(g);
    }

    protected void paintImage(Graphics g) {
        if (zoomComponent != null) {
            ZoomModel model = zoomComponent.getModel();
            Image image = model.getImage();
            Dimension size = model.getScaledSize();
            int x = (zoomComponent.getWidth() - size.width) / 2;
            int y = (zoomComponent.getHeight() - size.height) / 2;
            g.drawImage(image, x, y, size.width, size.height, zoomComponent);
        }
    }

    public static ComponentUI createUI(JComponent c) {
        return new BasicZoomUI();
    }

    protected ZoomModel getModel() {

        return zoomComponent == null ? null : zoomComponent.getModel();

    }

    protected class ChangeHandler implements ChangeListener {

        @Override
        public void stateChanged(ChangeEvent e) {
            zoomComponent.revalidate();
            zoomComponent.repaint();
        }

    }

    protected class ZoomAction extends AbstractAction {

        private int delta;

        public ZoomAction(int delta) {
            this.delta = delta;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            ZoomModel model = getModel();
            if (model != null) {
                model.setValue(model.getValue() + delta);
            }
        }

    }

    protected class ZoomOutAction extends ZoomAction {

        public ZoomOutAction() {
            super(-5);
        }

    }

    protected class ZoomInAction extends ZoomAction {

        public ZoomInAction() {
            super(5);
        }

    }

}

您可以从这里开始设计特定于平台的实现,但我决定坚持使用基本委托...

把它们放在一起

如果这还不够,在您可以使用其中任何一个之前,您必须安装委托...

UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");

nb:更改 your.awesome.package.name 以反映您的实际包名称...

可运行示例

 import java.awt.BorderLayout;
 import java.awt.Dimension;
 import java.awt.EventQueue;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.io.File;
 import java.io.IOException;
 import javax.imageio.ImageIO;
 import javax.swing.JFrame;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JSlider;
 import javax.swing.UIManager;
 import javax.swing.UnsupportedLookAndFeelException;

 public class TestZoom100 {

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

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

                     UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");

                     try {
                          DefaultZoomModel model = new DefaultZoomModel(ImageIO.read(new File("/your/awesome/image.jpg")));
                          model.setValue(50);
                          ZoomComponent zoomComp = new ZoomComponent();
                          zoomComp.setModel(model);

                          JSlider slider = new JSlider(model);

                          JFrame frame = new JFrame("Testing");
                          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                          frame.add(new JScrollPane(zoomComp));
                          frame.add(slider, BorderLayout.SOUTH);
                          frame.pack();
                          frame.setLocationRelativeTo(null);
                          frame.setVisible(true);
                     } catch (IOException exp) {
                          exp.printStackTrace();
                     }
                }
           });
      }

 }

不要忘记将 BasicZoomUI 的包名称更改为您存储它的包名称,并实际指定一个图像文件;)

【讨论】:

  • 非常感谢!这比我迄今为止阅读的任何文档都更有意义。非常感谢! :)
  • 现在你的工作是了解它在做什么,因为我没有记录大部分代码;) - 想象一下现在有人指向一段代码并问“你为什么这样做? "
  • 是的,非常感谢您的回复非常清楚,并且更容易理解。我现在正试图让它在测试应用程序中工作,但我应该能够自己弄清楚你已经提供了足够的帮助。谢谢!
  • 很遗憾您已经完成了所有这些工作,但我仍然无法让它在测试应用程序中运行。您能否提供有关如何正确测试组件的任何帮助?跨度>
  • 我已将迄今为止的测试应用程序代码添加到我的原始问题中。我无法理解如何设置图像并使组件出现在面板中/
猜你喜欢
  • 2015-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-16
  • 2023-03-21
  • 1970-01-01
  • 2017-04-30
  • 2011-09-03
相关资源
最近更新 更多