可以托拽的图片显示控件,JViewport 用法演示

有人发贴问,一个 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下的运行截图:




鼠标可以在图片的任意位置开始托拽,来改变显示的区域。

原文地址:https://www.cnblogs.com/bbsno1/p/3262784.html