TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
开门见山,程序运行结果如下图。
有4个组件,JButton、JScrollPane(内嵌JTree)、自定义组件ImageButton、一个JTextField。
布局原则是JButton左边界距离容器左边界5像素、右边界距离容器左边界130像素、所以长度为130-5=125固定不变,JButton上边界距离容器上边界10像素,下边界距离容器下边界35像素,所以高度为35-10=25固定不变;JScrollPane位于容器的中央,其中左右两边距离容器两边均是20像素,所以JScrollPane的宽度随着容器宽度的变化而变化,JScrollPane上下两边距离容器中心高度均是50像素,所以整体高度是100像素;ImageButton的上边界距离容器的底部50像素,左边界距离容器右边界100像素,由于ImageButton会根据背景尺寸产生PreferredSize,所以右边界、下边界不用设置。剩下的JTextField不是通过配置产生,具体见后面介绍。下面来看看xml配置,如下。
<components> ⑴<layout class="org.swingframework.layout.FormLayout" /> ⑵<component id="1101" class="javax.swing.JButton"> ⑶<form-data> <left percentage="0.0" offset="5" /> <right percentage="0.0" offset="130" /> <top percentage="0.0" offset="10"/> <bottom percentage="0.0" offset="35"/> </form-data> </component> <component id="1102" class="javax.swing.JScrollPane"> <form-data> <left percentage="0.0" offset="20" /> <top percentage="0.5" offset="-50" /> <right percentage="1.0" offset="-20" /> <bottom percentage="0.5" offset="50"/> </form-data> </component> <component id="1103" class="org.swingframework.component.ImageButton"> <form-data> <left percentage="1.0" offset="-100" /> <top percentage="1.0" offset="-50" /> </form-data> </component> </components> (1)指定容器的布局类,目前仅支持FormLayout、CenterLayout两种。
(2)定义一个组件,指定唯一的id和完整类名。
(3)为组件指定布局约束。
由于xml文档结构是固定的,因此xml解析采用XPath。XPath已在JDK1.5中被集成,而且相比DOM更加简单,关于XPath的更多内容参考其他资料。
定义布局注入LayoutInjection类,目的就是解释一个给定的xml文件,然后去给一个容器生成内部组件并布局这些组件。 public class LayoutInjection {
private Container injectTarget;
private InputStream layoutSource;
private LayoutManager layoutManager;
private Map<String, ComponentEntry> entryMap; public LayoutInjection(Container injectTarget, InputStream layoutSource) {
this.injectTarget = injectTarget;
this.layoutSource = layoutSource;
}
...... private class ComponentEntry {
private Component component;
private FormData formData; ComponentEntry(Component component, FormData formData) {
this.component = component;
this.formData = formData;
} public Component getComponent() {
return component;
} public FormData getFormData() {
return formData;
}
}
}
单独定义一个ComponentEntry内部类是为了将组件-布局约束关联。定义一个injectLayout方法来完成布局,在injectLayout方法内部,首先读取外部配置文件并将组件与布局约束保存到entryMap,然后为容器设置根据配置得到的布局管理器,下一步插入自定义属性,包括非配置产生的组件,与组件修饰、组件事件监听器等。最后一步是遍历entryMap根据每个组件与其布局约束完成组件创建与布局。
public final void injectLayout() throws Exception {
loadComponents();
injectTarget.setLayout(layoutManager);
customProperties();
synchronized (injectTarget.getTreeLock()) {
Set<String> idSet = entryMap.keySet();
for (String id : idSet) {
Component component = getComponentById(id);
FormData formData = getFormDataById(id);
injectTarget.add((Component) component, formData);
}
injectTarget.doLayout();
}
}
customProperties方法是空实现,需要自己去实现逻辑。 protected void customProperties() { }
injectLayout其他部分的实现参见完整源码。
最后给一个Test测试这个类。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.io.FileInputStream; import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.WindowConstants; import org.swingframework.component.ImageButton;
import org.swingframework.layout.FormAttachment;
import org.swingframework.layout.FormData;
import org.swingframework.layout.LayoutInjection; public class Test { public static void main(String[] args) throws Exception {
JFrame frm = new JFrame();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(350, 250));
frm.getContentPane().add(panel, BorderLayout.CENTER);
FileInputStream input = new FileInputStream("C:/layout.xml");
LayoutInjection injection = new LayoutInjection(panel, input) {
@Override
protected void customProperties() {
JButton button = (JButton) getComponentById("1101");
button.setText("this is a JButton");
JScrollPane jsp = (JScrollPane) getComponentById("1102");
jsp.setBorder(BorderFactory.createLineBorder(new Color(128,
128, 128), 2));
jsp.getViewport().add(new JTree());
ImageButton imageButton = (ImageButton) getComponentById("1103");
imageButton.setBackgroundImage(new ImageIcon("button_up.png"));
imageButton.setRolloverBackgroundImage(new ImageIcon(
"button_over.png"));
imageButton.setPressedBackgroundImage(new ImageIcon(
"button_down.png"));
// add extend component
JTextField jtf = new JTextField();
jtf.setBorder(BorderFactory.createLineBorder(new Color(128,
128, 128), 2));
FormData jtfFormData = new FormData();
jtfFormData.top = new FormAttachment(0.8f, 0);
jtfFormData.left = new FormAttachment(0.2f, 0);
jtfFormData.right = new FormAttachment(0.2f, 100);
jtfFormData.bottom = new FormAttachment(0.8f, 25);
panel.add(jtf, jtfFormData);
}
};
injection.injectLayout();
input.close();
frm.pack();
frm.setVisible(true);
}
}
加红的需要解释以下,xml数据源是java.io.InputStream的实例,当然不局限与文件读取;一般地,都要实现customProperties完成自定义逻辑,本例是修饰了组件外观,另外以非配置方式加载了JTextField组件,以非配置方式添加组件是很必要的,因为实际应用的时候,你添加的更多的是已有的组件实例而不是xml文件中写死的。
源码下载:http://file.javaxxz.com/2014/11/10/235701734.zip |
|