理解SWT布局(2)

FormLayout   * 2.0新特性*

    FormLayout通过为小窗口部件创建四边的Form附加值(attachment)来进行工作,并且把这些Form附加值存储在布局数据中。一个附加值让一个小窗口部件指定的一边粘贴(attach)到父Composite的一个位置或者这个布局中的另一个小窗口部件上。这为布局提供了极大的便利,并且这也允许你指定布局中单个小窗口部件的放置。

FormLayout的可配置域

MarginWidth, MarginHeight
     FormLayout中的 margin域类似于GridLayout中的相同域。左边和右边边距(margin)被定义成 marginWidth,而顶部和底部的边距被定义成 marginHeight。边距也能根据每个小窗口部件的附加值来定义。FormLayout边距在默认情况下为0。
  
     为了设置这些边距,我们创建一个FromLayout,然后设置它的边距域。下面这段代码会在父Composite的四个边都设置5个象素的边距。
 
Display display = new Display ();
Shell shell = new Shell (display);
FormLayout layout= new FormLayout ();
layout.marginHeight = 5;
layout.marginWidth = 5;
shell.setLayout (layout);
 
FormData对象域
      FormData对象用于指定FormLayout中的每一个小窗口部件怎么样放置。每一个FormData对象定义了小窗口部件四个边的附加值值。这些附加值决定在哪里定位小窗口部件的四个边。你可以用 setLayoutData方法来设定一个小窗口部件的FormData对象。例如:
 
  Button button1 = new Button(shell, SWT.PUSH);
  button1.setText("B1");
  button1.setLayoutData(new FormData());
 
     但是,这段代码创建了一个没有附加值的FormData对象。在这个例子中,定义了默认的附加值值,这没有体现出FormLayout的目的和效用。默认的附加值值让小窗口部件粘贴到父Composite的左边和顶部。如果FormLayout中的每一个小窗口部件都使用了默认的附加值值,它们就会全部被重叠的放置在父Composite的左上角。

Left, Right, Top和Bottom
     FormLayout中的 left, right, topbottom域分别指定了与小窗口部件的左边,右边,顶部和底部相关联的Form附加值对象。这些域被象下面的例中一样设定:
 
FormData formData = new FormData();
formData.top = new FormAttachment (0,60);
formData.bottom = new FormAttachment (100,-5);
formData.left = new FormAttachment (20,0);
formData.right = new FormAttachment (100,-3);
button1.setLayoutData(formData);
 
    一个Form附加值对象定义了一个小窗口部件指定的边的附加值。有很多方法能定义小窗口部件的边的粘贴:相对于父Composite的一个位置,相对于Composite的一个边,相对于另一个小窗口部件的相邻边,相对于另一个小窗口部件的相对边,或者和另一个小窗口部件居中。相对于父Composite的一个位置粘贴放置了小窗口部件的边,因此通常用相对于父Composite的百分比来表示。如果贴粘到父Composite的边缘,这个百分比就是0%或者100%。

      相对于另一个小窗口部件的相邻边粘贴保证了小窗口部件的指定边总是与另一个小窗口部件的最近边相邻。相对于另一个小窗口部件的相对边粘贴保证了小窗口部件的指定边总是与另一个小窗口部件的最远边对齐。最后,相对于另一个小窗口部件居中让小窗口部件在另一个小窗口部件中居中。这些方法都可以加或者不加偏移值来实现。
 
      如果没有为FormData对象定义附加值,默认会让小窗口部件粘贴到父Composite的左上角,象这样:
 
    
 
 button1.setLayoutData(new FormData());
 
     注意到如果多于一个小窗口部件被定义为没有任何附加值的时候,这些小窗口部件都会被放置到窗口中相同的默认位置上,并且会发生重叠。Form附加值在Form附加值对象这一节中将进行更仔细的叙述。
 
