《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


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());
		}
	}

}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/103757.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker搭建Plex流媒体服务并播放自己本地视频

Docker搭建Plex流媒体服务 安装Docker创建存储配置文件的目录创建Plex容器配置Plex设置媒体库访问Plex 1 介绍 Plex是一个流媒体服务器&#xff0c;可以轻松地将你的媒体文件库&#xff08;如电影、电视节目和音乐&#xff09;通过网络流式传输到各种设备上。 Plex 是一套媒体…

Android 系统架构

首语 由于工作内容的转变&#xff0c;使得我向Android系统方向转变&#xff0c;对于一个Android系统工程师&#xff0c;了解Android整个系统架构是必然的。本篇是Android系统学习的开篇&#xff0c;Android系统庞大且复杂&#xff0c;但是能对Android的认识更深&#xff0c;更…

2019年亚太杯APMCM数学建模大赛B题区域经济活力及其影响因素的分析与决策求解全过程文档及程序

2019年亚太杯APMCM数学建模大赛 B题 区域经济活力及其影响因素的分析与决策 原题再现 区域&#xff08;或城市或省级&#xff09;经济活力是区域综合竞争力的重要组成部分。近年来&#xff0c;为了提高经济活力&#xff0c;一些地区推出了许多刺激经济活力的优惠政策&#xf…

Day 10 python学习笔记

高阶函数 map( ) map()函数接收两个参数&#xff0c;一个是函数&#xff0c;一个是Iterable&#xff0c;map将传入的函数依次作用到序列的每个元素&#xff0c;并把结果作为新的Iterator返回。&#xff08;Iterator是一个可迭代对象&#xff09; 再简单点说&#xff1a;从可迭代…

LeetCode讲解篇之面试题 01.08. 零矩阵

文章目录 题目描述题解思路题解代码 题目描述 题解思路 遍历矩阵&#xff0c;若当前元素为零&#xff0c;则将该行和该列的第一个元素置零 遍历第一行&#xff0c;若当前元素为零&#xff0c;则将当前列置零 遍历第一列&#xff0c;若当前元素为零&#xff0c;则将当前行置零 …

整理指定文件夹下的所有文件,以类树状图显示并生成对应超链接

最近在整理家里学习资料的时候&#xff0c;由于年代久远&#xff0c;找不到我想要找的文件&#xff0c;windows文件搜索速度感觉太慢。于是想要生成一份类似文件索引的东西来显示所有资料&#xff0c;让我可以快速的找到需要的资料路径 直接上代码 import os import datetim…

73 应急响应-WEB分析phpjavaweb自动化工具

目录 应急响应&#xff1a;必备知识点&#xff1a;准备工作&#xff1a;有明确信息网站被入侵&#xff1a;无明确信息网站被入侵&#xff1a;常见分析方法&#xff1a; 演示案例&#xff1a;WindowsIISSql-日志&#xff0c;搜索LinuxBT_Nginxtp5-日志,后门360星图日志自动分析工…

Sui提供dApp Kit 助力快速构建React Apps和dApps

近日&#xff0c;Mysten Labs推出了dApp Kit&#xff0c;这是一个全新的解决方案&#xff0c;可用于在Sui上开发React应用程序和去中心化应用程序&#xff08;dApps&#xff09;。mysten/dapp-kit是专门为React定制的全新SDK&#xff0c;旨在简化诸如连接钱包、签署交易和从RPC…

PyTorch 与 TensorFlow:机器学习框架之战

深度学习框架是简化人工神经网络 (ANN) 开发的重要工具&#xff0c;并且其发展非常迅速。其中&#xff0c;TensorFlow 和 PyTorch 脱颖而出&#xff0c;各自在不同的机器学习领域占有一席之地。但如何为特定项目确定理想的工具呢&#xff1f;本综合指南[1]旨在阐明它们的优点和…

报错:Could not resolve host: mirrorlist.centos.org;Unknown error

报错&#xff1a;Could not resolve host: mirrorlist.centos.org;Unknown error 一般是因为网络配置错误导致无法连接外网&#xff0c;我们先尝试ping一下www.baidu.com发现无法ping通。 果然&#xff0c;接下来我们就开始排查吧&#xff01;&#xff01; 1.网络配置查看 打开…

