首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Swing DayDayUP之三:Swing组件层次结构引见

2012-12-28 
Swing DayDayUP之三:Swing组件层次结构介绍Swing程序要呈现出来,肯定要将组件放到JFrame,JDialog,JApplet,

Swing DayDayUP之三:Swing组件层次结构介绍
Swing程序要呈现出来,肯定要将组件放到JFrame,JDialog,JApplet,JWindow这几个容器组件中,这些顶层容器组件,有一个共同的结构,如上图所示,都有一个JRootPane面板来承载着所有要显示的组件。JRootPane又有以下部分组成:
1、GlassPane
2、JLayeredPane
(1)ContentPane
(2)JMenuBar
其中JRootPanel中的RootLayout对以上几部分进行布局,关于这点可以参考Java的API文档和java源码,里面有详细的说明。
这里想要说的一点是,JRootPane并不是只能放在这些顶层容器中的,你也可以放在一般的组件,比如JPanel中,这样Panel就有了层次结构,可以再其不同的层次上放置组件,这个后边还会再讲一下。
        写这篇文章,并不是为了讲Swing的这个架构,主要是想通过讲述这个架构,给大家介绍些开发过程中用的技巧。
GlassPanel这个可能大家都了解了,比如,可以模拟Eclipse中Tab页签的拖拽效果,当处理一些比较费时的操作,比如加载数据时,可以用来遮罩面板,一方面可以防止用户重复操作,另一方面可以用来显示进度。不过有一点限制就是每个顶层容器中GlassPanel只能有一个(其实像上面提到的,如果一个面板中的组件都有一个JRootPane来承载组件,其实在一个程序中GlassPane也是可以有多个的,通过这个,GlassPanel和JLayeredPane几乎可以达到相同的效果)。
        JLayeredPane有了深度的概念,一般的Layout在JLayeredPane是不适用的。一个Integer来标示组件在JLayeredPane中的深度,这个值越大,就会显示在越上面。JLayeredPane还有一个同层的上下关系,这个可以参考API或者源代码。重要的一点是JLayeredPane默认的Layout是null,所以,放到JLayeredPane上的组件,必须要通过setBounds的形式来设置大小才会显示出来,否则是看不到的,这点尤其要注意。JLayeredPane的层次结构能实现非常多的特效,我甚至认为,JLayeredPane 是Swing结构中,最精妙的设计之一,通过JLayeredPane我们可以实现文本的验证提示功能,举个例子,有一个文本框,用来输入电话号码,有时候可能用户会输入些字母,甚至是汉字,当用户切换到下一个面板或者最后提交验证以后,你可能希望给出一些提示来标明这些输入非法,这时候,就可以用到JLayeredPane了,你可以将非法输入的组件,传递给一个Panel,这个Panel在非法的组件旁边绘制一个小图标,然后让这个Panel显示在主界面的上面(后续会把相关代码发出)。还有另外一个用途就是使用JInternalFrame做控制面板,可以通过JLayeredPane将一个JInternalFrame悬浮到一个面板中,既不会占用程序的空间,又不会像dialog那样,必须要弹出来才可以。

         附一个通过LayeredPane来显示加载进度的例子,使用方法很简单只需要将你的组件通过我的adapter封装下,然后再放到你的程序中即可MaskAdapter maskPanel = MaskAdapter.getMaskpanel(panel, false);。



我遇到一个问题,有高手给解答一下,先说声谢谢.
程序如下:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class javademo extends JFrame{   
    public javademo(){
    Container con=this.getContentPane();
    con.setLayout(null);               
    paintpanel pp=new paintpanel(Color.red);
    con.add(pp);
    setSize(210,200);
    }   
    public static void main(String args[]){
        javademo jd=new javademo();
        jd.setVisible(true);
    }

}

class paintpanel extends JPanel{
   Color c;
   public paintpanel(Color color){
        c=color;
   }
   public void paintComponent(Graphics g){
       super.paintComponent(g);     //这一句到底有什么做用,我一直不知道.书上说,必须有
       g.setColor(c);
       g.fillRect(0,0,80,40);
   }
}

我希望绘图的位置固定,其他地方好放其他控件.但是无显示.请指教


作者:happyboy2007      发表时间:2008-9-10 14:40:00
 第1楼 

如果父类容器为空布局,则必须为子组件指定大小

con.setLayout(null);               
paintpanel pp=new paintpanel(Color.red);
pp.setBounds(0, 0, 202, 166); //必须指定面板的大小
con.add(pp);