Width和Height
     FormData中的 widthheight域用来指定小窗口部件所需的宽度和高度。如果所请求的宽度和高度与附加值设置的约束发生冲突,那么这些请求的高度和宽度就不能遵从了。虽然通过设置附加值也以决定宽度和高度值,但有些情况下你不希望为小窗口部件的所有边都定义附加值值。在这种情况下,它们就可以很有用的象下面这样设定小窗口部件的宽度和高度值:
 
 FormData formData = new FormData(20,30);
 formData.top = new FormAttachment (0,60);
 formData.left = new FormAttachment (20,0);
 button1.setLayoutData(formData);
 
     如果你希望只设定宽度和高度值,你可以直接设置FromData对象的宽度和高度值:
 
FormData formData = new FormData ();
formData.width = 30;
formData.top = new FormAttachment (0,60);
formData.bottom = new FormAttachment (100,-5);
formData.left = new FormAttachment (20,0);
button1.setLayoutData(formData); 
 
     注意,如果一个按钮粘贴到父Composite的两边,当这父Composite进行缩放时,这个按钮会随着父Composite增大和缩小。

FormAttachment对象
     From附加值是一个用来定义小窗口部件的指定边的附加值的对象。不需要总是为小窗口部件的四个边都设置附加值。通常只指定一个或者多个小窗口部件的边就能完全指定它的放置。为了更适当的放置你的小窗口部件,你必须至少为FormData中 left或者 right之一,以及 top或者 bottom之一定义附加值。如果你只希望粘贴小窗口部件的左边而不是右边,那么这个小窗口部件将用它的边来进行定位,并且这个小窗口部件会采用它的正常大小(或者当设置了请求大小时,采用它的请求大小)。如果你没有贴粘左边或者右边,默认的定义会把你的小窗口部件粘贴到窗体的左边。对于顶部和底部也是采用了相同的逻辑。

相对一个位置进行粘贴
     存在很多种类型的附加值。第一种就是粘贴到相对于父Composite的一个位置。通过定义一个100以内的百分值就能实现,例如:
 
  
 
FormData formData = new FormData();
formData.top = new FormAttachment (50,0);
button1.setLayoutData(formData);
 
   这里设置Button的顶部到相对于父Composite(一个Shell)高度的50%的位置,偏移量为0。当这个Shell进行缩放的时候,Button的顶部会始终在50%的位置,像这样:
 
   
 
   如果我们选择设定一个偏移值,Button的上部就会被设置为父Composite的50%加上或者减去为偏移量设置的象素数。
 
    我们也能定义不使用百分比定义按钮的位置,例如:
 
FormData formData = new FormData();
formData.top = new FormAttachment (30,70,10);
button1.setLayoutData(formData);
 
     如果父Composite的高度被定义为70个单位,这里设置了Button的顶部在父Composite顶部以下30个单位,再加上10个象素的地方。
 
相对于父Composite进行粘贴
     第二种类型的附加值是相对于父Composite的边缘进行粘贴。这与相对于一个位置进行粘贴的实现方法差不多,但是这个位置值只能是0%或者100%。当处于垂直样式的时候,0这个位置被定义为父Composite的顶部,但处于水平样式的时候被定义为左边。右边和底部边缘被定义为100。因此,如果我们要粘贴一个小窗口部件到父Composite的右边,我们只需要简单的创建一个附加值,然后设置它的位置值为100:
 
    
 
FormData formData = new FormData();
formData.right = new FormAttachment (100,-5);
button1.setLayoutData(formData);
 
     这里把Button的右边粘贴到了父Composite(一个Shell)的右边缘,并有一个5个象素的偏移值。注意,附加值是往一个方向增加的。如果你要让一个小窗口部件向下或者所右偏移,偏移值必须是正的。如果要小窗口部件向上或者向左偏移,这个偏移值必须是负的。现在当这个Shell被缩放时,这个Button就总是偏离右边缘5个象素:
 
   
 
