Swing编程方面步骤之三JFrame、JPanel、paint与paintComponent的区别

时间:2023-01-28 00:03:37

参考一 关于JFrame和JPanel问题

参考二 JPanel与JFrame有什么关系 与不同的

参考三Swing之paint与paintComponent的区别

参考四 paintComponent(Graphics g)方法什么时候被调用?

参考五 super.paintComponent(g);的问题


接着很久之前的博客 Swing编程方面步骤


1.JFrame和JPanel区别

包含Swing组件的程序必须包含一个*容器。但是Swing组件不可以直接加入到*容器中,而JFrame是*容器。通常是把Swing组件先加入JFrame, JDialog, and JApplet三个顶层容器类中的一个,然后再放入JFrame中。之前相关Swing的博客例子的类都直接继承JFrame,下面的例子的类先是继承JPanel,然后在类的构造函数中添加JFrame。

2.add()是任何JComponent都可以调用的,JPanel也可以,但是JFrame不可以。

对JFrame添加组件有两种方式,常用方式一。即通过 getContentPane ()方法获得JFrame的内容面板,再对其加入组件,如frame. getContentPane ().add(childComponent)。在下篇的五子棋单机版博客中,就是把棋盘对象加入到JFrame的内容面板中,如 Container contentPane=getContentPane(); contentPane.add(chessBoard);

3.java里面的paint()方法不是由用户调用的,而是由虚拟机系统调用的,即和下面的paintComponent()是一样的。

4.JComponent类有一个方法paintComponent(Graphics g),当主方法并没有调用这个类的方法,而这个方法里的代码仍被执行。什么时候paintComponent(Graphics g)被调用?

当java认为需要重新绘制组件的时候由java调用。例如在程序中repaint();或者程序窗口最小化,然后恢复;或者程序窗口被遮挡,又显现的时候。

在下篇的五子棋单机版博客中,单步执行主窗体程序,setVisible(true) ---》super.setVisible(b)--》show(b)--》show()--》super.show(),这个方法如下:

public void show() {
        if (!visible) {
            synchronized (getTreeLock()) {
                visible = true;
                mixOnShowing();
                ComponentPeer peer = this.peer;
                if (peer != null) {
                    peer.show();
                    createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED,
                                          this, parent,
                                          HierarchyEvent.SHOWING_CHANGED,
                                          Toolkit.enabledOnToolkit(AWTEvent.HIERARCHY_EVENT_MASK));
                    if (peer instanceof LightweightPeer) {
                        repaint();
                    }
                    updateCursorImmediately();
                }

                if (componentListener != null ||
                    (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0 ||
                    Toolkit.enabledOnToolkit(AWTEvent.COMPONENT_EVENT_MASK)) {
                    ComponentEvent e = new ComponentEvent(this,
                                                          ComponentEvent.COMPONENT_SHOWN);
                    Toolkit.getEventQueue().postEvent(e);
                }
            }
            Container parent = this.parent;
            if (parent != null) {
                parent.invalidate();
            }
        }
    }
当在这个方法中单步执行到红字部分 peer.show(),继续跟进peer.show()方法,然后出来父窗体。再返回到void show()方法继续单步往下执行到 红字部分

  if (componentListener != null || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0 ||  Toolkit.enabledOnToolkit(AWTEvent.COMPONENT_EVENT_MASK)) {
                    ComponentEvent e = new ComponentEvent(this,
                                                          ComponentEvent.COMPONENT_SHOWN);
                    Toolkit.getEventQueue().postEvent(e);
                }
            }

Container parent = this.parent,由于componentListener != null,会调用了我们在棋盘类中的重写的paintComponent方法,最后出现棋盘。


5.Swing中paint与paintComponent的区别

当Swing中的paint方法被调用时,paintComponent、paintBorder、 paintChildren 这三个方法也会被按顺序调用,之所以要按这个顺序调用是为了保证子组件能正确地显示在目前这个组件之上。

paintComponent就是本身这个容器自己画出自己组件的方法,且在JComponent中的方法paintComponent()还是保护类型的,即我们平常并不用管这个方法。只在我们需要改变绘制组件动作的时候,类只要先继承paintComponent(一般是JFrame),然后重写这个方法即可。

总之:

(1)如果只是为了在Swing中改变组件样式,重写paintComponent就可以了,其他paintBorder和paintChildren默认。如果还要保留容器中的原本组件就别忘了调用super.paintComponent(g)。

(2)如果要改写paint方法来改变本身这个容器的组件,那么也别忘了要调用super.paint(g),不然出来的东西是不包含原组件、原边框和子组件的。此处有例子如下:


package TestPanel;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class MyDrawPanel extends JPanel {//JPanel是普通容器

	public static void main(String[] args) {
		new MyDrawPanel();
	}
	
	public MyDrawPanel(){
		//创建组件 
		JFrame frame = new JFrame();//JFrame是*容器,包含Swing组件的程序必须包含一个*容器
		JLabel label = new JLabel("aaa");
		
		 //把JPanel添加组件 。因为Swing组件不可以直接加入到*容器中,通常是 JFrame, JDialog, and JApplet三个顶层容器类。
		this.add(label);
		
		//把JPanel及JPanel上的其他组件都加入顶层容器JFrame  
		frame.getContentPane().add(this);
		
		//设置面板和框架的基本属性
		this.setBackground(Color.blue);
		
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(300, 300);
		frame.setVisible(true);
	}
	
	public void paint(Graphics g) {
		super.paint(g);//如果注释掉super.paint(g),那么就不显示父类JPanel容器中的原本组件,只能显示出方块了。
		g.setColor(Color.orange);
		g.fillRect(20, 50, 100, 100);
	}
}


6.当我们重写JPanel这个父类的paintComponent()方法,来实现自己的绘制方案时,为啥要调用super.paintComponent()?

因为类是从JPanel继承而来的,相当于这个类本身就保存了JPanel的一个引用。当重写paintComponent()方法时,为了确保在此期间,父类JPanel能保留容器中的原本组件
,要先把自己的paintComponent()方法运行完,即调用super.paintComponent(),然后再运行我们自己的paintComponent()。否则就有可能出现的情况是,父类JPanel自己还没有绘制完,程序已经开始绘制子类的了。