《java 桌面软件开发》swing 以鼠标为中心放大缩小移动图片

swing 使用Graphic2D 绘制图片,要实现对图片进行缩放和自由拖动。

1.以鼠标所在的位置为中心,滚轮控制缩放

2.缩放后再支持鼠标拖动。

基本原理:

利用scale() 函数。进行缩放。但是要注意的地方是,如果是在 public void paintComponent(Graphics g) 里面通过这个Graphics g 参数获取graphic对象进行绘制,scale不会影响下一次的绘制。
一:所以,我们可以自行创建一个 "绘图区", 创建一个空的ImageBuffer, 然后获取这个ImageBuffer的 Graphics, 后续全部往这个ImageBuffer的 Graphics 绘制.
二: 最后在frame的paintComponent把我们这个 绘图区原样展示出来即可。 即,frame的
paintComponent只是固定将 绘图区作为一个图片绘制。
三:甚至可以创建多个ImageBuffer ,实现类似于ps多图层的样子,各个图层独立,paitComponent 汇总显示图层。

自己创建的ImageBuffer的 Graphics ,每次scale都是以上一次作为基础,累计的缩放。

利用transrate进行移动,(移动的是坐标系)。 每次transrate都是以上一次作为基础,累计的平移。

来实现我们的关键代码:

作为demo, 代码尽量是一个 main()到底:

swingDemo.java

java 复制代码
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;

public class swingDemo {

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

	public static Color BG_COLOR = new Color(128, 128, 128);