相对于另一个小窗口部件进行粘贴
     第三种类型的附加值是相对于父Composite中的另一个控件进行粘贴。小窗口部件的边可以被粘贴到另一个控件的相邻边(默认),粘贴到另一个控件的相对边,或者小窗口部件可以相对于另一个控件居中,这些都认使用或者不使用偏移值。
 
   相对于另一个控件进行粘贴最常用的方法是粘贴到它的相邻边。例如下面的代码:
 
  FormData formData = new FormData();
  formData.top = new FormAttachment (20,0);
  button1.setLayoutData(formData);
  
  FormData formData2 = new FormData();
  formData2.top = new FormAttachment (button1,10);
  button2.setLayoutData(formData2);
  
 把按钮2的顶部粘贴到了按钮1的底部,这是因为按钮1的底部和按钮2的顶部是相邻的。
 
    
 
     注意到当窗口被缩放时,按钮1会移动让它的顶部总是定位在Shell的20%的位置,而按钮2也是会移动让它的顶部总是在按钮1的相邻边(底部)以下10个象素的位置。
 
   
  
   虽然默认的情况下是相对于一个控件的相邻边进行粘贴,FromAttachment也能被创建用来相对一个控件的相对边进行粘贴。当要排列小窗口部件的时候这就非常的有用了。在这种情况下,你可以用TOP, BOTTOM, LEFT或者RIGHT排列(alignment)创建相对于另一个控件的附加值,例如:
  
formData2.top = new FormAttachment (button1,0,SWT.TOP); 
  
    在接下来的例子中,按钮1的顶部边被定位在Shell的20%的地方。按钮2使用了TOP排列,它的顶部边和按钮1的顶部边对齐。这意味着按钮2的顶部也被定位在这个Shell的20%的位置。注意到当指定了顶部附加值,只有小窗口部件的垂直放置会被定义。仍然需要为按钮2设置左边附加值以得Button不会发生重叠。
 
FormData formData = new FormData(50,50);
formData.top = new FormAttachment (20,0);
button1.setLayoutData(formData);
 
FormData formData2 = new FormData();
FormData2.left = new FormAttachment (button1,5);
formData2.top = new FormAttachment (button1,0,SWT.TOP);
button2.setLayoutData(formData2);
 
    结果如下:
 
   
  
     最后一个相对于另一个控件进行粘贴的方法是相对于另一个控件居中。当两个控件有不同的大小的时候,这就显得很有用了。在这种情况下,你用CENTER排列(aligmnet)创建相对于另一个控件的附加值,例如:
 
  formData.top = new FormAttachment (button1,0,SWT.CENTER);
 
      这会放置这个小窗口部件的顶部在一个允许这个小窗口部件相对于另一个控件居中的位置上,并且有一个值为0的偏移值。只设定顶部,或者底部,或者它们两者为居中附加值会导致相同的结果。这个小窗口部件的顶部边不会居中,但是整个小窗口部件会居中,所以这只需要指定一次。这里有一个例子:
 
     
 
FormData formData1 = new FormData (50,50);
button1.setLayoutData(formData1)
 
FormData formData2 = new FormData ();
formData2.left = new FormAttachment (button1,5);
formData2.top = new FormAttachment (button1,0,SWT.CENTER);
button2.setLayoutData (formData2);
 
    使用不同类型的附加值允许布局以不同的方法来定义。FormLayout解决了一些FillLayout,RowLayout或者GridLayout不能解决的问题,让它成为定义布局很有用的类。
 
     重要的不要定义 循环的附加值。例如,不要把按钮1的右边粘贴到按钮2的左边,然后又把按钮2的左边粘贴到按钮1的右边。这会过度约束布局,导致不可预知的行为。虽然运算会被中断,但结果是不可预知的。因此,确实保证你没有过度约束你的小窗口部件。仅提供所需要的附加值来适当的放置的小窗口部件。
 
一个FormLayout的例子
    到目前为止,所有使用FormLayout的例子都只是围绕着一两个Button,为了展示FormAttachment是如何工作 的。下面,我们会举一个有更多的Button的例子来展示使用附加值如何安排布局。我们会从画一张描绘我们要创建的附加值的基本草图开始。
 
 
 
FormData data1 = new FormData();
data1.left = new FormAttachment (0,5);
data1.right = new FormAttachment (25,0);
button1.setLayoutData(data1);
  
FormData data2 = new FormData();
data2.left = new FormAttachment (button1,5);
data2.right = new FormAttachment (100,-5);
button2.setLayoutData(data2);
  
FormData data3 = new FormData(60,60);
data3.top = new FormAttachment (button1,5);
data3.left = new FormAttachment (50,-30);
data3.right = new FormAttachment (50,30);
button3.setLayoutData(data3);
  