作者:benb      发表时间:2008-9-12 14:20:00
 第2楼 

初始问题:super.paintComponent到底做了什么,以至于子类覆盖时必须调用它?

不妨从源码里追溯一下
首先查看JPanel。其定义public class JPanel extend JComponent
找了一下,发现JPanel里没有paintComponent方法
继续找其父类,在JComponent里找到了paintComponent,其代码片段如下

protected void paintComponent(Graphics g) {
        if (ui != null) {
            Graphics scratchGraphics = SwingGraphics.createSwingGraphics(g);
            try {
                ui.update(scratchGraphics, this);
            }
            finally {
                scratchGraphics.dispose();
            }
        }
}

新的问题产生:
为什么要创建一个scratchGraphics?
ui.update更新了什么?
为什么要dispose一下?
我们逐个来解决。

首先,追踪createSwingGraphics方法
public static Graphics createSwingGraphics(Graphics g) {
        if (g == null) {
            Thread.dumpStack();
            return null;
        }
        return g.create();
}
发现只是简单的调用了一下Graphics接口的create方法,建立了一个新的图对象。

接着,看ui.update
public void update(Graphics g, JComponent c) {
    if (c.isOpaque()) {
        g.setColor(c.getBackground());
        g.fillRect(0, 0, c.getWidth(),c.getHeight());
    }
    paint(g, c);
}
利用上一步所创建的图对象,把组件的背景色画了出来。

最后一个dispose,查看Graphics接口的源码,根据Graphics对dispose方法的说明,此方法是丢弃Graphics,并释放其所占内存资源的。看来这句话的作用就是把用来画背景色的图对象抛弃了。


根据以上追踪,我们发现paintComponenet的作用是给组件画上背景色。
如果不调用此方法,我们之前对JPanel设置的背景色等属性将不会被展现。
而且根据源代码,我们发现涂背景时机也是有讲究的。

如果此方法在子类的实现中最先被调用,背景就处于最底下的一层,子类其他利用g进行的绘图将在有一个背景的基础下进行。
如果此方法没有最先被调用,在此方法前所做的绘图动作都会被画出来的背景overwrite。


结论:paintComponent方法应该要被所有子类调用,而且是在最先调用。

package com.gui;

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.event.*;

/**
* We have to provide our own glass pane so that it can paint a loading dialog
* and then the user can see the progress but can't operation the GUI, it's a
* transparent pane so the below contents is visible.
*
* Also please refer to articles by Sun - How to Use Root Panes: {@link http
* ://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.ht ml}
*
* @author Jacky Liu
* @version 1.0 2006-08
*/
public class LoadingGlassPanel extends JPanel {
private static final long serialVersionUID = 1L;
/**
* A label displays status text and loading icon.
*/
private JLabel statusLabel = new JLabel("Reading data, please wait...");

public LoadingGlassPanel() {
try {
statusLabel.setIcon(new ImageIcon(getClass().getResource("/loading.gif")));
} catch (RuntimeException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
statusLabel.setHorizontalAlignment(JLabel.CENTER);
// Must add a mouse listener, otherwise the event will not be
// captured
this.addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(MouseEvent e) {
}
});
this.setLayout(new BorderLayout());
this.add(statusLabel);
// Transparent
setOpaque(false);
}

/**
* Set the text to be displayed on the glass pane.
*
* @param text
*/
public void setStatusText(final String text) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
statusLabel.setText(text);
}
});
}

/**
* Install this to the jframe as glass pane.
*
* @param frame
*/
public void installAsGlassPane(JFrame frame) {
frame.setGlassPane(this);
}

/**
* A small demo code of how to use this glasspane.
*
* @param args
*/
public static void main(String[] args) {
JFrame frame = new JFrame("Test GlassPane");
final LoadingGlassPanel glassPane = new LoadingGlassPanel();
glassPane.installAsGlassPane(frame);
JButton button = new JButton("Test Query");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Call in new thread to allow the UI to update
Thread th = new Thread() {
public void run() {
glassPane.setVisible(true);
glassPane.setCursor(new Cursor(Cursor.WAIT_CURSOR));
// TODO Long time operation here
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
glassPane.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
glassPane.setVisible(false);
}
};
th.start();
}
});
frame.getContentPane().setLayout(new FlowLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(200, 200);
frame.setVisible(true);
}
}

热点排行