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

相关文章

微信公众号脚本-获取热搜自动新建草稿并发布文章

《微信公众号脚本-获取热搜自动新建草稿并发布文章》本来想写一个自动化发布微信公众号的小绿书的脚本,但是微信公众号官网没有小绿书的接口,那就写一个获取热搜微信普通文章的脚本吧,:本文主要介绍微信公众... 目录介绍思路前期准备环境要求获取接口token获取热搜获取热搜数据下载热搜图片给图片加上标题文字上传图片

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J