FormData data4 = new FormData();
data4.top = new FormAttachment (button3,5);
data4.bottom = new FormAttachment (100,-5);
data4.left = new FormAttachment (25,0);
button4.setLayoutData(data4);
  
FormData data5 = new FormData();
data5.bottom = new FormAttachment (100,-5);
data5.left = new FormAttachment (button4,5);
button5.setLayoutData(data5);
 
     在这个例子中,因为没有为按钮1和按钮2定义顶部附加值,它们粘贴到了布局的顶部上。钮3在左边和右边使用了百分比和偏移值而在布局中居中。按钮4和按钮5粘贴到了布局的底部,并有5个象素的偏移量。
 
   
 
    当我们进行缩放时,附加值就更显得明显了。按钮1的左边和右边都相对进行粘贴,所以当窗口被缩放时它也增大了。注意到它的右边总是在窗口的25%的位置。相同的缩放效果也被应用到按钮2上,因为它的两边也都相对进行了粘贴。左边相对按钮1进行了粘贴,所以它总是会在窗口的25%加上5个象素的位置。按钮3水平地保持在窗口的中间。按钮4的顶部和底部都相对进行粘贴,因此当窗口被缩放的时候它垂直地增大了,但是它只相对于左边进行了粘贴而对右边则没有,因此水平方向上它没有增大。按钮5不会增大或者缩小,但它的左边会保持在离按钮4有5个象素远,离窗口底部5个象素远的地方。
  
  
 

一个复杂的FormLayout的例子
    为了举例说明FormLayout如何能够用于更复杂的布局,我们用FormLayout来重做上面为GridLayout举例的“狗狗展示条目”的例子。这段代码创建一个一样的布局,但是用不同的观念来实现它。
 
     我们会从修改为网格布局所作的设计草图开始。我们会画出如何做,而不仅仅画出我们希望做成什么样。这人例子会用到所有的附加值类型。
 

  
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
 
public class ComplexFormLayoutExample {
   static Display display;
   static Shell shell;
   static Image dogImage;
   static Text dogNameText;
   static Combo dogBreedCombo;
   static Canvas dogPhoto;
   static List categories;
   static Text nameText;
   static Text phoneText;
 
