Java GUI:Awt/Swing 实现图片的缩放与滚动查看

引言

这学期开始学习软件工程,老师要求使用 Java 语言编写实验。好在之前用 Java 写过安卓应用,对 Java 有一定的了解。

这次的实验涉及到 GUI 的编写,由于是实验,所以采用基本的 AWT 和 Swing 两个工具包。之后如果有时间,会学习用 SWT 来开发 GUI 程序。

编写 GUI 程序,最先需要学习的就是组件的布局。Java 有许多种布局管理器,包括 FlowLayout、BorderLayout、BoxLayout、GridLayout、CardLayout 以及 GridBagLayout。

前四种是较为常用的布局管理器,对于简单的 GUI 程序来说已经能够胜任大部分的布局工作。

我个人感觉,从布局上来讲,无论是 Java 的 AWT、Swing 工具包,还是 Qt 框架,或是安卓开发,都有其相通之处。理解了常见的几种布局方式,在各种语言的 GUI 开发中都能够派上用场。

GUI 开发非常有助于理解 Java 的 OOP 思想,组件的继承关系,抽象类的派生,接口的实现等等,即使以后不从事 GUI 开发,也能获益匪浅。

实际情景

在编写界面的过程中,我需要在窗口中显示图片,但由于图片的尺寸太大,导致即使窗口最大化也无法完整显示图片,因此需要对图片进行缩放操作。

如果只是单纯地把图片缩小到窗口大小,那么会导致很多细节都无法看清,因此不仅仅需要进行图片的缩放,还需要确保不缩小图片的情况下,也能够通过滚动条来查看图片的各个部分。

如何显示图片并进行缩放

使用 JLabel 组件

显示图片

JLabel label = new JLabel();
ImageIcon icon = new ImageIcon("image.jpg-default")
label.setIcon(icon)

通过以下方法进行缩放

int newWidth = 2 * icon.getIconWidth();
int newHeight = 2 * icon.getIconHeight();
Image scaledImage = icon.getImage().getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
label.setIcon(new ImageIcon(scaledImage));

这种方法虽然能够实现图片的显示以及缩放功能,但是每次缩放图片的效率低,速度较慢。

覆写组件的 painComponent 方法

除了调用 JLabel 的 setIcon() 方法来加载图片,我们还可以调用 Swing 组件的 paintComponent() 方法来绘制图片。

paintComponent() 方法是所有 Swing 组件都自带的一个方法,该方法用于绘制组件的背景,当组件的 repaint() 方法被调用时,该方法也会随之被调用。

我们将在下文给出具体的代码实现,其中自定义类继承自 JComponent,原因是 Swing 中绝大部分的组件均继承自 JComponent,且我们不需要额外的功能,因此 JComponent 是最理想的父类。

创建可缩放图片的自定义组件

我们定义一个叫做 ImageComponent 的类,它继承自 JComponent。

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class ImageComponent extends JComponent {

    final BufferedImage img;

    public ImageComponent(String path) throws IOException {
        img = ImageIO.read(new File(path));
        setZoom(1);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Dimension dim = getPreferredSize();
        g.drawImage(img, 0, 0, dim.width, dim.height, this);
    }

    public void setZoom(double zoom) {
        int w = (int) (zoom * img.getWidth());
        int h = (int) (zoom * img.getHeight());
        setPreferredSize(new Dimension(w, h));
        revalidate();
        repaint();
    }
}

这里定义了一个 setZoom() 方法,该方法调用组件的 setPreferredSize() 来设定欲绘制的图片尺寸,接着调用 repaint() 来重绘组件背景,而 repaint() 会自动调用 paintComponent() 来进行背景的绘制,由于我们覆写了 paintComponent() 方法,因此便能按着我们的想法将缩放后的图片绘制到组件上。

测试组件的效果之前,请先将下面的 Lenna 图保存到 D 盘。

我们通过一个滑动条(JSlider)来控制图片的缩放,并为 ImageComponent 加上滚动条(JScrollPane),以便图片超出显示范围时可以滚动查看。

    public static void main(String[] args) throws Exception {

        final String lenna = "D:\\lenna.png-default";

        final JSlider slider = new JSlider(0, 1000, 500);
        final ImageComponent image = new ImageComponent(lenna);
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                image.setZoom(2. * slider.getValue() / slider.getMaximum());
            }
        });

        JFrame frame = new JFrame("ImageComponent Test");
        frame.add(slider, BorderLayout.NORTH);
        frame.add(new JScrollPane(image));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        frame.setVisible(true);
    }

一个更加完整的组件

上述的 ImageComponent 组件,提供了展示图片以及对图片进行缩放的方法。不过我们调用该组件的时候,往往像上面的 main() 函数中一样,需要利用滑动条进行缩放,以及滚动条进行滚动。为了调用起来更方便,我们可以将 ImageComponent 与 JSlider、JScrollPane 封装成一个组件,这样创建组件时就是完整的形式,可以直接使用。

我们将这个组件叫做 ImageViewer,它同样继承自 JComponent。

import java.awt.*;
import java.io.IOException;
import javax.swing.*;
import javax.swing.event.*;

public class ImageViewer extends JComponent {
    final JSlider slider;
    final ImageComponent image;
    final JScrollPane scrollPane;

    public ImageViewer(String path) throws IOException {
        slider = new JSlider(0, 1000, 500);
        image = new ImageComponent(path);
        scrollPane = new JScrollPane(image);

        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                image.setZoom(2. * slider.getValue() / slider.getMaximum());
            }
        });

        this.setLayout(new BorderLayout());
        this.add(slider, BorderLayout.NORTH);
        this.add(scrollPane);
    }
}

该组件的调用如下:

    public static void main(String[] args) throws IOException {
        JFrame frame = new JFrame("ImageViewer Test");
        ImageViewer imageViewer = new ImageViewer("D:\\Lenna.png-default");

        frame.add(imageViewer);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        frame.setVisible(true);
    }

效果如下:

我们只需要定义一个 ImageViewer 组件,就包含了基本的缩放和滚动功能,用起来更加方便。

当然,如果你想要通过其它事件来触发图片的缩放功能,你可以自己定义 ImageViewer 组件。

参考

ImageComponent 组件的实现参考自 StackOverflow:

How to adjust scrollbar with image zoomin and zoomout in Swing

相关文章

Loading Likes...

发表评论

电子邮件地址不会被公开。 必填项已用*标注