Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 702|回复: 0

[Swing学习]换一种方式弹出Swing Popup

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-11-4 23:56:00 | 显示全部楼层 |阅读模式
    Swing中Popup结构是Swing所有弹出窗口的基础类。Swing的弹出式窗口包括菜单、JComboBox下拉框、JToolTip等
      ,它们都使用PopupFactory.getPopup来获得Popup窗口。目前Swing弹出窗口总是瞬时显示,没有像滚动弹出和淡入淡出等动画效果
      。但在一些操作系统上,弹出窗口可以被配置为动画弹出模式的。比如在Windows中,通过桌面属性->外观->效
      果的对话框,可以为菜单和工具设置位滚动效果。如下图所示:
      

      


         如果你选择这种效果,Windows桌面应用程序的菜单和提示都会滚动弹出。但即使在Windows启动了这一效果,Swing
      仍然使用瞬时弹出模式,对于追求完美的人来说总让人觉得不满意。

         幸运的是Swing的PopupFactory工厂提供了使用自定义PopupFactory的方法,你可以编写自己的PopupFactory类,来实现
      这种滚动弹出窗口,并在程序开始调用PopupFactory.setSharedInstance(PopupFactory factory)来替换Swing缺省的
      
    PopupFactory,从而使得swing的所有弹出窗口都具有这种效果。
         如何编写PopupFactory呢?首先要了解Swing应用程序的Popup窗口类型。为提高效率,Swing会尽可能地使用轻量级的弹出窗口,这在前面的一篇文章《如何混排Swing和AWT组件》中提到过。Swing弹出窗口分为三种类型:轻量级弹出窗口、中量级弹出窗口及重量级弹出窗口。下面简要叙述一下它们适用范围。
         轻量级弹出是窗口是继承自JComponent的,它们实际上是添加到顶层容器的JLayeredPane浮动层的普通组件。由于被添加到的层高于ContentPane的层,因此在显示时会覆盖其他Swing组件。这种轻量级组件只能在弹出窗口边界不超出顶层容器的边界时使用,否则显示的内容由于剪裁作用就会不完全。
         重量级组件是使用JWindow等重量级顶层容器实现的弹出时窗口。这样做的目的是为了实现边界超出顶层容器边界的弹出窗口。比如多级长菜单经常会超出JFrame的边界,这时它所使用的弹出式窗口就是重量级弹出窗口。
         中量级组件是使用AWT/Canvas实现的介于轻量级组件和重量级组件之间的弹出窗口。这类窗口的边界同轻量级组件一样不超出顶层容器的边界,但由于某些原因,比如Swing和AWT混排的窗口,由于AWT组件的Z-order通常要高于Swing组件所依赖的顶层AWT容器组件的Z-order,所以Swing的可扩展组件如菜单就可能被AWT组件所遮盖,这时就要求使用AWT组件来实现弹出窗口。这种情况一般使用AWT/Canvas作为弹出窗口的实现。
         因此轻量级弹出窗口的内容组件通常就是弹出窗口本身,而重量级弹出窗口的通常是内容组件的顶层容器JWindow对象,而中量级组件通常是它的父容器AWT/Canvas对象。另外JToolTip是个特殊的依赖于特定组件的组件,当它弹出窗口是轻/中量级时,它的弹出窗口是它的父容器。
         明白这些原理后,我们只需要继承Swing的PopupFactory,重载它的getPopup方法,提供自己的Popup。自定义的Popup实现实际上是一个Popup的代理类,该代理类将Popup的显示和关闭方法重载,在显示时启动动画,进行滚动,在关闭前,结束可能的滚动时钟。这个类是这样定义的:
         /**
          * 这个类是一个Popup代理,将真实Popup的显示过程动画弹出
          */
         class PopupProxy extends Popup implements ActionListener{
             //一些常量
             private static final int ANIMATION_FRAME_INTERVAL=10;
             private static final int ANIMATION_FRAMES=10;
             //被代理的弹出式窗口,这个弹出式窗口是从缺省工厂那儿获得的。
             private Popup popupProxy;
             //当前组件
             private Component topComponent;
            
             //弹出式窗口最终尺寸
             private Dimension fullSize;
             //动画时钟
             private Timer timer;
             //动画的当前帧
             private int frameIndex;
            
             public PopupProxy(Popup popup, Component component){
                 popupProxy=popup;
                 topComponent=component;
             }
             /**
              * 覆盖show方法启动动画线程
              */
             @Override
             public void show() {
                 //代理窗口显示
                 popupProxy.show();
                 //获取显示后窗口的最终大小。
                 fullSize=topComponent.getSize();
                 //设置窗口的初始尺寸
                 topComponent.setSize(
                         horizontalExtending?0:fullSize.width,
                         verticalExtending?0:fullSize.height);
                 //初始化为第一帧
                 frameIndex=1;
                 //启动动画时钟
                 timer=new Timer(ANIMATION_FRAME_INTERVAL, this);
                 timer.start();
             }
             /**
              * 重载hide,关闭可能的时钟
              */
             @Override
             public void hide() {
                 if(timer!=null&&timer.isRunning()){
                     //关闭时钟
                     timer.stop();
                     timer=null;
                 }
                 //代理弹出窗口关闭
                 popupProxy.hide();
             }
             //动画时钟事件的处理,其中一帧
             public void actionPerformed(ActionEvent e) {
                 //设置当前帧弹出窗口组件的尺寸
                 topComponent.setSize(
                         horizontalExtending?
                             fullSize.width*frameIndex/ANIMATION_FRAMES:
                             fullSize.width,
                         verticalExtending?
                             fullSize.height*frameIndex/ANIMATION_FRAMES:
                             fullSize.height);
                
                 if(frameIndex==ANIMATION_FRAMES){
                     //最后一帧,关闭时钟
                     timer.stop();
                     timer=null;
                 }else
                     //前进一帧
                     frameIndex++;
             }
         }
         其构造函数从PopupFactory获取一个Popup及顶层弹出窗口组件。显示时,启动时钟,设置最小尺寸。每一动画帧计算当前尺寸并更新弹出窗口的大小。最后一帧关闭时钟。在关闭弹出窗口时,关闭有可能正在进行动画滚动的时钟。
         自定义的ScrollablePopupFactory是这样定义的:
    public class ScrollablePopupFactory extends PopupFactory{
         //是否横向滚动,缺省不
         private boolean horizontalExtending;
         //是否垂直滚动,缺省不
         private boolean verticalExtending;
         /**
          * Creates a new instance of ScrollablePopupFactory
          */
         public ScrollablePopupFactory(){
         }
         /**
          * @param h 是否横向滚动
          * @param v 是否垂直滚动
          */
         public ScrollablePopupFactory(boolean h, boolean v) {
             horizontalExtending=h;
             verticalExtending=v;
         }
         /**
          * 覆盖PopupFactory.getPopup方法提供自定义Popup代理
          *
          */
         @Override
         public Popup getPopup(Component owner, Component contents, int x, int y) throws IllegalArgumentException {
             //获取缺省的Popup
             Popup popup = super.getPopup(owner, contents, x, y);
             //如果纵横都不滚动,直接使用缺省的弹出式窗口
             if(!(horizontalExtending||verticalExtending))
                 return popup;
             //目前没有好办法判断弹出窗口是何种类型,只好用类名字来判断
             String name=popup.getClass().getName();
             if(name.equals("javax.swing.PopupFactory$HeavyWeightPopup")){
                 //重量级的弹出窗口,其顶层容器为JWindow
                 return new PopupProxy(
                         popup,
                         SwingUtilities.getWindowAncestor(contents));
             }else{
                 //轻量级的弹出窗口
                 if(contents instanceof JToolTip)
                     //如果组件是JToolTip,则其父亲容器就是顶层容器
                     return new PopupProxy(
                             popup,
                             contents.getParent());
                 else
                     //其他弹出式窗口则组件本身就是顶层容器
                     return new PopupProxy(
                             popup,
                             contents);
             }
         }
         /**
          * 这个类是一个Popup代理,将真实Popup的显示过程动画弹出
          */
         class PopupProxy extends Popup implements ActionListener{
           ......
         }
    }
         ScrollablePopupFactory.getPopup使用父类方法的getPopup获取Popup对象,并根据其类型计算出不同弹出式窗口的顶层组件,然后将Popup该顶层组件封装成PopupProxy对象返回给调用者。
         如何使用这个ScrollablePopupFactory?只需要在程序开始时设置一下就可以了:
         public static void main(String args[]) {
             try {
                 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
             } catch (Exception ex) {}
             //设置自定义的PopupFactory,注意纵横都扩展,可以改变布尔值只横向或纵向扩展,或者没有动画
             PopupFactory.setSharedInstance(new ScrollablePopupFactory(true, true));
             EventQueue.invokeLater(new Runnable() {
                 public void run() {
                     new ScrollablePopupDemo().setVisible(true);
                 }
             });
         }
         下面是演示程序的抓图,菜单正在滚动展开,下面有两个checkbox指定滚动展开方式。另外可以拖动窗口尺寸,以便让菜单、下拉框和ToolTip的窗口边界超过外层窗口的边界,测试重量级弹出窗口的效果。

         同样原理可以实现弹出式窗口的淡入淡出动画效果。该演示的源码需要JDK1.6编译,其主类是dyno.swing.beans.test.ScrollablePopupDemo,下载后导入NetBeans进行编译运行。
         更新:针对有的网友提出的JInternalFrame内显示菜单先画顶层容器,再在该容器上形成动画效果的问题,我稍微做了一下修改。新版本的不再有这种问题。



      
      
       
       

         
       

         
       
      
    复制代码

    源码下载:http://file.javaxxz.com/2014/11/4/235600015.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-25 13:24 , Processed in 0.374254 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表