   public static void main(String[] args){
       display = new Display();
       shell = new Shell(display);
       FormLayout layout= new FormLayout();
       layout.marginWidth = 5;
       layout.marginHeight = 5;
       shell.setLayout(layout);
       shell.setText("Dog Show Entry");
  
       Group ownerInfo = new Group(shell, SWT.NONE);
       ownerInfo.setText("Owner Info");
       FormLayout ownerLayout = new FormLayout();
       ownerLayout.marginWidth = 5;
       ownerLayout.marginHeight = 5;
       ownerInfo.setLayout(ownerLayout);
  
       Label dogName = new Label(shell, SWT.NONE);
       dogName.setText("Dog's Name:");
       dogNameText = new Text(shell, SWT.SINGLE | SWT.BORDER);
       Label dogBreed = new Label(shell, SWT.NONE);
       dogBreed.setText("Breed:");
       dogBreedCombo = new Combo(shell, SWT.NONE);
       dogBreedCombo.setItems(new String [] {"Collie", "Pitbull", "Poodle", "Scottie"});
       Label photo = new Label(shell, SWT.NONE);
       photo.setText("Photo:");
       dogPhoto = new Canvas(shell, SWT.BORDER);
       Button browse = new Button(shell, SWT.PUSH);
       browse.setText("Browse...");
       Button delete = new Button(shell, SWT.PUSH);
       delete.setText("Delete");
        Label cats = new Label (shell, SWT.NONE);
       cats.setText("Categories");
       categories = new List(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
       categories.setItems(new String [] {
           "Best of Breed", "Prettiest Female", "Handsomest Male",
           "Best Dressed", "Fluffiest Ears", "Most Colors",
           "Best Performer", "Loudest Bark", "Best Behaved",
           "Prettiest Eyes", "Most Hair", "Longest Tail",
           "Cutest Trick"});
      Button enter = new Button(shell, SWT.PUSH);
      enter.setText("Enter");
  
      FormData data = new FormData();
      data.top = new FormAttachment (dogNameText,0,SWT.CENTER);
      dogName.setLayoutData(data);
    
      data = new FormData();
      data.left = new FormAttachment (dogName,5);
      data.right = new FormAttachment (100,0);
      dogNameText.setLayoutData(data);
  
      data = new FormData();
      data.top = new FormAttachment (dogBreedCombo,0,SWT.CENTER);
      dogBreed.setLayoutData(data);
  
      data = new FormData();
      data.top = new FormAttachment (dogNameText,5);
      data.left = new FormAttachment (dogNameText,0,SWT.LEFT);
      data.right = new FormAttachment (categories,-5);
      dogBreedCombo.setLayoutData(data);
  
      data = new FormData(80,80);
      data.top = new FormAttachment (dogBreedCombo,5);
      data.left = new FormAttachment (dogNameText,0,SWT.LEFT);
      data.right = new FormAttachment (categories,-5);
      data.bottom = new FormAttachment (ownerInfo,-5);
      dogPhoto.setLayoutData(data);
      dogPhoto.addPaintListener(new PaintListener() {
           public void paintControl(final PaintEvent event) {
                if (dogImage != null) {
                    event.gc.drawImage(dogImage, 0, 0);
                }
          }
      });
        
      data = new FormData();
      data.top = new FormAttachment (dogPhoto,0,SWT.TOP);
      photo.setLayoutData(data);
  
      data = new FormData();
      data.top = new FormAttachment (photo,5);
      data.right = new FormAttachment (dogPhoto, -5);
      browse.setLayoutData(data);
      browse.addSelectionListener(new SelectionAdapter() {
          public void widgetSelected(SelectionEvent event) {
              String fileName = new FileDialog(shell).open();
              if (fileName != null) {
                   dogImage = new Image(display, fileName);
              }
          }
      });
  
      data = new FormData();
      data.left = new FormAttachment (browse,0,SWT.LEFT);
      data.top = new FormAttachment (browse,5);
      data.right = new FormAttachment (dogPhoto, -5);
      delete.setLayoutData(data);
      delete.addSelectionListener(new SelectionAdapter() {
          public void widgetSelected(SelectionEvent event) {
              if (dogImage != null) {
                   dogImage.dispose();
                   dogImage = null;
                   dogPhoto.redraw();
              }
         }
     });
  
     data = new FormData(90,140);
     data.top = new FormAttachment (dogPhoto,0,SWT.TOP);
     data.right = new FormAttachment (100,0);
     data.bottom = new FormAttachment (enter,-5);
     categories.setLayoutData(data);
      
    data = new FormData();
    data.bottom = new FormAttachment (categories,-5);
    data.left = new FormAttachment (categories,0,SWT.CENTER);
    cats.setLayoutData(data);
 
    data = new FormData();
    data.right = new FormAttachment (100,0);
    data.bottom = new FormAttachment (100,0);
    enter.setLayoutData(data);
    enter.addSelectionListener(new SelectionAdapter() {
          public void widgetSelected(SelectionEvent event) {
              System.out.println("\nDog Name: " + dogNameText.getText());
              System.out.println("Dog Breed: " + dogBreedCombo.getText());
              System.out.println("Owner Name: " + nameText.getText());
              System.out.println("Owner Phone: " + phoneText.getText());
              System.out.println("Categories:");
              String cats[] = categories.getSelection();
              for (int i = 0; i < cats.length; i++) {
                   System.out.println("\t" + cats[i]);
              }
         }
    });
  
    data = new FormData();
    data.bottom = new FormAttachment (enter,-5);
    data.left = new FormAttachment (0,0);
    data.right = new FormAttachment (categories,-5);
    ownerInfo.setLayoutData(data);
  
     Label name = new Label(ownerInfo, SWT.NULL);
     name.setText("Name:");
     Label phone = new Label(ownerInfo, SWT.PUSH);
     phone.setText("Phone:");
     nameText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
     phoneText = new Text(ownerInfo, SWT.SINGLE | SWT.BORDER);
       
     data = new FormData();
     data.top = new FormAttachment (nameText,0,SWT.CENTER);
     name.setLayoutData(data);
        
     data = new FormData();
     data.top = new FormAttachment (phoneText,0,SWT.CENTER);
     phone.setLayoutData(data);
        
     data = new FormData();
     data.left = new FormAttachment (phone,5);
     data.right = new FormAttachment (100,0);
     nameText.setLayoutData(data);
       
     data = new FormData();
     data.left = new FormAttachment (nameText,0,SWT.LEFT);
     data.right = new FormAttachment (100,0);
     data.top = new FormAttachment (55,0);
     phoneText.setLayoutData(data);
        
    shell.pack();
    shell.open();
  
    while (!shell.isDisposed()) {
         if (!display.readAndDispatch())
              display.sleep();
    }
    display.dispose();
  }
}
 
