前三节大概描述了swing的绘制实现,现在补充一下Swing里十分重要的一个绘制概念:revalidate。
按Swing的想法,将开发java界面的开发人员分为两类:首先是开发独立的组件,这些组件与具体应用无关,Swing自身也提供了一套,开发者也可以开发自己的组件,这些人是组件开发者角色;然后是为某应用程序构建GUI,则是使用那些组件类建立组件实例,并且通过建立容器关系来实现界面需求,这些是GUI开发者角色。从这个清晰的划分出发可以体会Swing面向OO开发的精神。组件开发者开发的每一个组件面向所有应用,是一个高度抽象,打包封装好的类,因此高度复用;而GUI开发者的开发过程则是根据需要继续OO---,首先整个应用界面职责被封装成一个类,该类的实例的生命周期就代表了整个应用界面的生命周期,再从应用领域角度进行界面职责划分,利用容器概念,快速通过聚合,定制组件实例打造出承担各个细分应用领域界面交互职责的“面板类/页面类”,最后应用界面类实例将在整个生命周期内不断耦合托付各类“面板类/页面类”实例(一般通过“页面接口”实现动态性)来完成全部界面交互职责。
反过来说,Swing也正是为了这样的oo实践需要而诞生的。在以上描述的gui构建中,最频繁的就是通过容器组装出特定面板组件来。那么如何定制子组件在容器面板上的分布呢?
Swing的想法是Container中可以setLayout(LayoutManager mgr),该mgr给出了一种布局方式,比如按五位图布局还是表格布局;而子组件加入容器时可以给出针对此容器布局方式的各自的布局信息, Container.add(Component comp, Object constraints);最后当绘制该容器的子组件时根据这些布局信息计算出各组件合适的大小位置等信息进行绘制即可。不过在Swing中,为了提高性能,不是每次绘制时都重新根据布局信息计算该如何绘制子组件,而是将在一次处理过程中把根据布局信息计算的绘制要求存储在子组件和容器中,比如调整后的子组件的位置,容器的合适大小等,以后绘制时直接绘制;除非此后发生布局变化,比如容器remove掉一个子组件,将要导致一次重新计算布局信息的处理过程;由于Swing中的组件关系是一层包一层,某一层的变化可能导致整个树都需要重新计算,这就和repaint的场景类似了,也因此,Swing将采取类似repaint的提交请求等待处理的模式,就是传说中的revalidate。下面看jre1.7中具体的实现:
Public class JComponent{
public void revalidate() {
if (getParent() == null) {
return;
}
if (SwingUtilities.isEventDispatchThread()) {
invalidate();//使该组件布局失效
RepaintManager.currentManager(this).addInvalidComponent(this);//向RM请求一次重布局
}
else {//如果不是在EDT中调用需要调整到EDT中。
// To avoid a flood of Runnables when constructing GUIs off
// the EDT, a flag is maintained as to whether or not
// a Runnable has been scheduled.
synchronized(this) {
if (getFlag(REVALIDATE_RUNNABLE_SCHEDULED)) {
return;
}
setFlag(REVALIDATE_RUNNABLE_SCHEDULED, true);
}
Runnable callRevalidate = new Runnable() {
public void run() {
synchronized(JComponent.this) {
setFlag(REVALIDATE_RUNNABLE_SCHEDULED, false);
}
revalidate();
}
};
SwingUtilities.invokeLater(callRevalidate);
}
}
Public Class Container{
public void invalidate() {
LayoutManager layoutMgr = this.layoutMgr;
if (layoutMgr instanceof LayoutManager2) {
LayoutManager2 lm = (LayoutManager2) layoutMgr;
lm.invalidateLayout(this);//通知LayoutManager应该丢弃可能缓存的当前布局计算结果信息
}
super.invalidate();
}
}
Public Class Component{
public void invalidate() {
synchronized (getTreeLock()) {
valid = false;//每个组件都有这么一个域,记录着当前布局信息是否有效
if (!isPreferredSizeSet()) {//直接清空prefSize
prefSize = null;
}
if (!isMinimumSizeSet()) {//直接清空minSize
minSize = null;
}
if (!isMaximumSizeSet()) {//直接清空maxSize
maxSize = null;
}
if (parent != null && parent.valid) {//递归向上
parent.invalidate();
}
}
}
}
Public Class RepaintManager{
public synchronized void addInvalidComponent(JComponent invalidComponent)
{
Component validateRoot = null;
/* Find the first JComponent ancestor of this component whose
* isValidateRoot() method returns true.
*/
// isValidateRoot标志着该组件可以作为展开布局计算的根组件。
for(Component c = invalidComponent; c != null; c = c.getParent()) {
if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
return;//对于这两种特殊情况将撤销本次重新计算布局的请求
}
if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {//JRootPane isValidateRoot
validateRoot = c;
break;
}
}
/* There's no validateRoot to apply validate to, so we're done.
*/
if (validateRoot == null) {//一直没有找到ValidateRoot则撤销请求
return;
}
/* If the validateRoot and all of its ancestors aren't visible
* then we don't do anything. While we're walking up the tree
* we find the root Window or Applet.
*/
//如果validateRoot及之上的祖宗无peer或不可见则撤销请求
Component root = null;
for(Component c = validateRoot; c != null; c = c.getParent()) {
if (!c.isVisible() || (c.getPeer() == null)) {
return;
}
if ((c instanceof Window) || (c instanceof Applet)) {//找到对应的顶层窗口
root = c;
break;
}
}
if (root == null) {//一直没有找到顶层窗口则请求无效
return;
}
/* Lazily create the invalidateComponents vector and add the
* validateRoot if it's not there already. If this validateRoot
* is already in the vector, we're done.
*/
if (invalidComponents == null) {//延迟创建该单例
invalidComponents = new ArrayList();
}
else {
int n = invalidComponents.size();
for(int i = 0; i < n; i++) {
if(validateRoot == invalidComponents.get(i)) {//如果当前要开展布局计算的validateRoot已经注册在记录列表中则忽略本次请求---请求合并。
return;
}
}
}
invalidComponents.add(validateRoot);//加入该请求
// Queue a Runnable to invoke paintDirtyRegions and
// validateInvalidComponents.
scheduleProcessingRunnable();//将导致进行布局计算的调度,该方法在前面的分节已分析过。
}
//前面纪要分析中未分析的ProcessingRunnable.run中将执行的方法,这次revalidate可能加入的//invalidComponents将被validate
public void validateInvalidComponents() {
java.util.List ic;
synchronized(this) {//并发的合理交换
if(invalidComponents == null) {
return;
}
ic = invalidComponents;
invalidComponents = null;
}
int n = ic.size();
for(int i = 0; i < n; i++) {//对每一个validateRoot进行validate
ic.get(i).validate();
}
}
}
Public Class Container{
public void validate() {
/* Avoid grabbing lock unless really necessary. */
if (!valid) {//如果本组件valid==false
boolean updateCur = false;
synchronized (getTreeLock()) {
if (!valid && peer != null) {
ContainerPeer p = null;
if (peer instanceof ContainerPeer) {
p = (ContainerPeer) peer;
}
if (p != null) {
System.out.println("Window show>>p.beginValidate");
p.beginValidate();//通知开始validate—轻量级peer不做任何操作
}
validateTree();//实际布局计算处理过程入口
valid = true;//置为有效
if (p != null) {
p.endValidate();//通知结束validate—轻量级peer不做任何操作
System.out.println("Window show>>p.endValidate");
updateCur = isVisible();//如果是可视状态,更新Cursor
}
}
}
if (updateCur) {
System.out.println("Window show>>updateCur");
updateCursorImmediately();//更新Cursor
}
}
}
protected void validateTree() {
if (!valid) {//如果本组件valid==false
if (peer instanceof ContainerPeer) {
((ContainerPeer)peer).beginLayout();
}
doLayout();//实际布局计算处理过程入口
Component component[] = this.component;
for (int i = 0 ; i < ncomponents ; ++i) {//往下对valid==false的子组件遍历validate
Component comp = component[i];
if ( (comp instanceof Container)//如果是非窗口的容器,且valid==false,直接进行validateTree处理
&& !(comp instanceof Window)
&& !comp.valid) {
((Container)comp).validateTree();
} else {//否则需要进入validate递归处理
comp.validate();
}
}
if (peer instanceof ContainerPeer) {
((ContainerPeer)peer).endLayout();
}
}
valid = true;
}
public void doLayout() {
layout();//实际布局计算处理过程入口
}
/**
* @deprecated As of JDK version 1.1,
* replaced by doLayout()
.
*/
@Deprecated
public void layout() {
LayoutManager layoutMgr = this.layoutMgr;
if (layoutMgr != null) {
layoutMgr.layoutContainer(this); // LayoutManager负责进行布局计算
}
}
//现在以BorderLayoutManager(五位图布局)为例
public class BorderLayout
public void layoutContainer(Container target) {
synchronized (target.getTreeLock()) {
Insets insets = target.getInsets();
int top = insets.top;
int bottom = target.height - insets.bottom;
int left = insets.left;
int right = target.width - insets.right;
boolean ltr = target.getComponentOrientation().isLeftToRight();
Component c = null;
if ((c=getChild(NORTH,ltr)) != null) {
c.setSize(right - left, c.height);//对子组件重新设置大小
Dimension d = c.getPreferredSize();
c.setBounds(left, top, right - left, d.height); //对子组件重新设置边界
top += d.height + vgap;
}//调整大小和边界将引起repaint
if ((c=getChild(SOUTH,ltr)) != null) {
c.setSize(right - left, c.height);
Dimension d = c.getPreferredSize();
c.setBounds(left, bottom - d.height, right - left, d.height);
bottom -= d.height + vgap;
}
if ((c=getChild(EAST,ltr)) != null) {
c.setSize(c.width, bottom - top);
Dimension d = c.getPreferredSize();
c.setBounds(right - d.width, top, d.width, bottom - top);
right -= d.width + hgap;
}
if ((c=getChild(WEST,ltr)) != null) {
c.setSize(c.width, bottom - top);
Dimension d = c.getPreferredSize();
c.setBounds(left, top, d.width, bottom - top);
left += d.width + hgap;
}
if ((c=getChild(CENTER,ltr)) != null) {
c.setBounds(left, top, right - left, bottom - top);
}
}
}
//子组件信息是在加入容器是被加入到BorderLayoutManager中去的
Public Class Container{
protected void addImpl(Component comp, Object constraints, int index) {//追加子组件
~~~
if (layoutMgr != null) {//如果容器具有LayoutManager
if (layoutMgr instanceof LayoutManager2) {//BorderLayoutManager是
((LayoutManager2)layoutMgr).addLayoutComponent(comp, constraints);// BorderLayoutManager会在此通知中加入子组件
} else if (constraints instanceof String) {
layoutMgr.addLayoutComponent((String)constraints, comp);
}
}
}
Swing的revalidate简单说就是首先置为自身及所有祖宗无效,然后登记计算布局请求,该请求可能因上一次提交的同样的validateRoot相同而被合并;处理请求将从validateRoot开始用布局管理器计算布局,计算结果体现在对子组件的大小范围等信息的调整,这些调整将引起repaint提交绘制请求,然后再次递归往下对所有标记无效的子组件处理。
阅读(1370) | 评论(1) | 转发(0) |