有人发贴问,一个 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下的运行截图:
鼠标可以在图片的任意位置开始托拽,来改变显示的区域。