   这是在“狗狗展示”中Mary Smith输入Fifi后布局看起来的样子:
 
  
  
    当这个窗口被缩放时,象GridLayout的例子中一样,相同的控件会被缩放。
 
   
 
编写你自己的布局类
   有时候,你可能需要编写你自己的布局类。有可能你的布局的需求很复杂。或者你有些地方你可能需要相同的外观,而你希望改进代码的重用率。或者你希望扩大领域知识来创建一个很有效率的布局类。不管原因是什么,在编写一个新的类之前,有几件事情是你要考虑的:
  • 这个布局能够用GridLayout或者FormLayout,以及可能是一些嵌套布局来实现吗?
  • 希望达到的效果可以更容易的用一个缩放监听器来实现吗?
  • 你定义了一个通用的布局算法还是只是布置了小窗口部件?
 
   除非你正在编写一种很通用的可以被很多Composite小窗口部件使用的布局类,通常比较好和容易的做法是简单的在缩放监听器中计算大小并且布置子组件。很多的SWT定制小窗口部件就是用这个方法编写的,虽然一个新的小窗口部件可以被实现为一个Composite/Layout对,把它实现成一个在缩放监听器中进行布局,并且在computeSize中计算的合适大小的Composite却更为清晰。
 
   首先,我们会先来看看布局是怎么样工作的,然后我们再创建一个新的布局类。另一个编写你自己的而已的例子可以在“用SWT创建你自己的小窗口部件”(Creating Your Own Widgets Using SWT)中的“复合小窗口部件示例”(Compound Widget Example)一节中找到,它显示了如何用一个缩放监听器或者一个新布局类达到相同的外观。

布局是如何工作的
    Layout是所有布局的超类。它只有两个方法 computeSizelayout。这个类的定义如下:
 
public abstract class Layout {
   protected abstract Point computeSize(Composite composite, int widthHint, int heightHint, boolean flushCache);
   protected abstract void layout(Composite composite, boolean flushCache);
}
 
    一旦这个Composite的子组件被确定大小并根据布局类中的编写的布局算法来放置的时候, computeSize方法计算包括所有这些子组件的矩形区域的宽度和高度。hint参数允许宽度和/或者高度被约束。例如,如果一个维被约束,一个布局可能选择在另一个维上增长。一个SWT.DEFAULT的hint意思是使用合适的(preffed)大小。
 
   layout方法用来布置Coposite的子组件和确定Composite的子组件大小。布局可以选择缓存(cache)布局相关的信息,例如每一个子组件的合适上下文。flushCache参数告诉布局冲洗(flush)已缓存的数据。
 
    既然一个布局控制着小窗口部件在一个Composite中的大小和放置,Composite中也有一些方法可以和布局一起使用。
 
   最先的两个方法允许设置和得到Composite中的Layout对象。
 
 public void setLayout(Layout layout);
 public Layout getLayout();
 
    一个应用程序可以强制一个布局重新计算子组件的大小,并通过调用父Composite的 layout()方法重新定位这些子组件。
 
 public void layout(boolean changed);
 public void layout();
    // calls layout(true);
 
