java SWT:自定义布局(Layout)实现组件自动缩放显示

2024-03-23 07:20

本文主要是介绍java SWT:自定义布局(Layout)实现组件自动缩放显示,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

什么是布局(Layout)

窗口布局(Layout)其实是指Composite中组件的一种定位原则的实现,当Composite改变大小时,会自动调用Composite初始化时设置的Layout对象来重新调整所有组件的位置。
一般的UI框架都提供了一些默认布局,比如SWT中的FillLayout,GridLayout…如果使用WindowBuilder开发UI,可以在Design界面下看到所有SWT提供的布局对象,见下图
这里写图片描述

自定义布局

有的时候,使用SWT提供的布局是无法满足需要的,这种情况下,就需要自实现所需的特殊布局。
实现自定义的Layout并不复杂,
以下是org.eclipse.swt.widgets.Layout的简要注释说明:

package org.eclipse.swt.widgets;
import org.eclipse.swt.graphics.*;/*** 布局抽象类,* 用于控制组件内所有子对象的位置和尺寸*/
public abstract class Layout {/*** 必须实现的抽象方法* 返回容器组件(父窗口)的Client区域尺寸*/
protected abstract Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache);protected boolean flushCache (Control control) {return false;
}/*** 必须实现的抽象方法* 设置所有容器组件(父窗口)内所有子组件的位置和大小* @param composite 将被重新设置布局的容器组件(父窗口)* @param flushCache <code>true</code> means flush cached layout values*/
protected abstract void layout (Composite composite, boolean flushCache);
}

从上面的代码可以知道,只要实现抽象类org.eclipse.swt.widgets.Layout的两个抽象方法就可以实现一个特殊布局了,SWT提供的那些默认布局类都是通过继承Layout实现的

关于Layout的详细原文说明参见SWT的javadoc
http://help.eclipse.org/neon/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Layout.html

组件自动缩放显示

上一节讲完Layout的实现思路,下面就以以一个实例来说明如何实现自定义布局。
比如下面的图中矩形框,并不是画在背景图上的,而是背景透明的Composite,可以移动和改变尺寸(如何实现,参见我的上一篇博客《 java SWT入门:自定义背景透明且可鼠标拖动改变尺寸和位置的Composite》)
这些矩形用于对图像中的人脸位置进行标注,我们希望当图像大小和位置改变的时候,这些矩形在图像上的相对位置保持不变。
这里写图片描述
这里写图片描述

这种需求,SWT中现成的布局都不能满足要求,所以就要自己实现一个,以下是实现代码,
ActiveRectContainer.java

