有人发贴问,一个 JPanel 里的图片太大了,超出了 JPanel 的大小范围,“我想拖动鼠标按住JPanel,拖动JPanel,把那些显示不了的线段“拖回来”。”
这是 JViewport 的典型应用场景,很多人会用 JScrollPane,但是对 JViewport 可能不熟悉,其实 JScrollPane 是整合了几个 JViewport,JScrollBar,以及特别设计的布局的一个控件,其中的 JViewport 单独拿出来也很好用,下面就是示例代码。
为了显示图片,先做一个 panel,如下
- /*
- * Copyright 2013 (raistlic@gmail.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.Graphics;
- import java.awt.Image;
- import java.awt.Insets;
- import javax.swing.JPanel;
- /**
- *
- * @author raistlic
- */
- public class JImagePanel extends JPanel {
- private static final Color GRID_1 = Color.GRAY.brighter();
- private static final Color GRID_2 = GRID_1.brighter();
- private static final int GRID_SIZE = 10;
- private Image image;
- public void setImage(Image image) {
- // although not checked, this method should be called with-in EDT.
- this.image = image;
- revalidate();
- repaint();
- }
- @Override
- public Dimension getPreferredSize() {
- if( image == null )
- return getMinimumSize();
- else {
- Insets i = getInsets();
- return new Dimension(
- i.left + i.right + image.getWidth(this),
- i.top + i.bottom + image.getHeight(this));
- }
- }
- @Override
- protected void paintComponent(Graphics g) {
- super.paintComponent(g);
- int width = getWidth();
- int height = getHeight();
- g.setColor(GRID_1);
- g.fillRect(0, 0, width, height);
- g.setColor(GRID_2);
- for(int x=0, y=0, line=0; y<height; ) {
- g.fillRect(x, y, GRID_SIZE, GRID_SIZE);
- x += 2 * GRID_SIZE;
- if( x > width ) {
- y += GRID_SIZE;
- line = (line + 1) % 2;
- x = line * GRID_SIZE;
- }
- }
- Insets i = getInsets();
- g.drawImage(image, i.left, i.top, this);
- }
- }
绘制代码中间有大段画格子(类似PS里面对透明部分的表现)的,如果不需要可以无视。
然后做一个可以“鼠标托拽改变其中控件显示位置”的 JViewport:
- /*
- * Copyright 2013 (raistlic@gmail.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import java.awt.Component;
- import java.awt.Dimension;
- import java.awt.Point;
- import java.awt.event.MouseEvent;
- import java.awt.event.MouseListener;
- import java.awt.event.MouseMotionListener;
- import javax.swing.JViewport;
- /**
- *
- * @author raistlic
- */
- public class JDragableViewport extends JViewport {
- public JDragableViewport() {
- MouseDragHandler handler = this.new MouseDragHandler();
- addMouseListener(handler);
- addMouseMotionListener(handler);
- }
- @Override
- public void setViewPosition(Point p) {
- p.x = Math.max(0, p.x);
- p.y = Math.max(0, p.y);
- Component v = getView();
- if( v != null ) {
- Dimension d = v.getPreferredSize();
- Dimension size = getSize();
- p.x = Math.min(d.width - size.width, p.x);
- p.y = Math.min(d.height - size.height, p.y);
- }
- super.setViewPosition(p);
- }
- private class MouseDragHandler implements MouseListener, MouseMotionListener {
- private Point cursor = new Point();
- private Point view = new Point();
- @Override
- public void mouseClicked(MouseEvent e) {}
- @Override
- public void mouseReleased(MouseEvent e) {}
- @Override
- public void mouseEntered(MouseEvent e) {}
- @Override
- public void mouseExited(MouseEvent e) {}
- @Override
- public void mouseMoved(MouseEvent e) {}
- @Override
- public void mouseDragged(MouseEvent e) {
- Point p = e.getPoint();
- int dx = cursor.x - p.x;
- int dy = cursor.y - p.y;
- view.x += dx;
- view.y += dy;
- setViewPosition(view);
- cursor = p;
- view = getViewPosition();
- }
- @Override
- public void mousePressed(MouseEvent e) {
- cursor = e.getPoint();
- view = getViewPosition();
- }
- }
- }
可以发现,它就是一个普通的 JViewport,加了两个鼠标相关的 listener。
然后写一个小测试,用于打开图片文件并察看:
- /*
- * Copyright 2013 (raistlic@gmail.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import java.awt.BorderLayout;
- import java.awt.GridBagConstraints;
- import java.awt.GridBagLayout;
- import java.awt.Insets;
- import java.awt.event.ActionEvent;
- import java.awt.image.BufferedImage;
- import java.io.File;
- import java.io.IOException;
- import java.util.Arrays;
- import javax.imageio.ImageIO;
- import javax.swing.AbstractAction;
- import javax.swing.Action;
- import javax.swing.BorderFactory;
- import javax.swing.JButton;
- import javax.swing.JFileChooser;
- import javax.swing.JFrame;
- import javax.swing.JOptionPane;
- import javax.swing.JPanel;
- import javax.swing.JTextField;
- import javax.swing.JViewport;
- import javax.swing.SwingUtilities;
- import javax.swing.UIManager;
- import javax.swing.filechooser.FileFilter;
- /**
- *
- * @author raistlic
- */
- public class ImageViewDemo extends JPanel {
- public static void main(String[] args) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- JFrame f = new JFrame("Image View Demo");
- f.setContentPane(new ImageViewDemo());
- f.setSize(800, 600);
- f.setLocationRelativeTo(null);
- f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- f.setVisible(true);
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- }
- private static final String LABEL_OPEN_FILE = "Open Image File";
- private static final Iterable<String> SUPPORTED_FILE_EXT = Arrays.asList(
- ".jpg", ".png"
- );
- private JImagePanel imagePanel;
- private Action openAction;
- private JTextField pathField;
- private JFileChooser fileChooser;
- ImageViewDemo() {
- super(new BorderLayout(5, 5));
- imagePanel = new JImagePanel();
- openAction = new OpenImageFileAction(LABEL_OPEN_FILE);
- pathField = new JTextField();
- pathField.setEditable(false);
- pathField.setOpaque(false);
- initLayout();
- }
- private void initLayout() {
- JViewport viewPort = new JDragableViewport();
- viewPort.setView(imagePanel);
- add(viewPort, BorderLayout.CENTER);
- JPanel control = new JPanel(new GridBagLayout());
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.insets = new Insets(5, 5, 5, 5);
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.weightx = 1.0;
- gbc.gridx = 0;
- gbc.gridy = 0;
- control.add(pathField, gbc);
- gbc.weightx = 0;
- gbc.fill = GridBagConstraints.NONE;
- gbc.gridx += 1;
- control.add(new JButton(openAction), gbc);
- control.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
- add(control, BorderLayout.SOUTH);
- setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
- }
- private JFileChooser getFileChooser() {
- if( fileChooser == null ) {
- fileChooser = new JFileChooser();
- fileChooser.setFileFilter(new FileFilter() {
- @Override
- public boolean accept(File f) {
- assert f != null;
- if( f.isDirectory() ) {
- return true;
- }
- else {
- String fname = f.getName().toLowerCase();
- for(String s : SUPPORTED_FILE_EXT)
- if( fname.endsWith(s) )
- return true;
- return false;
- }
- }
- @Override
- public String getDescription() {
- return "Image Files";
- }
- });
- fileChooser.setDialogTitle(LABEL_OPEN_FILE);
- }
- return fileChooser;
- }
- private class OpenImageFileAction extends AbstractAction {
- private OpenImageFileAction(String name) {
- super(name);
- }
- @Override
- public void actionPerformed(ActionEvent e) {
- JFileChooser jfc = getFileChooser();
- int openResult = jfc.showOpenDialog(ImageViewDemo.this);
- if( openResult == JFileChooser.APPROVE_OPTION ) {
- try {
- File file = jfc.getSelectedFile();
- BufferedImage image = ImageIO.read(file);
- imagePanel.setImage(image);
- pathField.setText(file.getAbsolutePath());
- }
- catch (IOException ex) {
- JOptionPane.showMessageDialog(ImageViewDemo.this, "Failed to open Image : " + ex.getMessage());
- }
- }
- }
- }
- }
代码有些凌乱,作为演示用的小测试也将就了……
下面是Win 7下的运行截图:
鼠标可以在图片的任意位置开始托拽,来改变显示的区域