当前位置: 首页 > 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的异常接口 …...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...