JEditorPane的包级关系
java.lang.Object
java.awt.Component
java.awt.Container
javax.swing.JComponent
javax.swing.text.JTextComponent
javax.swing.JEditorPane
javax.swing.JTextPane
复制代码
JEditorPane的介绍
它是用于编辑各种内容的文本组件。该组件使用EditorKit的EditorKit来完成其行为。它有效地转化为适当的文本编辑器,用于提供给他们的内容。 编辑器在任何给定时间绑定的内容类型由当前EditorKit EditorKit确定。 如果内容设置为新的URL,则其类型用于确定应用于加载内容的EditorKit 。
model
这里的model就是document
private Document initializeModel(EditorKit kit, URL page) {
Document doc = kit.createDefaultDocument();
if (pageProperties != null) {
// transfer properties discovered in stream to the
// document property collection.
for (Enumeration<String> e = pageProperties.keys(); e.hasMoreElements() ;) {
String key = e.nextElement();
doc.putProperty(key, pageProperties.get(key));
}
pageProperties.clear();
}
if (doc.getProperty(Document.StreamDescriptionProperty) == null) {
doc.putProperty(Document.StreamDescriptionProperty, page);
}
return doc;
}
复制代码
创建了一个初始化model,然后在对page中的属性添加进model,现在就是一个有内容的Document了。
view
public View create(Element elem) {
Document doc = elem.getDocument();
Object i18nFlag
= doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
// build a view that support bidi
return createI18N(elem);
} else {
return new WrappedPlainView(elem);
}
}
View createI18N(Element elem) {
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new PlainParagraph(elem);
} else if (kind.equals(AbstractDocument.ParagraphElementName)){
return new BoxView(elem, View.Y_AXIS);
}
}
return null;
}
复制代码
根据前面创建的document的结构形成相应视图框架
model to view
这里是默认继承父类JTextComponent的modelToView()来实现
public Rectangle modelToView(int pos) throws BadLocationException {
return getUI().modelToView(this, pos);
}
复制代码
将model中的给定位置转换为view坐标系中的位置。pos必须是正数。
首先先看几个继承关系
java.lang.Object
javax.swing.plaf.ComponentUI
javax.swing.plaf.TextUI
javax.swing.plaf.basic.BasicTextUI
复制代码
javax.swing.JComponent
javax.swing.text.JTextComponent
复制代码
JTextComponent中的两个方法
public void setUI(TextUI ui) {
super.setUI(ui);
}
复制代码
public TextUI getUI() { return (TextUI)ui; }
复制代码
这里的TextUI是ComponentUI类型,也就是这里的modelToView依然是一个继承方法,具体实现在BasicTextUi中,代码如下
public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {
Document doc = editor.getDocument();
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readLock();
}
try {
Rectangle alloc = getVisibleEditorRect();
if (alloc != null) {
rootView.setSize(alloc.width, alloc.height);
Shape s = rootView.modelToView(pos, alloc, bias);
if (s != null) {
return s.getBounds();
}
}
} finally {
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readUnlock();
}
}
return null;
}
复制代码
这里的getVisibleEditorRect()其实是初始化一个rectangle,具体代码如下
protected Rectangle getVisibleEditorRect() {
Rectangle alloc = editor.getBounds();
if ((alloc.width > 0) && (alloc.height > 0)) {
alloc.x = alloc.y = 0;
Insets insets = editor.getInsets();
alloc.x += insets.left;
alloc.y += insets.top;
alloc.width -= insets.left + insets.right;
alloc.height -= insets.top + insets.bottom;
return alloc;
}
return null;
}
复制代码
BasicTextUI中的modelToView()
public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
if (view != null) {
return view.modelToView(pos, a, b);
}
return null;
}
复制代码
这里的rootView是继承自View,View中的modelToView()
public abstract Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException;
复制代码
controller
在JEditorPane中
public synchronized void addHyperlinkListener(HyperlinkListener listener) {
listenerList.add(HyperlinkListener.class, listener);
}
复制代码
这里的listenerList是JEditorPane中继承JComponent得到的
JComponent中
protected EventListenerList listenerList = new EventListenerList();
复制代码
至于EventListenerList
单个实例可用于使用列表来容纳所有实例的所有侦听器(所有类型的所有类型)。 使用EventListenerList的类的责任是提供类型安全的API(最好符合JavaBean规范)以及将事件通知方法分配给列表中适当的事件侦听器的方法。 这个类提供的主要好处是它在没有听众的情况下相对便宜,并且在一个地方提供了事件监听器列表的序列化,以及一定程度的MT安全性(正确使用时)。
public synchronized <T extends EventListener> void add(Class<T> t, T l) {
if (l==null) {
// In an ideal world, we would do an assertion here
// to help developers know they are probably doing
// something wrong
return;
}
if (!t.isInstance(l)) {
throw new IllegalArgumentException("Listener " + l +
" is not of type " + t);
}
if (listenerList == NULL_ARRAY) {
// if this is the first listener added,
// initialize the lists
listenerList = new Object[] { t, l };
} else {
// Otherwise copy the array and add the new listener
int i = listenerList.length;
Object[] tmp = new Object[i+2];
System.arraycopy(listenerList, 0, tmp, 0, i);
tmp[i] = t;
tmp[i+1] = l;
listenerList = tmp;
}
}
复制代码
但是仅仅有listener是不够的,还需要有“fire”方法,下面是一个例子
EventListenerList listenerList = new EventListenerList();
FooEvent fooEvent = null;
public void addFooListener(FooListener l) {
listenerList.add(FooListener.class, l);
}
public void removeFooListener(FooListener l) {
listenerList.remove(FooListener.class, l);
}
// Notify all listeners that have registered interest for
// notification on this event type. The event instance
// is lazily created using the parameters passed into
// the fire method.
protected void fireFooXXX() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==FooListener.class) {
// Lazily create the event:
if (fooEvent == null)
fooEvent = new FooEvent(this);
((FooListener)listeners[i+1]).fooXXX(fooEvent);
}
}
}
复制代码
fire这个方法一般在实现在EventListenerList没有,但是在Component中有体现,这点可能是为了减少代码的耦合度。
到此简单的MVC模式介绍完了。
下一个问题是如何将Shape通过paint方法“画”到计算机界面。