当前位置: 首页 > news >正文

《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() {@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubBufferedImage 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 blocke1.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 Transformdouble 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() {@Overridepublic 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() {@Overridepublic void mouseReleased(MouseEvent e) {// TODO Auto-generated method stubSystem.out.println("mouseReleased:" + e.getX() + "x" + e.getY());}@Overridepublic void mousePressed(MouseEvent e) {// TODO Auto-generated method stubSystem.out.println("mousePressed----:" + e.getX() + "x" + e.getY());mStartX = e.getX();mStartY = e.getY();}@Overridepublic void mouseExited(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseEntered(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseClicked(MouseEvent e) {// TODO Auto-generated method stubSystem.out.println("mouseClicked----:" + e.getX() + "x" + e.getY());}});this.addMouseMotionListener(new MouseAdapter() {@Overridepublic void mouseMoved(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseDragged(MouseEvent e) {// TODO Auto-generated method stubif (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(); // 重新绘制画布}@Overridepublic 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());}}}


 

相关文章:

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

swing 使用Graphic2D 绘制图片&#xff0c;要实现对图片进行缩放和自由拖动。 1.以鼠标所在的位置为中心&#xff0c;滚轮控制缩放 2.缩放后再支持鼠标拖动。 基本原理&#xff1a; 利用scale() 函数。进行缩放。但是要注意的地方是&#xff0c;如果是在 public void paintCom…...

浅析人脸活体检测技术的功能及几种分类

在日常生活工作中&#xff0c;出现了人脸验证、人脸支付、人脸乘梯、人脸门禁等等常见的应用场景。这说明人脸识别技术已经在门禁安防、金融行业、教育医疗等领域被广泛地应用&#xff0c;人脸识别技术的高速发展与应用同时也出现不少质疑。其中之一就是人脸识别很容易被照片、…...

【Java基础面试三十五】、谈谈你对面向接口编程的理解

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;谈谈你对面向接口编程的…...

利用TreeMap来达成离散化的目的

假如有一些奶牛&#xff0c;他们有种类的区别&#xff0c;我们设黑色奶牛的id为1000010000&#xff0c;白色为1&#xff0c;诸如此类以此类推还有红色等各种颜色&#xff0c;接下来给你一群奶牛的颜色id让你统计每种颜色的奶牛有几头。 如过我们使用数组显然1000010000会爆空间…...

PG14安装_rpm方式

一、前期准备 发现生产环境有用rpm安装&#xff0c;故整理安装rpm安装步骤&#xff0c;目的是准备walminer恢复数据用的环境 二、安装包下载 https://download.postgresql.org/pub/repos/yum/ 含多个版本 https://download.postgresql.org/pub/repos/yum/14/redhat/rhel-7-x…...

水管安装过滤器笔记

文章目录 方案准备工具剪管钳热熔器软管接头及单向阀扳手 操作过程花洒 搬家后&#xff0c;水质不行&#xff0c;洗脸掉皮&#xff0c;洗头以前不掉头皮屑的&#xff0c;居然也掉头皮屑。有必要简单过滤下了。 水质情况&#xff0c;并不是脏脏的的那种水&#xff0c;看上去还比…...

【Objective-C】浅析Block及其捕获机制

目录 Block的基本使用Block的声明Block的实现Block的调用 Block作为形参使用Block作为属性使用给Block起别名Block的copy Block的捕获机制auto类型的局部变量__block浅析static类型的局部变量全局变量 其他问题 Block的基本使用 什么是Block&#xff1f; Block &#xff08;块…...

GitHub和Gitee的区别以及具体使用

文章目录 GitHub和GiteeGitHub和Gitee区别GitHub的使用Gitee的使用 GitHub和Gitee GitHub和Gitee区别 速度不同&#xff1a;GitHub位于美国&#xff0c;而Gitee位于中国。这意味着在中国使用Gitee可能会有更快的访问速度和更好的稳定性。如果我们希望体验Git飞一般的速度&…...

使用Redis实现分布式锁解决商品超卖问题(接上篇文章)

1. RedLock&#xff08;红锁&#xff09;简介 RedLock是一种用于分布式系统的锁定算法&#xff0c;旨在提供分布式锁的高可用性和分布式容错性。它是由Redis的创建者Salvatore Sanfilippo提出的&#xff0c;用于克服Redis单实例的单点故障问题。RedLock的目标是确保在多个Redis…...

2001-2022年全国290+个地级市高铁开通数据

2001-2022年全国290个地级市高铁开通数据 1、时间&#xff1a;2001-2022年 2、范围&#xff1a;298地级市&#xff08;293地级市数&#xff08;其中莱芜市2019年撤市设区&#xff09;4直辖市数 &#xff09; 3、来源&#xff1a;国家铁路局、铁路客货运输专刊及相关统计 国…...

【Java 进阶篇】深入浅出:Bootstrap 轮播图

在现代网页设计中&#xff0c;轮播图是一个常见的元素。它们可以用于展示图片、广告、新闻、产品或任何您希望吸引用户注意力的内容。要实现一个轮播图&#xff0c;您通常需要一些复杂的HTML、CSS和JavaScript代码&#xff0c;这对于初学者来说可能会感到困难。但幸运的是&…...

75. 颜色分类

75. 颜色分类 双指针——快慢指针 class Solution {public void sortColors(int[] nums) {int n nums.length;int p0 0;for(int i 0; i < n; i){if(nums[i] 0){swap(nums, p0, i);p0;}}int p1 p0;for(int i p0; i < n; i){if(nums[i] 1){swap(nums, p1, i);p1;}…...

Multi-Modal Diagnosis of Infectious Diseases in the Developing World

方法 作者未提供代码...

网络协议--IP:网际协议

3.1 引言 IP是TCP/IP协议族中最为核心的协议。所有的TCP、UDP、ICMP及IGMP数据都以IP数据报格式传输&#xff08;见图1-4&#xff09;。许多刚开始接触TCP/IP的人对IP提供不可靠、无连接的数据报传送服务感到很奇怪。 不可靠&#xff08;unreliable&#xff09;的意思是它不能…...

【考研数学】线性代数第六章 —— 二次型(3,正定矩阵与正定二次型)

文章目录 一、基本概念1.1 引例1.2 正定二次型概念 二、正定二次型的判别写在最后 一、基本概念 1.1 引例 &#xff08;1&#xff09;二次型 f ( x 1 , x 2 , x 3 ) x 1 2 3 x 2 2 2 x 3 2 X T A X f(x_1,x_2,x_3)x_1^23x_2^22x_3^2\pmb{X^TAX} f(x1​,x2​,x3​)x12​3…...

【Java 进阶篇】手把手教你创建 Bootstrap 旅游网站

随着互联网的普及&#xff0c;旅游行业在全球范围内迅速发展。人们通过网络规划、预订和分享他们的旅行经历。因此&#xff0c;拥有一个令人印象深刻的旅游网站对于吸引游客和提供有用信息至关重要。在本篇博客中&#xff0c;我们将手把手教您如何创建一个令人兴奋的旅游网站&a…...

一百九十二、Flume——Flume数据流监控工具Ganglia单机版安装

一、目的 在安装好Flume之后&#xff0c;需要用一个工具可以对Flume数据传输进行实时监控&#xff0c;这就是Ganglia 二、Ganglia介绍 Ganglia 由 gmond、gmetad 和 gweb 三部分组成。 &#xff08;一&#xff09;第一部分——gmond gmond&#xff08;Ganglia Monitoring Da…...

光学知识整理-偏振光

偏振光 目录基础概念基础概念的补充平面偏振光&#xff08;线偏振光&#xff09;部分偏振光圆偏振光椭圆偏振光菲涅耳公式相位关系 反射折射所引起的偏振态的改变斯托克斯倒逆关系重要参数 目录 基础概念 光是横波&#xff1a;光是电磁波,其电场分量(电场强度)E、磁场分量(磁…...

CUDA纹理内存tex1D/tex2D/tex3D函数

CUDA的tex1D是用于从一维纹理中读取数据的函数。纹理是一种特殊的内存区域&#xff0c;可以用来存储图像、视频或其他数据。tex1D函数可以用于从纹理中读取数据&#xff0c;并将其传递给CUDA程序。 tex1D函数的语法如下&#xff1a; float tex1D(sampler_t sampler, float te…...

【Java基础面试三十八】、请介绍Java的异常接口

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;请介绍Java的异常接口 …...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...