package net.gdface.ui;import java.net.URL;
import java.util.ArrayList;
import java.util.List;import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.wb.swt.SWTResourceManager;/*** 活动矩形显示容器* 窗口尺寸改变时所有{@link ActiveRectangle}对象自动等比例改变* * @author guyadong**/
public class ActiveRectContainer extends Decorations {/*** 创建自定义的布局对象实现窗口内的ActiveRectangle对象能根据父窗口的尺寸改变而同步等比例改变,* 以保持每一个矩形在父窗口上的相对位置不变 * @author guyadong**/class ZoomLayout extends Layout {@Overrideprotected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {// 返回可以画图的区域Rectangle bounds = composite.getClientArea();return new Point(bounds.width, bounds.height);}@Overrideprotected void layout(Composite composite, boolean flushCache) {Control[] childrens = composite.getChildren();Rectangle originalbounds = getBackgroundImage().getBounds();Rectangle bounds = composite.getBounds();// 计算x/y轴缩放比例float zoomX = (float) bounds.width / originalbounds.width;float zoomY = (float) bounds.height / originalbounds.height;for (Control children : childrens) {if (children instanceof ActiveRectangle) {// 对于ActiveRectangle对象调用zoom方法改变bounds((ActiveRectangle) children).zoom(zoomX, zoomY);}else if(children.getLayoutData() instanceof Rectangle){// 对于其他Control对象调用如果通过setLayoutData()设置了原始的对象尺寸,则可以根据父窗口尺寸同步改变boundsRectangle originalBounds=(Rectangle) children.getLayoutData();// 获取Control原始位置尺寸children.setBounds(new Rectangle((int)(originalBounds.x*zoomX),(int)(originalBounds.y*zoomY),(int)(originalBounds.width*zoomX),(int)(originalBounds.height*zoomY)));}}}}protected List<ActiveRectangle> annRects=EMPTY_RECTS;private static final List<ActiveRectangle> EMPTY_RECTS=new ArrayList<ActiveRectangle>();/*** @param parent* @param image 显示的背景图像,为null时不显示* @param rects 显示的矩形对象数组* @param focusIndex 焦点矩形索引,超出 rects索引范围时无效*/public ActiveRectContainer(Composite parent, Image image, Rectangle[] rects, int focusIndex) {super(parent, SWT.BORDER|SWT.RESIZE);if(null!=rects&&rects.length>0){this.annRects=new ArrayList<ActiveRectangle>();for(Rectangle rect:rects){// 创建矩形对象(ActiveRectangle)annRects.add(new ActiveRectangle(this, false,rect));}try{// 设置焦点对象this.annRects.get(focusIndex).focus=true;}catch(IndexOutOfBoundsException  e){}}this.setBackgroundImage(null==image?SWTResourceManager.getMissingImage():image);// 设置自定义布局对象        this.setLayout(new ZoomLayout());addPaintListener(new PaintListener() {@Overridepublic void paintControl(PaintEvent e) {// 调用重绘方法paintImage(e.gc);}});}/*** @param parent* @param url 背景图像的URL* @param rects* @param focusIndex * @see #AutoZoomRecContainer(Composite, Image, Rectangle[], int)*/public ActiveRectContainer(Composite parent, URL url, Rectangle[] rects, int focusIndex) {this(parent, SWTResourceManager.getImage(url),rects, focusIndex);}/*** 将 {@link #image} 重绘图像到窗口(缩放到整个窗口)* * @param gc*/protected final void paintImage(GC gc) {boolean isAdvanced = gc.getAdvanced();try {gc.setAdvanced(true);gc.setAntialias(SWT.ON);Point destSize = getSize();Image image = getBackgroundImage();Rectangle imgSize = image.getBounds();gc.drawImage(image, 0, 0, imgSize.width, imgSize.height, 0, 0, destSize.x, destSize.y);} finally {gc.setAdvanced(isAdvanced);}}/*** 以窗口中心为原点对窗口进行缩放* @param zoomX x轴缩放比例* @param zoomY x轴缩放比例*/public void zoomCenter(float zoomX, float zoomY) {// 以背景图像的尺寸为当前对象的原始尺寸Rectangle originalSize = getBackgroundImage().getBounds();Rectangle bounds=getBounds();// 缩放后的尺寸int width=(int) (originalSize.width*zoomX);int height=(int) (originalSize.height*zoomX);// 中心点位置int x=bounds.x+(bounds.width-width)/2;int y=bounds.y+(bounds.height- height)/2;       super.setBounds(x, y, width, height);}/*** x/y轴等比例缩放* @param zoom 缩放比例* @see #zoomCenter(float, float)*/public void zoomCenter(float zoom) {zoomCenter(zoom,zoom);}@Overrideprotected void checkSubclass() {}}

注意:自定义布局实现在ActiveRectContainer.java的代码中是以一个内部类ZoomLayout 来实现的

以下是用WindowBuilder生成的测试代码
TestRectContainer.java

package testwb;import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.wb.swt.SWTResourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import net.gdface.ui.ActiveRectContainer;import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;public class TestRectContainer {static final Logger logger = LoggerFactory.getLogger(TestRectContainer.class);protected Shell shell;/*** Launch the application.* @param args*/public static void main(String[] args) {try {TestRectContainer window = new TestRectContainer();window.open();} catch (Exception e) {e.printStackTrace();}}/*** Open the window.*/public void open() {Display display = Display.getDefault();createContents();shell.open();shell.layout();while (!shell.isDisposed()) {if (!display.readAndDispatch()) {display.sleep();}}}/*** 返回适合当前窗口尺寸完整显示图像的缩放比例,图像长宽都小于显示窗口时,则返回1* * @return*/private static float fitZoom(Point parentSize,Point childrenSize) {if (childrenSize.x < parentSize.x && childrenSize.y < parentSize.y)return 1f;if (childrenSize.x * parentSize.y < childrenSize.y * parentSize.x) {return ((float) parentSize.y) / (float)childrenSize.y;}return ((float) parentSize.x) / (float)childrenSize.x;}private static Rectangle fitBounds(Point parentSize,Point childrenSize) {float zoom = fitZoom(parentSize, childrenSize);childrenSize.x*=zoom;childrenSize.y*=zoom;return new Rectangle((parentSize.x-childrenSize.x)/2,(parentSize.y-childrenSize.y)/2,childrenSize.x,childrenSize.y);}/*** Create contents of the window.*/protected void createContents() {shell = new Shell();shell.setText("SWT Application");shell.setSize(569, 459);Rectangle[] rects = new Rectangle[]{new Rectangle(50,50,200,200),new Rectangle(350,100,150,250),new Rectangle(125,300,200,321)};ActiveRectContainer canvas;//Image image = SWTResourceManager.getImage("http://pic8.nipic.com/20100704/3525627_110847063052_2.jpg");Image image = SWTResourceManager.getImage("J:/workspace.neon/iadbui/src/image/3525627_110847063052_2.jpg");//Image image = SWTResourceManager.getImage(this.getClass(),"/image/3525627_110847063052_2.jpg");Point imgSize=new Point(image.getBounds().width,image.getBounds().height);canvas = new ActiveRectContainer(shell, image,rects,0);canvas.setBounds(fitBounds(shell.getSize(),imgSize));Button btnNewButton = new Button(canvas, SWT.NONE);btnNewButton.setBounds(189, 95, 80, 27);// 如果这里通过setLayoutData设置了btnNewButton的原始尺寸,btnNewButton也会随着窗口尺寸改变而自动缩放btnNewButton.setLayoutData(new Rectangle(189, 95, 80, 27));btnNewButton.setText("New Button");}
}

请注意:
对于一般的Control对象,如果没有通过setLayoutData方法设置原始的尺寸位置,则Layout对其无效,所以上面的测试代码中对btnNewButton调用了setLayoutData,指定了初始的位置和尺寸。这样它才能与父窗口同步缩放。如下图
这里写图片描述
如果注释掉 btnNewButton.setLayoutData(new Rectangle(189, 95, 80, 27));这一行,效果是这样的
这里写图片描述

参考

《org.eclipse.swt.widgets.Layout》
《 java SWT入门:自定义背景透明且可鼠标拖动改变尺寸和位置的Composite》

这篇关于java SWT:自定义布局(Layout)实现组件自动缩放显示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/837556

相关文章

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

基于Python实现自动化邮件发送系统的完整指南

《基于Python实现自动化邮件发送系统的完整指南》在现代软件开发和自动化流程中,邮件通知是一个常见且实用的功能,无论是用于发送报告、告警信息还是用户提醒,通过Python实现自动化的邮件发送功能都能... 目录一、前言:二、项目概述三、配置文件 `.env` 解析四、代码结构解析1. 导入模块2. 加载环

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

Java 正则表达式的使用实战案例

《Java正则表达式的使用实战案例》本文详细介绍了Java正则表达式的使用方法,涵盖语法细节、核心类方法、高级特性及实战案例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、正则表达式语法详解1. 基础字符匹配2. 字符类([]定义)3. 量词(控制匹配次数)4. 边

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹

Java中的stream流分组示例详解

《Java中的stream流分组示例详解》Java8StreamAPI以函数式风格处理集合数据,支持分组、统计等操作,可按单/多字段分组,使用String、Map.Entry或Java16record... 目录什么是stream流1、根据某个字段分组2、按多个字段分组(组合分组)1、方法一:使用 Stri

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装