   你需要在你改变了任何子组件,可能导致子组件的大小和位置发生改变的时候做这件事情,比如改变了子组件的字体,改变了子组件的文本或者图片,增加了新的子组件,或者为子组件增加了子组件。(如果子组件可以适应这个变化,布局可能不会成功—例如,改变一个可滚动的多行的Text的字体或者文本)。既然这些改变都是通过编程来实现的,它们不会触发事件。从而,父构件不会知道这些变化,必须通过 layout方法来告许它。这个策略导致了闪动,这是因为应用程序可能作了好几个改变然后才告诉父构件重新布局,并且子组件只被重画一次而不是每次改变都重画一次。如果没有调用 layout()并改变是在shell打开后发生的,那么子组件可能不会被正确的放置直到shell由于某种原因进行了缩放。注意 shell.open()导致一个布局过程的发生。
 
   Composite的 computeSize方法计算Composite的合适大小,这是它由这个布局决定的客户区(client)的大小加上它的修剪(trim)。
 
 public Point computeSize(int widthHint, int heightHint, boolean changed);
 public Point computeSize(int widthHint, int heightHint);
    // calls computeSize(widthHint, heightHint, true);
 
  Composite的 clientArea是包含所有的子组件的矩形区。一个布局在客户区中放置子组件。
 
 public Rectangle getClientArea ();
 
  Composite的 trim(修剪)是客户区外的区域。对于一些Composite,修剪的大小是零。修剪可以通过传递客户区的尺寸给 computeTrim计算出来。
 
 public Rectangle computeTrim (int x, int y, int width, int height);
 
   调用Composite的 pack方法将把它缩放到它的合适大小。
 
 public void pack(boolean changed);
  // calls setSize(computeSize(SWT.DEFAULT, SWT.DEFAULT, changed));
 public void pack();
  // calls pack(true);
 
    layoutcomputeSizepack方法的布尔型参数是 changed标志。如果为 true,它表明Composite的内容已经被通过某些方法改变而影响了它的合适大小,所以布局所持有的任何缓存都需要被冲洗。当一个Composite被缩放时,它调用layout(false)来让自己的布局安排它的子组件;因此小窗口部件的内容缓存没有被冲洗掉。这让布局只在必要的进要的时候进行代价高昂的计算。
 
    缓存可以改进性能,但它也可能是有欺骗性的。你可以选择完全不缓存 — 事实上,最好不要试力用缓存,直到你的代码已经稳定。当你考虑缓存什么时,确保你没有缓存任何小窗口部件状态,例如一个标签的文本,或者一个列表的项的数目。
 
定制存局的例子
   如果在你的应用程序中你有一些垂直导向的Composite小窗口部件,你可能会选择编写ColumnLayout。我们将会展示一个简单的布局类版本,它把Composite子组件放置到一个单列中。这个类有固定的修剪和间距。子组件被赋予相同的宽度,但它们能有自己自然的高度。(注意,在 *2.0*中,RowLayout已经被扩展为有ColumnLayout的功能,如果它的类型被设为SWT.VERTICAL的话。正因为如此,这个例子只是作为一个例子。事实上,如果你现在需要把小窗口部件放在一列中,你可以用RowLayout来实现。)
 
    这段ColumnLayout类的例子就在下面。注意,我们缓存了小窗口部件子组件的宽度,子组件的高度和(加上了间距),而且这些值被用来计算大小和放置子组件。如果 flushCachetrue,它们会被重新计算。
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
 
public class ColumnLayout extends Layout {
     // fixed margin and spacing
     public static final int MARGIN = 4;
     public static final int SPACING = 2;
 
     // cache
     Point [] sizes;
     int maxWidth, totalHeight;
 
     protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
         Control children[] = composite.getChildren();
         if (flushCache || sizes == null || sizes.length != children.length) {
               initialize(children);
    }
    int width = wHint, height = hHint;
    if (wHint == SWT.DEFAULT) width = maxWidth;
    if (hHint == SWT.DEFAULT) height = totalHeight;
    return new Point(width + 2 * MARGIN, height + 2 * MARGIN);
  }
 