SpringBoot 源码分析(四) 内置Tomcat分析

一、Tomcat相关知识 1. tomcat目录结构 Tomcat文件的目录结构 2.启动流程 启动一个Tomcat服务是执行的bin目录下的脚本程序&#xff0c;startup.bat和 startup.sh.一个是windows的脚本&#xff0c;一个是Linux下的脚本&#xff0c;同样还可以看到两个停止的脚本 shutdown.ba…

64从零开始学Java之关于日期时间的新特性

作者&#xff1a;孙玉昌&#xff0c;昵称【一一哥】&#xff0c;另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在上一篇文章中&#xff0c;壹哥给大家讲解了Java里的格式化问题&#xff0c;这样我们就可以个性化设…

网络套接字编程

1.基础预备知识 1.1源ip和目的ip 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址 源IP地址表示发起通信的设备的IP地址。它是数据包的出发点&#xff0c;标识了数据包的来源。当一个设备发送数据包到网络上的其他设备时&#xff0c;该数据包的源IP字段会被…

WSL2的安装与配置(创建Anaconda虚拟环境、更新软件包、安装PyTorch、VSCode)

1. WSL2 安装 以管理员身份打开 PowerShell&#xff08;“开始”菜单 >“PowerShell” >单击右键 >“以管理员身份运行”&#xff09;&#xff0c;然后输入以下命令&#xff1a; dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /a…

Maven学习

Maven介绍 Maven是Apache的一个开源项目&#xff0c;主要服务于基于Java平台的项目构建&#xff0c;依赖管理和项目信息管理。 Maven可以让团队能够更科学的构建项目&#xff0c;我们可以用配置文件的方式&#xff0c;对项目的名称、描述、项目版本号、项目依赖等信息进行描述…

中文编程开发语言编程实际案例:程序控制灯电路以及桌球台球室用这个程序计时计费

中文编程开发语言编程实际案例&#xff1a;程序控制灯电路以及桌球台球室用这个程序计时计费 上图为&#xff1a;程序控制的硬件设备电路图 上图为&#xff1a;程序控制灯的开关软件截图&#xff0c;适用范围比如&#xff1a;台球厅桌球室的计时计费管理&#xff0c;计时的时候…

RedHat8升级GLIBC_2.29,解决ImportError: /lib64/libm.so.6: version `GLIBC_2.29

问题背景 在做大模型微调训练时&#xff0c;执行python脚本时出现如下报错&#xff1a; 查看当前服务器版本&#xff0c;确实没有GLIBC_2.29的 strings /lib64/libm.so.6 | grep GLIBC_ GLIBC_2.2.5 GLIBC_2.4 GLIBC_2.15 GLIBC_2.18 GLIBC_2.23 GLIBC_2.24 GLIBC_2.25 GLIB…

MySQL的优化利器:索引条件下推,千万数据下性能提升273%

MySQL的优化利器&#xff1a;索引条件下推&#xff0c;千万数据下性能提升273%&#x1f680; 前言 上个阶段&#xff0c;我们聊过MySQL中字段类型的选择&#xff0c;感叹不同类型在千万数据下的性能差异 时间类型&#xff1a;MySQL字段的时间类型该如何选择&#xff1f;千万…

[Go版]算法通关村第十八关青铜——透析回溯的模版

目录 认识回溯思想回溯的代码框架从 N 叉树说起有的问题暴力搜索也不行回溯 递归 局部枚举 放下前任Go代码【LeetCode-77. 组合】回溯热身-再论二叉树的路径问题题目&#xff1a;二叉树的所有路径Go 代码 题目&#xff1a;路径总和 IIGo 代码 回溯是最重要的算法思想之一&am…

【Jenkins 安装】

一&#xff1a;安装文件夹准备 在/home/admin 界面下新建三个文件夹&#xff0c;用来安装tomcat、maven 1.打开&#xff0c;/home/admin目录 cd /home/admin 2.新建三个文件夹 mkdir tomcat mkdir maven 二&#xff1a;安装tomcat 1.打开tomcat目录进行tomcat的安装 访问:h…