《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 绘制图片,要实现对图片进行缩放和自由拖动。 1.以鼠标所在的位置为中心,滚轮控制缩放 2.缩放后再支持鼠标拖动。 基本原理: 利用scale() 函数。进行缩放。但是要注意的地方是,如果是在 public void paintCom…...
浅析人脸活体检测技术的功能及几种分类
在日常生活工作中,出现了人脸验证、人脸支付、人脸乘梯、人脸门禁等等常见的应用场景。这说明人脸识别技术已经在门禁安防、金融行业、教育医疗等领域被广泛地应用,人脸识别技术的高速发展与应用同时也出现不少质疑。其中之一就是人脸识别很容易被照片、…...
【Java基础面试三十五】、谈谈你对面向接口编程的理解
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:谈谈你对面向接口编程的…...
利用TreeMap来达成离散化的目的
假如有一些奶牛,他们有种类的区别,我们设黑色奶牛的id为1000010000,白色为1,诸如此类以此类推还有红色等各种颜色,接下来给你一群奶牛的颜色id让你统计每种颜色的奶牛有几头。 如过我们使用数组显然1000010000会爆空间…...
PG14安装_rpm方式
一、前期准备 发现生产环境有用rpm安装,故整理安装rpm安装步骤,目的是准备walminer恢复数据用的环境 二、安装包下载 https://download.postgresql.org/pub/repos/yum/ 含多个版本 https://download.postgresql.org/pub/repos/yum/14/redhat/rhel-7-x…...
水管安装过滤器笔记
文章目录 方案准备工具剪管钳热熔器软管接头及单向阀扳手 操作过程花洒 搬家后,水质不行,洗脸掉皮,洗头以前不掉头皮屑的,居然也掉头皮屑。有必要简单过滤下了。 水质情况,并不是脏脏的的那种水,看上去还比…...
【Objective-C】浅析Block及其捕获机制
目录 Block的基本使用Block的声明Block的实现Block的调用 Block作为形参使用Block作为属性使用给Block起别名Block的copy Block的捕获机制auto类型的局部变量__block浅析static类型的局部变量全局变量 其他问题 Block的基本使用 什么是Block? Block (块…...
GitHub和Gitee的区别以及具体使用
文章目录 GitHub和GiteeGitHub和Gitee区别GitHub的使用Gitee的使用 GitHub和Gitee GitHub和Gitee区别 速度不同:GitHub位于美国,而Gitee位于中国。这意味着在中国使用Gitee可能会有更快的访问速度和更好的稳定性。如果我们希望体验Git飞一般的速度&…...
使用Redis实现分布式锁解决商品超卖问题(接上篇文章)
1. RedLock(红锁)简介 RedLock是一种用于分布式系统的锁定算法,旨在提供分布式锁的高可用性和分布式容错性。它是由Redis的创建者Salvatore Sanfilippo提出的,用于克服Redis单实例的单点故障问题。RedLock的目标是确保在多个Redis…...
2001-2022年全国290+个地级市高铁开通数据
2001-2022年全国290个地级市高铁开通数据 1、时间:2001-2022年 2、范围:298地级市(293地级市数(其中莱芜市2019年撤市设区)4直辖市数 ) 3、来源:国家铁路局、铁路客货运输专刊及相关统计 国…...
【Java 进阶篇】深入浅出:Bootstrap 轮播图
在现代网页设计中,轮播图是一个常见的元素。它们可以用于展示图片、广告、新闻、产品或任何您希望吸引用户注意力的内容。要实现一个轮播图,您通常需要一些复杂的HTML、CSS和JavaScript代码,这对于初学者来说可能会感到困难。但幸运的是&…...
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;}…...
网络协议--IP:网际协议
3.1 引言 IP是TCP/IP协议族中最为核心的协议。所有的TCP、UDP、ICMP及IGMP数据都以IP数据报格式传输(见图1-4)。许多刚开始接触TCP/IP的人对IP提供不可靠、无连接的数据报传送服务感到很奇怪。 不可靠(unreliable)的意思是它不能…...
【考研数学】线性代数第六章 —— 二次型(3,正定矩阵与正定二次型)
文章目录 一、基本概念1.1 引例1.2 正定二次型概念 二、正定二次型的判别写在最后 一、基本概念 1.1 引例 (1)二次型 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)x123…...
【Java 进阶篇】手把手教你创建 Bootstrap 旅游网站
随着互联网的普及,旅游行业在全球范围内迅速发展。人们通过网络规划、预订和分享他们的旅行经历。因此,拥有一个令人印象深刻的旅游网站对于吸引游客和提供有用信息至关重要。在本篇博客中,我们将手把手教您如何创建一个令人兴奋的旅游网站&a…...
一百九十二、Flume——Flume数据流监控工具Ganglia单机版安装
一、目的 在安装好Flume之后,需要用一个工具可以对Flume数据传输进行实时监控,这就是Ganglia 二、Ganglia介绍 Ganglia 由 gmond、gmetad 和 gweb 三部分组成。 (一)第一部分——gmond gmond(Ganglia Monitoring Da…...
光学知识整理-偏振光
偏振光 目录基础概念基础概念的补充平面偏振光(线偏振光)部分偏振光圆偏振光椭圆偏振光菲涅耳公式相位关系 反射折射所引起的偏振态的改变斯托克斯倒逆关系重要参数 目录 基础概念 光是横波:光是电磁波,其电场分量(电场强度)E、磁场分量(磁…...
CUDA纹理内存tex1D/tex2D/tex3D函数
CUDA的tex1D是用于从一维纹理中读取数据的函数。纹理是一种特殊的内存区域,可以用来存储图像、视频或其他数据。tex1D函数可以用于从纹理中读取数据,并将其传递给CUDA程序。 tex1D函数的语法如下: float tex1D(sampler_t sampler, float te…...
【Java基础面试三十八】、请介绍Java的异常接口
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:请介绍Java的异常接口 …...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