   protected void layout(Composite composite, boolean flushCache) {
       Control children[] = composite.getChildren();
       if (flushCache || sizes == null || sizes.length != children.length) {
            initialize(children);
   }
   Rectangle rect = composite.getClientArea();
   int x = MARGIN, y = MARGIN;
   int width = Math.max(rect.width - 2 * MARGIN, maxWidth);
   for (int i = 0; i < children.length; i++) {
        int height = sizes[i].y;
        children[i].setBounds(x, y, width, height);
        y += height + SPACING;
   }
}
 
void initialize(Control children[]) {
      maxWidth = 0;
      totalHeight = 0;
     sizes = new Point [children.length];
     for (int i = 0; i < children.length; i++) {
           sizes[i] = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
            maxWidth = Math.max(maxWidth, sizes[i].x);
            totalHeight += sizes[i].y;
     }
      totalHeight += (children.length - 1) * SPACING;
   }
}
 
     这里有一些简单的测试代码来测试ColumnLayout。 Growshrink按钮展示了在改变了其中一个子组件的宽度后调用Shell的 layout()方法强制一个重新布局的过程。调用 layout()和调用 layout(true)是一样的,它告诉ColumnLayout在设定子组件的边界的时候冲掉它的缓存。Shell在放置好子组件后也调用 pack()方法。这强制让Shell采用新的大小。
 
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.events.*;
 
public class ColumnLayoutTest {
     static Shell shell;
     static Button button3;
 
     public static void main(String[] args) {
           Display display = new Display();
           shell = new Shell(display);
           shell.setLayout(new ColumnLayout());
           new Button(shell, SWT.PUSH).setText("B1");
           new Button(shell, SWT.PUSH).setText("Very Wide Button 2");
           (button3 = new Button(shell, SWT.PUSH)).setText("Button 3");
           Button grow = new Button(shell, SWT.PUSH);
           grow.setText("Grow Button 3");
           grow.addSelectionListener(new SelectionAdapter() {
                 public void widgetSelected(SelectionEvent e) {
                      button3.setText("Extreemely Wide Button 3");
                      shell.layout();
                      shell.pack();
                }
           });
           Button shrink = new Button(shell, SWT.PUSH);
           shrink.setText("Shrink Button 3");
           shrink.addSelectionListener(new SelectionAdapter() {
                  public void widgetSelected(SelectionEvent e) {
                       button3.setText("Button 3");
                       shell.layout();
                       shell.pack();
                  }
           });
          shell.pack();
          shell.open();
          while (!shell.isDisposed()) {
                if (!display.readAndDispatch()) display.sleep();
           }
      }
}
 
     如果我们运行这段测试代码,窗口会像左图看起来那样。按下Grow Button 3按钮会导致窗口如右图所示。用鼠标缩放窗口的大小也能使按钮变宽(或者变窄)但是它们不会变高。
 
      
 
重载Composite

     如果你正在编写自己的小窗口部件,就像“用SWT创建你自己的小窗口部件”(Creating Your Own Widgets Using SWT)中描述的那样,并且你子类化了Composite,那么你的实现要几点要考虑:
    • 如果在你的新Composite中提供了修剪(trimming),确保你重载了 computeTrimgetClientArea
    • 绝对不要重载 layout(),但是你可以重载 layout(boolean)
 
    有的时候你想让你的新Composite有特殊的外观,并且你不想就用程序有能力指定一个布局。你的新Composite既可以在一个缩放处理(handle)里面,也可以用一个私有的定制布局来实现。在这种情况下,你可能需要做下在这些工作:
    • 重载 setLayout让它不做任何事。
    • 重载 layout(boolean)让它调用你的布局代码。
    • 重载 computeSize让它正确的计算出你的Composite的大小。
 
概要
     SWT提供了很多不同的方法来放置小窗口部件。最简单的方法并且是你会最常使用到的方法,就是用这些标准布局类的其中一个:FillLayout,RowLayout,GridLayout或者FormLayout。
 
     在一些情况下,你可能需要编写你自己的布局类来提供一些特殊的外观或者重用很相似的布局代码,但是通常一个在父小窗口部件上的缩放监听器就已经显得足够了。
 
    为了得到进一步的帮助以了解SWT的布局类,你可以查看 org.eclipse.swt.examples.layoutexample中的例子。

你可能感兴趣的:(eclipse,编程,算法,中间件,cache)