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

相关文章

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu

创建Java keystore文件的完整指南及详细步骤

《创建Javakeystore文件的完整指南及详细步骤》本文详解Java中keystore的创建与配置,涵盖私钥管理、自签名与CA证书生成、SSL/TLS应用,强调安全存储及验证机制,确保通信加密和... 目录1. 秘密键(私钥)的理解与管理私钥的定义与重要性私钥的管理策略私钥的生成与存储2. 证书的创建与

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Java中的数组与集合基本用法详解

《Java中的数组与集合基本用法详解》本文介绍了Java数组和集合框架的基础知识,数组部分涵盖了一维、二维及多维数组的声明、初始化、访问与遍历方法,以及Arrays类的常用操作,对Java数组与集合相... 目录一、Java数组基础1.1 数组结构概述1.2 一维数组1.2.1 声明与初始化1.2.2 访问