	public swingDemo() {
		JFrame mjf = new JFrame("图片查看");
		ImagePanle mImgeView = new ImagePanle();
		mImgeView.setPreferredSize(new Dimension(500, 500));
		mImgeView.setMinimumSize(new Dimension(500, 500));
		mImgeView.setBackground(BG_COLOR);

		JMenuBar jmb = new JMenuBar();
		JMenu meSetting = new JMenu("文件");
		JMenuItem mOpen = new JMenuItem("打开");

		mOpen.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub

				BufferedImage curBufferedImg;
				JFileChooser fileChooser = new JFileChooser();
				fileChooser.setMultiSelectionEnabled(true);
				fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
				fileChooser.setMultiSelectionEnabled(false);
				int option = fileChooser.showOpenDialog(mjf);
				if (option == JFileChooser.APPROVE_OPTION) {
					try {
						File file = fileChooser.getSelectedFile();
						curBufferedImg = ImageIO.read(new File(file.getAbsolutePath()));
						mImgeView.updateImage(curBufferedImg);
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}

			}
		});

		meSetting.add(mOpen);
		jmb.add(meSetting);

		mjf.setJMenuBar(jmb);
		mjf.add(mImgeView);

		mjf.setMinimumSize(new Dimension(800, 600));
		mjf.setVisible(true);
		mjf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	}

	class ImagePanle extends JPanel {

		BufferedImage mSrcBuffeImg = null;
		private static final long serialVersionUID = 1L;
		private double mScale = 1.0;
		private static final boolean B_REAL_SIZE = true;
		private double mCurX = 0;
		private double mCurY = 0;
		private double mStartX = 0;
		private double mStartY = 0;
		private double mTranslateX = 0;
		private double mTranslateY = 0;

//		记录最初原始坐标系,用于清除背景
		AffineTransform mOriginTransform;
		BufferedImage mViewBufferImg;
		Graphics2D mViewG2d;

		void refreshView() {
			clear_buffer(mViewG2d, mOriginTransform, mViewBufferImg);
			mViewG2d.drawImage(mSrcBuffeImg, 0, 0, null);
			repaint();
		}

		void clear_buffer(Graphics2D g2d, AffineTransform org, BufferedImage bufImg) {
//			将保存的测量数据,重新在经过变换后的坐标系上进行绘制
			// 先恢复一下原始状态,保证清空的坐标是全部,执行清空,然后再切会来
			AffineTransform temp = g2d.getTransform();
			g2d.setTransform(org);
			g2d.clearRect(0, 0, bufImg.getWidth(), bufImg.getHeight());
			g2d.setTransform(temp);
		}

		public void updateImage(BufferedImage srcImage) {
			mSrcBuffeImg = srcImage;
			mViewBufferImg = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_ARGB);
			System.out.println("create buff image");
			mViewG2d = mViewBufferImg.createGraphics();
			mViewG2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			mViewG2d.setBackground(BG_COLOR);
			System.out.println("crate bufg2d");
			mOriginTransform = mViewG2d.getTransform();
			refreshView();
		}

		private Point internal_getImagePoint(double mouseX, double mouseY) {

			// 不管是先平移后缩放还是先缩放后平移,都以 先减 再缩放的方式可以获取正确
			double rawTranslateX = mViewG2d.getTransform().getTranslateX();
			double rawTranslateY = mViewG2d.getTransform().getTranslateY();
			// 获取当前的 Scale Transform
			double scaleX = mViewG2d.getTransform().getScaleX();
			double scaleY = mViewG2d.getTransform().getScaleY();

//        		不管是先平移后缩放还是先缩放后平移,都以 先减 再缩放的方式可以获取正确
			int imageX = (int) ((mouseX - rawTranslateX) / scaleX);
			int imageY = (int) ((mouseY - rawTranslateY) / scaleY);

			return new Point(imageX, imageY);
		}

		public ImagePanle() {

//			启用双缓存
			setDoubleBuffered(true);

			this.addMouseWheelListener((MouseWheelListener) new MouseWheelListener() {
				@Override
				public void mouseWheelMoved(MouseWheelEvent e) {
					if (mViewG2d == null) {
						return;
					}
					mCurX = e.getX();
					mCurY = e.getY();

					int notches = e.getWheelRotation();
					if (notches < 0) {
						// 滚轮向上,放大画布
						mScale = 1.1;

					} else {
						// 滚轮向下,缩小画布
						mScale = 0.9;
					}

					Point imagePoint = internal_getImagePoint(e.getX(), e.getY());
					int imageX = imagePoint.x;
					int imageY = imagePoint.y;
					System.out.println("x:" + e.getX() + "y:" + e.getY() + ",imagex:" + imageX + "x" + imageY);

					double tralateX = mScale * imageX - imageX;
					double tralateY = mScale * imageY - imageY;

					mViewG2d.scale(mScale, mScale);
					mViewG2d.translate(-tralateX / mScale, -tralateY / mScale); // 图片方大,就需要把坐标往左移动,移动的尺度是要考虑缩放的
					// 先恢复一下原始状态,保证清空的坐标是全部,执行清空,然后再切会来
					AffineTransform temp = mViewG2d.getTransform();
					mViewG2d.setTransform(mOriginTransform);
					mViewG2d.clearRect(0, 0, mViewBufferImg.getWidth(), mViewBufferImg.getHeight());
					mViewG2d.setTransform(temp);

					mViewG2d.drawImage(mSrcBuffeImg, 0, 0, null);
					repaint(); // 重新绘制画布
				}

			});

			this.addMouseListener(new MouseListener() {

				@Override
				public void mouseReleased(MouseEvent e) {
					// TODO Auto-generated method stub
					System.out.println("mouseReleased:" + e.getX() + "x" + e.getY());
				}

				@Override
				public void mousePressed(MouseEvent e) {
					// TODO Auto-generated method stub
					System.out.println("mousePressed----:" + e.getX() + "x" + e.getY());
					mStartX = e.getX();
					mStartY = e.getY();
				}

				@Override
				public void mouseExited(MouseEvent e) {
					// TODO Auto-generated method stub

				}

				@Override
				public void mouseEntered(MouseEvent e) {
					// TODO Auto-generated method stub

				}

				@Override
				public void mouseClicked(MouseEvent e) {
					// TODO Auto-generated method stub
					System.out.println("mouseClicked----:" + e.getX() + "x" + e.getY());

				}
			});
			this.addMouseMotionListener(new MouseAdapter() {

				@Override
				public void mouseMoved(MouseEvent e) {
					// TODO Auto-generated method stub
				}

				@Override
				public void mouseDragged(MouseEvent e) {
					// TODO Auto-generated method stub
					if (mViewG2d == null) {
						return;
					}

					mCurX = e.getX();
					mCurY = e.getY();
					System.out.println("mouseDragged:" + e.getX() + "x" + e.getY() + "trans:" + (mCurX - mStartX) + ":"
							+ (mCurY - mStartY));

					// 平移坐标,也是相对于变换后的坐标系而言的,所以
					double scaleX = mViewG2d.getTransform().getScaleX();
					double scaleY = mViewG2d.getTransform().getScaleY();

					// TODO mCurX - mStartX 太小,比如为2, 而scalX 比较大,比如为3 则移动的时候回发生 (int)2/3 ==0; 不移动。
					// 解决方案,把移动 ,全部在原始坐标系上做,也就是最后绘制缓冲区的时候,drawimage(transX,transY)
					mTranslateX = (mCurX - mStartX) / scaleX;
					mTranslateY = (mCurY - mStartY) / scaleY;

					// 自身就是累计的
					mViewG2d.translate(mTranslateX, mTranslateY);

					mStartX = mCurX;
					mStartY = mCurY;
					System.out.println("mouseDragged: over+++");

					// 先恢复一下原始状态,保证清空的坐标是全部,执行清空,然后再切会来
					AffineTransform temp = mViewG2d.getTransform();
					mViewG2d.setTransform(mOriginTransform);
					mViewG2d.clearRect(0, 0, mViewBufferImg.getWidth(), mViewBufferImg.getHeight());
					mViewG2d.setTransform(temp);

					mViewG2d.drawImage(mSrcBuffeImg, 0, 0, null);
					repaint();
				}
			});

		}

		public void reset_scale() {
//			恢复到1.0 缩放,0,0 左上角对齐
			mCurX = 0;
			mCurY = 0;
			mScale = 1.0;
			mViewG2d.setTransform(mOriginTransform);
			mViewG2d.clearRect(0, 0, mViewBufferImg.getWidth(), mViewBufferImg.getHeight());
			mViewG2d.drawImage(mSrcBuffeImg, 0, 0, null);
			repaint(); // 重新绘制画布
		}

		@Override
		public void paintComponent(Graphics g) {

			super.paintComponent(g);
			if (mViewBufferImg == null) {
				return;
			}
//			如果有多个"图层"要注意图层的顺序
			Graphics2D g2d = ((Graphics2D) g);
			g2d.drawImage(mViewBufferImg, 0, 0, null);
			System.out.println("draw-----------:" + getWidth() + "x" + getHeight());
		}
	}

}
相关推荐
九圣残炎7 分钟前
【从零开始的LeetCode-算法】3354. 使数组元素等于零
java·算法·leetcode
寒笙LED13 分钟前
C++详细笔记(六)string库
开发语言·c++·笔记
IT书架20 分钟前
golang面试题
开发语言·后端·golang
初遇你时动了情37 分钟前
uniapp 城市选择插件
开发语言·javascript·uni-app
天天扭码41 分钟前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
程序猿小柒1 小时前
leetcode hot100【LeetCode 4.寻找两个正序数组的中位数】java实现
java·算法·leetcode
不爱学习的YY酱1 小时前
【操作系统不挂科】<CPU调度(13)>选择题(带答案与解析)
java·linux·前端·算法·操作系统
zongzi_4941 小时前
二次封装的天气时间日历选择组件
开发语言·javascript·ecmascript
丁总学Java1 小时前
Maven项目打包,com.sun.tools.javac.processing
java·maven
kikyo哎哟喂2 小时前
Java 代理模式详解
java·开发语言·代理模式